Java多线程编程核心技术--Lock的使用(一)
2017-01-09 14:07
706 查看
使用ReentrantLock类
在Java多线程中,可以使用synchronized关键字来实现线程之间的同步互斥,但在JDK1.5中新增加了ReentrantLock类也能达到同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路分支通知等功能,而且在使用上也比synchronized更加灵活。
使用ReentrantLock实现同步:测试1
控制台打印结果如下:
可见,当前线程打印完毕之后将锁进行释放,其他线程才可以继续打印。线程打印数据是分组打印,因为当前线程已经持有锁,但线程之间打印的顺序是随机的。
使用ReentrantLock实现同步:测试2
控制台打印结果如下:
可见,调用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线程,没有选择权,会出现相当大的效率问题。
看以下代码:
运行以上代码,控制台抛出异常:
异常信息是监视器出错,解决的办法是必须在condition.await()方法调用之前调用lock.lock()代码获得同步监视器。将Service类做如下修改:
重新运行程序,控制台打印结果如下:
程序不结束,原因是调用了Condition对象的await()方法,使当前执行任务的线程进入了等待WAITING状态。
正确使用Condition实现等待/通知
控制台打印结果如下:
成功实现等待/通知模式。
Object类中的wait()等待相当于Condition类中的await()方法。
Object类中的wait(long timeout)方法相当于Condition类中的await(long time, TimeUnit unit)方法。
Object类中的notify()方法相当于Condition类中的signal()方法。
Object类中的notifyAll()方法相当于Condition类中的signalAll()方法。
使用多个Condition实现通知部分线程:错误用法
控制台打印结果如下:
程序运行后A和B都被唤醒了。
使用多个Condition实现通知部分线程:正确用法
将上面例子中Service作如下修改:
控制台打印结果如下:
实现生产者/消费者模式:一对一交替打印
控制台打印结果如下:
将以上代码做如下修改:
此时控制台打印结果如下:
程序运行一段时间后出现假死现象。前面有程序和这个程序的原理一样。解决方法是修改Service里面的condition.signal()为condition.signalAll()。
公平锁与非公平锁
控制台打印结果如下:
打印结果基本呈有序的状态,这就是公平锁的特点。
getHoldCount方法
getQueueLength()方法
控制台打印结果如下:
getWaitQueueLength()方法
控制台打印结果如下:
getWaitQueueLength(condition)方法返回等待与此锁定相关的给定条件condition的线程估计数。
使用Condition实现顺序执行
控制台输入结果如下:
使用ReentrantReadWriteLock类
ReentrantLock具有完全互斥排他的效果,同一时刻只有一个线程在执行ReentrantLock.lock()方法后面的任务。使用读写锁ReentrantReadWriteLock可以区分读写操作。读操作不互斥时,可以使用读锁让多个线程同时读。写锁互斥时,同一时刻值允许一个线程写。
读读共享
程序运行结果如下:
可以看出,两个线程同时进入lock()方法后面的代码。
写写互斥
将以上代码的Service类做如下修改:
程序运行结果如下:
可见此时两个线程以排队方式执行lock()后面的代码。
读写互斥
程序运行结果如下:
两个线程以排队方式执行lock()后面的代码,可见读写操作是互斥的。
将以上代码做如下修改:
此时程序运行结果如下:
可见,两个线程也是以排队方式执行lock()后面的代码的。
综合ReentrantReadWriteLock类的几个实体程序,“读写” “写写” “写读”都是互斥的。“读读”是异步的,非互斥的。
在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类的几个实体程序,“读写” “写写” “写读”都是互斥的。“读读”是异步的,非互斥的。
相关文章推荐
- Java多线程编程核心技术(第四章Lock的使用)
- Java多线程编程核心技术--Lock的使用(一)
- java多线程编程核心技术4-Lock的使用
- Java多线程核心技术(四):Lock的使用
- 多线程编程核心技术读书笔记(四):Lock的使用
- 【Java多线程编程核心技术】4.Lock的使用-笔记总结
- java 多线程核心技术 第四章 lock的使用
- Java多线程编程核心技术---对象及变量的并发访问(二)
- java多线程编程核心技术知识点总结
- Java多线程编程核心技术---对象及变量的并发访问(一)
- Java多线程编程核心技术---对象及变量的并发访问(一)
- Java多线程编程核心技术——生产者消费者模型
- Java多线程编程核心技术---学习分享
- Java多线程编程核心技术--定时器
- 一、java多线程编程核心技术之(笔记)——多线程的实现
- Java多线程编程核心技术---单例模式与多线程
- Java多线程编程核心技术---线程间通信(一)
- Java多线程编程核心技术 第一章笔记
- Java多线程编程核心技术---单例模式与多线程
- 好读书不求甚解(一)Java多线程编程核心技术