Java多线程编程核心技术--Lock的使用(一)

时间:2021-11-30 13:58:56
使用ReentrantLock类

在Java多线程中,可以使用synchronized关键字来实现线程之间的同步互斥,但在JDK1.5中新增加了ReentrantLock类也能达到同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路分支通知等功能,而且在使用上也比synchronized更加灵活。

使用ReentrantLock实现同步:测试1
public class Service {
private Lock lock = new ReentrantLock();
public void testMethod() {
lock.lock();
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " i=" + (i + 1));
}
lock.unlock();
}
} public class MyThread extends Thread {
private Service service;
public MyThread(Service service) {
super();
this.service = service;
} @Override
public void run() {
service.testMethod();
}
} public class Main {
public static void main(String[] args) {
Service service = new Service();
MyThread t1 = new MyThread(service);
MyThread t2 = new MyThread(service);
MyThread t3 = new MyThread(service);
MyThread t4 = new MyThread(service);
MyThread t5 = new MyThread(service);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}

控制台打印结果如下:

Thread-3 i=1
Thread-3 i=2
Thread-3 i=3
Thread-3 i=4
Thread-3 i=5
Thread-0 i=1
Thread-0 i=2
Thread-0 i=3
Thread-0 i=4
Thread-0 i=5
Thread-1 i=1
Thread-1 i=2
Thread-1 i=3
Thread-1 i=4
Thread-1 i=5
Thread-2 i=1
Thread-2 i=2
Thread-2 i=3
Thread-2 i=4
Thread-2 i=5
Thread-4 i=1
Thread-4 i=2
Thread-4 i=3
Thread-4 i=4
Thread-4 i=5

可见,当前线程打印完毕之后将锁进行释放,其他线程才可以继续打印。线程打印数据是分组打印,因为当前线程已经持有锁,但线程之间打印的顺序是随机的。


使用ReentrantLock实现同步:测试2
public class Service {
private Lock lock = new ReentrantLock();
public void methodA() {
try {
lock.lock();
System.out.println("methodA begin " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("methodA end " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void methodB() {
try {
lock.lock();
System.out.println("methodB begin " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("methodB end " + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
} public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.methodA();
}
} public class ThreadAA extends Thread {
private Service service;
public ThreadAA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.methodA();
}
} public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.methodA();
}
} public class ThreadBB extends Thread {
private Service service;
public ThreadBB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.methodA();
}
} public class Main {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadAA aa = new ThreadAA(service);
aa.setName("AA");
aa.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
ThreadBB bb = new ThreadBB(service);
bb.setName("BB");
bb.start();
}
}

控制台打印结果如下:

methodA begin A time=1467162810185
methodA end A time=1467162815186
methodA begin AA time=1467162815186
methodA end AA time=1467162820187
methodA begin B time=1467162820187
methodA end B time=1467162825187
methodA begin BB time=1467162825187
methodA end BB time=1467162830188

可见,调用lock.lock()代码的线程就持有了“对象监视器”,其他线程只能等待锁被释放时再次争抢。效果和使用synchronized关键字一样,线程之间还是顺序执行的。


使用condition实现等待/通知:错误用法与解决

关键字synchronized与wait()和notify()/notifyAll()方法结合可以实现等待/通知模式,类ReentrantLock也可以实现同样的功能,但需要借助Condition对象。Condition类是在JDK5中出现的技术,使用它有更好的灵活性,比如可以实现多路通知功能,也就是在一个Lock对象里面可以创建多个Condition(即对象监视器)实例,线程对象可以注册在知道的Condition中,从而可以有选择性地进行线程通知,在调度线程上更加灵活。

在使用notify()/notifyAll()方法进行通知时,被通知的线程是由JVM随机选择的。但使用ReentrantLock结合Condition类是可以实现前面介绍过的“选择性通知”,这个功能是非常重要的,而且在Condition类中是默认提供的。

而synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在它一个对象的身上。线程开始notifyAll()时,需要通知所有的WAITING线程,没有选择权,会出现相当大的效率问题。

看以下代码:

public class Service {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void await() {
try {
condition.await();
} catch (Exception e) {
e.printStackTrace();
}
}
} public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.await();
}
} public class Main {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.start();
}
}

