1、Reentrant其实是ReEntrant,可重入的意思。先来个标准例子:
package com.amuro.studythread.chapter4_lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample
{
public static void main(String[] args)
{
A a = new A();
for(int i = 0; i < 5; i ++)
{
Thread t = new Thread(new Runnable()
{
@Override
public void run()
{
a.a();
}
});
t.setName("t" + (i + 1));
t.start();
}
}
static class A
{
ReentrantLock lock = new ReentrantLock();
void a()
{
lock.lock();
String tName = Thread.currentThread().getName();
for(int i = 0; i < 10; i++)
{
System.out.println(tName + " : " + (i + 1));
}
b();
lock.unlock();
}
void b()
{
lock.lock();
System.out.println("tName b");
lock.unlock();
}
}
}
程序输出:
t1 : 1
t1 : 2
t1 : 3
t1 : 4
t1 : 5
t1 : 6
t1 : 7
t1 : 8
t1 : 9
t1 : 10
t1 b
t2 : 1
t2 : 2
t2 : 3
t2 : 4
t2 : 5
t2 : 6
t2 : 7
t2 : 8
t2 : 9
t2 : 10
t2 b
t3 : 1
t3 : 2
t3 : 3
t3 : 4
t3 : 5
t3 : 6
t3 : 7
t3 : 8
t3 : 9
t3 : 10
t3 b
t4 : 1
t4 : 2
t4 : 3
t4 : 4
t4 : 5
t4 : 6
t4 : 7
t4 : 8
t4 : 9
t4 : 10
t4 b
t5 : 1
t5 : 2
t5 : 3
t5 : 4
t5 : 5
t5 : 6
t5 : 7
t5 : 8
t5 : 9
t5 : 10
t5 b
可以见这种情况下,lock的作用和synchronized的几乎一样,是一把可重入的锁。
2、condition/signal == wait/notify
package com.amuro.studythread.chapter4_lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionExample
{
public static void main(String[] args) throws Exception
{
final Service service = new Service();
Thread t = new Thread(new Runnable()
{
@Override
public void run()
{
service.await();
}
});
t.start();
Thread.sleep(3000);
service.signal();
}
static class Service
{
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
void await()
{
try
{
lock.lock();
System.out.println("await begin");
condition.await();
System.out.println("await end");
}
catch (Exception e)
{
}
finally
{
lock.unlock();
}
}
void signal()
{
lock.lock();
System.out.println("signal");
condition.signal();
lock.unlock();
}
}
}
程序输出:
await begin
signal
await end
package com.amuro.studythread.chapter4_lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionExample
{
public static void main(String[] args) throws Exception
{
final Service service = new Service();
Thread t1 = new Thread(new Runnable()
{
@Override
public void run()
{
service.await1();
}
});
t1.start();
Thread t2 = new Thread(new Runnable()
{
@Override
public void run()
{
service.await2();
}
});
t2.start();
Thread.sleep(3000);
service.signal();
}
static class Service
{
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
void await1()
{
try
{
lock.lock();
System.out.println("await1 begin");
condition.await();
System.out.println("await1 end");
}
catch (Exception e)
{
}
finally
{
lock.unlock();
}
}
void await2()
{
try
{
lock.lock();
System.out.println("await2 begin");
condition.await();
System.out.println("await2 end");
}
catch (Exception e)
{
}
finally
{
lock.unlock();
}
}
void signal()
{
lock.lock();
System.out.println("signal");
condition.signalAll();
lock.unlock();
}
}
}
程序输出:
await1 begin
await2 begin
signal
await1 end
await2 end
3、实现生产消费者模式
package com.amuro.studythread.chapter4_lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class SinglePC
{
public static void main(String[] args)
{
P p = new P();
C c = new C();
new Thread(new Runnable()
{
@Override
public void run()
{
while(true)
{
p.set();
}
}
}).start();
new Thread(new Runnable()
{
@Override
public void run()
{
while(true)
{
c.get();
}
}
}).start();
}
static String value = "";
static ReentrantLock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
static class P
{
void set()
{
try
{
lock.lock();
while(!"".equals(value))
{
condition.await();
}
System.out.println("empty");
value = "Double X";
Thread.sleep(2000);
condition.signal();
}
catch (Exception e)
{
}
finally
{
lock.unlock();
}
}
}
static class C
{
void get()
{
try
{
lock.lock();
while("".equals(value))
{
condition.await();
}
System.out.println(value);
value = "";
Thread.sleep(2000);
condition.signal();
}
catch (Exception e)
{
}
finally
{
lock.unlock();
}
}
}
}
4、公平锁与非公平锁
公平锁:线程获取锁的顺序遵循先到先得;
非公平锁:非先到先得,可能导致某些线程一直拿不到锁。
创建方法:
ReentrantLock lock = new ReentrantLock(true);//fair
ReentrantLock lock = new ReentrantLock(false);//unfair
5、lock的常用方法
1)int getHoldCount
当前线程保持此锁定的个数,也就是调用lock方法的次数。
2)int getQueueLength()
等待获取此lock的线程数,比如5个线程争夺一个lock,当1个线程获得lock后执行了sleep方法,此时调用getQueueLength方法返回的结果为4。
3)int getWaitQueueLength()
5个线程,每个线程都执行了同一个condition的await方法,则调用此方法返回5。
4)boolean hasQueueThread(thread)和bolean hasQueueThreads()
查询指定线程是否在等待获取锁;
查询是否有线程在等待获取锁。
5)boolean hasWaiters(condition)
查询是否有线程在用当前condition进行等待。
6)boolean isFair()
查询是否公平锁。
7)boolean isHeldByCurrentThread()和boolean isLocked()
查询当前线程是否获得了锁。
查询是否有线程了获得了锁。
8)void lockInterruptibly()
不太好解释,先看代码:
package com.amuro.studythread.chapter4_lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockInterruptibly
{
public static void main(String[] args) throws Exception
{
final Service s = new Service();
Thread tA = new Thread(new Runnable()
{
@Override
public void run()
{
s.invoke();
}
});
tA.start();
Thread.sleep(500);
Thread tB = new Thread(new Runnable()
{
@Override
public void run()
{
s.invoke();
}
});
tB.start();
tB.interrupt();
System.out.println("main end");
}
static class Service
{
private ReentrantLock lock;
public Service()
{
lock = new ReentrantLock();
}
void invoke()
{
try
{
lock.lock();
// lock.lockInterruptibly();
System.out.println(Thread.currentThread().getName() + " begin");
for(int i = 0; i < Integer.MAX_VALUE / 10; i++)
{
String str = new String();
Math.random();
}
System.out.println(Thread.currentThread().getName() + " end");
}
catch (Exception e)
{
System.out.println(Thread.currentThread().getName() + " goes into exception " + e.getMessage());
}
finally
{
if(lock.isHeldByCurrentThread())
{
lock.unlock();
}
}
}
}
}
当使用lock.lock()时,打印结果为:
Thread-0 begin
main end
Thread-0 end
Thread-1 begin
Thread-1 end
当使用lock.lockInterruptibly时,打印结果为:
Thread-0 begin
main end
Thread-1 goes into exception null
Thread-0 end
由此可见,lockInterruptibly方法的意义是:如果线程未被中断(调用interrupt方法)时,可以正常获取锁,如果已经被中断则抛出异常。
9)boolean tryLock()
这个方法最大的好处就是发现拿不到锁的时候,可以进行其他处理。方法会直接返回false。
10)void awaitUninterruptibly()
正常情况下,在condition.await的时候调用线程的interrupt方法会导致抛出异常,但是如果使用awaitUninterruptibly方法,就不会抛出异常,程序会继续等待signal。
11)void awaitUtil(Date)
package com.amuro.studythread;
import java.util.Calendar;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class Test
{
public static void main(String[] args) throws Exception
{
final Test t = new Test();
Thread th = new Thread(new Runnable()
{
@Override
public void run()
{
t.invoke();
}
});
th.start();
Thread.sleep(1000);
t.signal();
}
ReentrantLock lock;
Condition condition;
public Test()
{
lock = new ReentrantLock();
condition = lock.newCondition();
}
void invoke()
{
try
{
Calendar c = Calendar.getInstance();
c.add(Calendar.SECOND, 3);
lock.lock();
System.out.println("begin");
condition.awaitUntil(c.getTime());
System.out.println("end");
}
catch(Exception e)
{
System.out.println("exception: " + e.getMessage());
}
finally
{
lock.unlock();
}
}
void signal()
{
try
{
lock.lock();
condition.signal();
}
finally
{
lock.unlock();
}
}
}
线程会一直等待到传入的时间,但是可以被signal提前打断。
6、condition实现线程的顺序执行
package com.amuro.studythread.chapter4_lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class SequenceThreadInvoke
{
static int go = 1;
public static void main(String[] args)
{
final ReentrantLock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
int length = 5;
for(int i = 0; i < length; i++)
{
MyThread t = new MyThread(i + 1)
{
@Override
public void run()
{
while(true)
{
try
{
lock.lock();
while(go != getI())
{
condition.await();
}
System.out.println(getI());
if(go == length)
{
go = 1;
}
else
{
go++;
}
Thread.sleep(1000);
condition.signalAll();
}
catch (Exception e)
{
}
finally
{
lock.unlock();
}
}
}
};
t.start();
}
}
static class MyThread extends Thread
{
int i;
public MyThread(int i)
{
super();
this.i = i;
}
public int getI()
{
return i;
}
}
}
1、概念
ReentrantLock和synchronized都有天然的缺陷就是同一时间只能有一个线程获得锁,虽然保证了线程安全,但是效率极其低下。所以java又提供了读写锁,其中读锁是共享锁,在没有线程进行写入操作时,多个线程可以同时获得读锁;但是同一时刻只允许一个线程进行写操作。
2、读读共享,写写互斥
package com.amuro.studythread.chapter4_lock;
import java.util.Date;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class WRLockExample1
{
public static void main(String[] args) throws Exception
{
final File file = new File();
for(int i = 0; i < 10; i++)
{
new Thread(new Runnable()
{
@Override
public void run()
{
file.read();
}
}).start();
}
Thread.sleep(2000);
for(int i = 0; i < 10; i++)
{
new Thread(new Runnable()
{
@Override
public void run()
{
file.write();
}
}).start();
}
}
static class File
{
ReentrantReadWriteLock wrLock;
public File()
{
wrLock = new ReentrantReadWriteLock();
}
void read()
{
try
{
wrLock.readLock().lock();
System.out.println(
Thread.currentThread().getName() +
" get read lock on " +
new Date(System.currentTimeMillis()));
Thread.sleep(1000);
}
catch (Exception e)
{
}
finally
{
wrLock.readLock().unlock();
}
}
void write()
{
try
{
wrLock.writeLock().lock();
System.out.println(
Thread.currentThread().getName() +
" get write lock on " +
new Date(System.currentTimeMillis()));
Thread.sleep(1000);
}
catch(Exception e)
{
}
finally
{
wrLock.writeLock().unlock();
}
}
}
}
程序输出:
Thread-7 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-9 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-3 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-0 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-2 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-1 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-8 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-4 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-5 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-6 get read lock on Wed Jan 03 10:47:52 CST 2018
Thread-10 get write lock on Wed Jan 03 10:47:54 CST 2018
Thread-12 get write lock on Wed Jan 03 10:47:55 CST 2018
Thread-11 get write lock on Wed Jan 03 10:47:56 CST 2018
Thread-13 get write lock on Wed Jan 03 10:47:57 CST 2018
Thread-14 get write lock on Wed Jan 03 10:47:58 CST 2018
Thread-15 get write lock on Wed Jan 03 10:47:59 CST 2018
Thread-16 get write lock on Wed Jan 03 10:48:00 CST 2018
Thread-17 get write lock on Wed Jan 03 10:48:01 CST 2018
Thread-18 get write lock on Wed Jan 03 10:48:02 CST 2018
Thread-19 get write lock on Wed Jan 03 10:48:03 CST 2018
这个很好理解,不多说了;
3、读写互斥,写读互斥
package com.amuro.studythread.chapter4_lock;
import java.util.Date;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class WRLockExample2
{
public static void main(String[] args) throws Exception
{
final File file = new File();
new Thread(new Runnable()
{
@Override
public void run()
{
file.read();
}
}).start();
new Thread(new Runnable()
{
@Override
public void run()
{
file.write();
}
}).start();
}
static class File
{
ReentrantReadWriteLock wrLock;
public File()
{
wrLock = new ReentrantReadWriteLock();
}
void read()
{
try
{
wrLock.readLock().lock();
System.out.println(
Thread.currentThread().getName() +
" get read lock on " +
new Date(System.currentTimeMillis()));
Thread.sleep(5000);
}
catch (Exception e)
{
}
finally
{
wrLock.readLock().unlock();
}
}
void write()
{
try
{
wrLock.writeLock().lock();
System.out.println(
Thread.currentThread().getName() +
" get write lock on " +
new Date(System.currentTimeMillis()));
Thread.sleep(1000);
}
catch(Exception e)
{
}
finally
{
wrLock.writeLock().unlock();
}
}
}
}
程序输出:
Thread-0 get read lock on Wed Jan 03 11:05:47 CST 2018
Thread-1 get write lock on Wed Jan 03 11:05:52 CST 2018
可见当一个读写锁之间是互斥的,在读锁或者写锁没有unlock时,无法获得另一种锁。