运行以上代码,控制台抛出异常:

java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(Unknown Source)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(Unknown Source)
at com.umgsai.thread.thread46.Service.await(Service.java:14)
at com.umgsai.thread.thread46.ThreadA.run(ThreadA.java:11)

异常信息是监视器出错,解决的办法是必须在condition.await()方法调用之前调用lock.lock()代码获得同步监视器。将Service类做如下修改:

public class Service {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void await() {
try {
lock.lock();
System.out.println("A");
condition.await();
System.out.println("B");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println("锁被释放了");
}
}
}

重新运行程序,控制台打印结果如下:

A

程序不结束,原因是调用了Condition对象的await()方法,使当前执行任务的线程进入了等待WAITING状态。


正确使用Condition实现等待/通知
public class Service {
private Lock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void await() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + " await时间为" + System.currentTimeMillis());
condition.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void signal() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + " signal时间为" + System.currentTimeMillis());
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
} public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.await();
};
} public class Main {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.start();
Thread.sleep(2000);
service.signal();
}
}

控制台打印结果如下:

Thread-0 await时间为1467293210228
main signal时间为1467293212227

成功实现等待/通知模式。

Object类中的wait()等待相当于Condition类中的await()方法。

Object类中的wait(long timeout)方法相当于Condition类中的await(long time, TimeUnit unit)方法。

Object类中的notify()方法相当于Condition类中的signal()方法。

Object类中的notifyAll()方法相当于Condition类中的signalAll()方法。


使用多个Condition实现通知部分线程:错误用法
public class Service {
private Lock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void awaitA() {
try {
lock.lock();
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + " begin awaitA时间为" + System.currentTimeMillis());
condition.await();//释放锁
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end awaitA时间为" + System.currentTimeMillis());
lock.unlock();
}
} public void awaitB() {
try {
lock.lock();
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + " begin awaitB时间为" + System.currentTimeMillis());
condition.await();//释放锁
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end awaitB时间为" + System.currentTimeMillis());
lock.unlock();
}
} public void signalAll() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + " end signalAll时间为" + System.currentTimeMillis());
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
} public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
} @Override
public void run() {
service.awaitA();
}
} public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
} @Override
public void run() {
service.awaitB();
}
} public class Main {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
Thread.sleep(3000);
service.signalAll();
}
}

控制台打印结果如下:

A begin awaitA时间为1467293693906
B begin awaitB时间为1467293694407
main end signalAll时间为1467293696406
A end awaitA时间为1467293696906
B end awaitB时间为1467293697407

程序运行后A和B都被唤醒了。


使用多个Condition实现通知部分线程:正确用法

将上面例子中Service作如下修改:

public class Service {
private Lock lock = new ReentrantLock();
public Condition conditionA = lock.newCondition();
public Condition conditionB = lock.newCondition();
public void awaitA() {
try {
lock.lock();
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + " begin awaitA时间为" + System.currentTimeMillis());
conditionA.await();//释放锁
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end awaitA时间为" + System.currentTimeMillis());
lock.unlock();
}
} public void awaitB() {
try {
lock.lock();
Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + " begin awaitB时间为" + System.currentTimeMillis());
conditionB.await();//释放锁
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end awaitB时间为" + System.currentTimeMillis());
lock.unlock();
}
} public void signalAll_A() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + " end signalAll_A时间为" + System.currentTimeMillis());
conditionA.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} public void signalAll_B() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + " end signalAll_B时间为" + System.currentTimeMillis());
conditionB.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
} public class Main {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.setName("A");
a.start();
ThreadB b = new ThreadB(service);
b.setName("B");
b.start();
Thread.sleep(3000);
service.signalAll_A();//通知在conditionA上等待的线程
Thread.sleep(2000);
service.signalAll_B();//通知在conditionB上等待的线程
}
}

控制台打印结果如下:

A begin awaitA时间为1467293966937
B begin awaitB时间为1467293967437
main end signalAll_A时间为1467293969438
A end awaitA时间为1467293969938
main end signalAll_B时间为1467293971438
B end awaitB时间为1467293971939

实现生产者/消费者模式:一对一交替打印
public class Service {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean hasValue = false;
public void set() {
try {
lock.lock();
while (hasValue) {
condition.await();
}
System.out.println("★");
hasValue = true;
condition.signal();//通知在condition上等待的线程
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void get() {
try {
lock.lock();
while (!hasValue) {
condition.await();
}
System.out.println("☆");
hasValue = false;
condition.signal();//通知在condition上等待的线程
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
} public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
service.set();
}
}
} public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
service.get();
}
}
} public class Main {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
ThreadB b = new ThreadB(service);
a.start();
b.start();
}
}

控制台打印结果如下:

......






......

将以上代码做如下修改:

public class Main {
public static void main(String[] args) {
Service service = new Service();
ThreadA[] a = new ThreadA[10];
ThreadB[] b = new ThreadB[10];
for (int i = 0; i < 10; i++) {
a[i] = new ThreadA(service);
a[i].start();
b[i] = new ThreadB(service);
b[i].start();
}
}
}

此时控制台打印结果如下:

......
有可能★连续


有可能★连续

有可能☆连续

有可能★连续

有可能☆连续

有可能★连续

有可能☆连续
有可能☆连续
有可能☆连续

有可能★连续
有可能★连续
....

程序运行一段时间后出现假死现象。前面有程序和这个程序的原理一样。解决方法是修改Service里面的condition.signal()为condition.signalAll()。


公平锁与非公平锁
/**
* 公平锁
*/
public class Service {
private ReentrantLock lock;
public Service(boolean isFair) {
super();
lock = new ReentrantLock(isFair);
} public void serviceMethod() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "获得锁定");
} finally{
lock.unlock();
}
}
} public class RunFair {
public static void main(String[] args) {
final Service service = new Service(true);
Runnable runnable = new Runnable() { @Override
public void run() {
System.out.println(Thread.currentThread().getName() + "运行了");
service.serviceMethod();
}
};
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(runnable);
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();;
}
}
}

控制台打印结果如下:

Thread-0运行了
Thread-1运行了
Thread-0获得锁定
Thread-1获得锁定
Thread-2运行了
Thread-2获得锁定
Thread-3运行了
Thread-4运行了
Thread-3获得锁定
Thread-4获得锁定
Thread-5运行了
Thread-5获得锁定
Thread-6运行了
Thread-8运行了
Thread-6获得锁定
Thread-7运行了
Thread-8获得锁定
Thread-7获得锁定
Thread-9运行了
Thread-9获得锁定

打印结果基本呈有序的状态,这就是公平锁的特点。


getHoldCount方法
/**
* getHoldCount()方法查询当前线程保持此锁定的个数
* 也就是调用()方法的次数
*/
public class Service {
private ReentrantLock lock = new ReentrantLock();
public void serviceMethod1() {
try {
lock.lock();
System.out.println("serviceMethod1 getHoldCount=" + lock.getHoldCount());//1
serviceMethod2();
} finally{
lock.unlock();
}
}
public void serviceMethod2() {
try {
lock.lock();
System.out.println("serviceMethod2 getHoldCount=" + lock.getHoldCount());//2
} finally{
lock.unlock();
}
}
public static void main(String[] args) {
Service service = new Service();
service.serviceMethod1();
}
}
getQueueLength()方法
//getQueueLength返回正在等待此锁定的线程估计数
public class MyService {
public ReentrantLock lock = new ReentrantLock();
public void serviceMethod1() {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "进入方法");
Thread.sleep(Integer.MAX_VALUE);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
final MyService myService = new MyService();
Runnable runnable = new Runnable() { @Override
public void run() {
myService.serviceMethod1();
}
};
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(runnable);
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
Thread.sleep(2000);
System.out.println(myService.lock.getQueueLength() + "在等待获取锁");
}
}

控制台打印结果如下:

Thread-0进入方法
9在等待获取锁
getWaitQueueLength()方法
public class MyService1 {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void waitMethod() {
try {
lock.lock();
condition.await();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} public void notifyMethod() {
try {
lock.lock();
System.out.println(lock.getWaitQueueLength(condition) + "个线程在等待condition");//10
condition.signal();
System.out.println(lock.getWaitQueueLength(condition) + "个线程在等待condition");//9
condition.signal();
System.out.println(lock.getWaitQueueLength(condition) + "个线程在等待condition");//8
condition.signalAll();
System.out.println(lock.getWaitQueueLength(condition) + "个线程在等待condition");//7
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} public static void main(String[] args) throws InterruptedException {
final MyService1 service = new MyService1();
Runnable runnable = new Runnable() { @Override
public void run() {
service.waitMethod();
}
};
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(runnable);
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
Thread.sleep(2000);
service.notifyMethod();
}
}

控制台打印结果如下:

10个线程在等待condition
9个线程在等待condition
8个线程在等待condition
0个线程在等待condition

getWaitQueueLength(condition)方法返回等待与此锁定相关的给定条件condition的线程估计数。

public class Service {
public ReentrantLock lock = new ReentrantLock();
public Condition condition = lock.newCondition();
public void waitMethod() {
try {
lock.lock();//获得锁
Thread.sleep(Integer.MAX_VALUE);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
final Service service = new Service();
Runnable runnable = new Runnable() { @Override
public void run() {
service.waitMethod();
}
};
Thread a = new Thread(runnable);
a.start();
Thread.sleep(500);
Thread b = new Thread(runnable);
b.start();
Thread.sleep(500);
System.out.println(service.lock.hasQueuedThread(a));//false 查询指定的线程是否正在等待此锁定
System.out.println(service.lock.hasQueuedThread(b));//true
System.out.println(service.lock.hasQueuedThreads());//true 查询是否有线程正在等待此锁
}
}

public class MyService {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void waitMethod() {
try {
lock.lock();
condition.await();//进入WAITTING状态,释放锁
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void notifyMethod() {
try {
lock.lock();
System.out.println("是否有线程正在等待condition:" + lock.hasWaiters(condition));
System.out.println("正在等待condition的线程数是:" + lock.getWaitQueueLength(condition));//10个线程等待
condition.signal();//唤醒一个线程
System.out.println("正在等待condition的线程数是:" + lock.getWaitQueueLength(condition));//9个线程等待
condition.signalAll();//剩下的个全部唤醒
System.out.println("正在等待condition的线程数是:" + lock.getWaitQueueLength(condition));//0个线程等待
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
final MyService service = new MyService();
Runnable runnable = new Runnable() { @Override
public void run() {
service.waitMethod();
}
};
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(runnable);
threads[i].start();
}
Thread.sleep(2000);
service.notifyMethod();
}
}

使用Condition实现顺序执行
public class Run {
volatile private static int nextPrintWho = 1;
private static ReentrantLock lock = new ReentrantLock();
final private static Condition conditionA = lock.newCondition();
final private static Condition conditionB = lock.newCondition();
final private static Condition conditionC = lock.newCondition();
public static void main(String[] args) {
Thread threadA = new Thread(){
public void run() {
try {
lock.lock();
while (nextPrintWho != 1) {
conditionA.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadA " + (i + 1));
}
nextPrintWho = 2;
conditionB.signalAll();//唤醒B组
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread threadB = new Thread(){
public void run() {
try {
lock.lock();
while (nextPrintWho != 2) {
conditionB.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadB " + (i + 1));
}
nextPrintWho = 3;
conditionC.signalAll();//唤醒C组
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread threadC = new Thread(){
public void run() {
try {
lock.lock();
while (nextPrintWho != 3) {
conditionC.await();
}
for (int i = 0; i < 3; i++) {
System.out.println("ThreadC " + (i + 1) + " " + System.currentTimeMillis());
}
nextPrintWho = 1;
conditionA.signalAll();//唤醒A组
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread[] aThreads = new Thread[5];
Thread[] bThreads = new Thread[5];
Thread[] cThreads = new Thread[5];
for (int i = 0; i < cThreads.length; i++) {
aThreads[i] = new Thread(threadA);
bThreads[i] = new Thread(threadB);
cThreads[i] = new Thread(threadC);
aThreads[i].start();
bThreads[i].start();
cThreads[i].start();
}
}
}

控制台输入结果如下:

ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1 1468067302315
ThreadC 2 1468067302315
ThreadC 3 1468067302315
ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1 1468067302315
ThreadC 2 1468067302315
ThreadC 3 1468067302315
ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1 1468067302315
ThreadC 2 1468067302315
ThreadC 3 1468067302315
ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1 1468067302316
ThreadC 2 1468067302316
ThreadC 3 1468067302316
ThreadA 1
ThreadA 2
ThreadA 3
ThreadB 1
ThreadB 2
ThreadB 3
ThreadC 1 1468067302316
ThreadC 2 1468067302316
ThreadC 3 1468067302316

使用ReentrantReadWriteLock类

ReentrantLock具有完全互斥排他的效果,同一时刻只有一个线程在执行ReentrantLock.lock()方法后面的任务。使用读写锁ReentrantReadWriteLock可以区分读写操作。读操作不互斥时,可以使用读锁让多个线程同时读。写锁互斥时,同一时刻值允许一个线程写。

读读共享
public class Service {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
try {
try {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + "获得读锁" + System.currentTimeMillis());
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
} catch (Exception e) {
e.printStackTrace();
}
}
} public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.read();
}
} public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.read();
}
} public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
ThreadB b = new ThreadB(service);
a.start();
b.start();
}
}

程序运行结果如下:

Thread-0获得读锁1468068667767
Thread-1获得读锁1468068667767

可以看出,两个线程同时进入lock()方法后面的代码。


写写互斥

将以上代码的Service类做如下修改:

public class Service {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void write() {
try {
try {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "获得写锁" + System.currentTimeMillis());
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

程序运行结果如下:

Thread-0获得写锁1468069134317
Thread-1获得写锁1468069135322

可见此时两个线程以排队方式执行lock()后面的代码。


读写互斥
public class Service {
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
try {
try {
lock.readLock().lock();
System.out.println(Thread.currentThread().getName() + "获得读锁" + System.currentTimeMillis());
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void write() {
try {
try {
lock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "获得写锁" + System.currentTimeMillis());
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();
}
} catch (Exception e) {
e.printStackTrace();
}
}
} public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.read();
}
} public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.write();
}
} public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
ThreadB b = new ThreadB(service);
a.start();
b.start();
}
}

程序运行结果如下:

Thread-0获得读锁1468069343031
Thread-1获得写锁1468069344033

两个线程以排队方式执行lock()后面的代码,可见读写操作是互斥的。

将以上代码做如下修改:

public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
ThreadB b = new ThreadB(service);
b.start();//先启动写线程
a.start();//后启动读线程
}
}

此时程序运行结果如下:

Thread-1获得写锁1468069625161
Thread-0获得读锁1468069626166

可见,两个线程也是以排队方式执行lock()后面的代码的。

综合ReentrantReadWriteLock类的几个实体程序,“读写” “写写” “写读”都是互斥的。“读读”是异步的,非互斥的。