在Java多线程中,可以使用synchronized关键字实现线程之间的同步互斥,在jdk1.5后新增的ReentrantLock类同样可达到此效果,且在使用上比synchronized更加灵活。

观察ReentrantLock类可以发现其实现了Lock接口

public class ReentrantLock implements Lock,java.io.Serializable

1、使用ReentrantLock实现同步

lock()方法:上锁

unlock()方法:释放锁


  1. /*
  2. * 使用ReentrantLock类实现同步
  3. * */
  4. class MyReenrantLock implements Runnable{
  5. //向上转型
  6. private Lock lock = new ReentrantLock();
  7. public void run() {
  8. //上锁
  9. lock.lock();
  10. for(int i = 0; i < 5; i++) {
  11. System.out.println("当前线程名: "+ Thread.currentThread().getName()+" ,i = "+i);
  12. }
  13. //释放锁
  14. lock.unlock();
  15. }
  16. }
  17. public class MyLock {
  18. public static void main(String[] args) {
  19. MyReenrantLock myReenrantLock = new MyReenrantLock();
  20. Thread thread1 = new Thread(myReenrantLock);
  21. Thread thread2 = new Thread(myReenrantLock);
  22. Thread thread3 = new Thread(myReenrantLock);
  23. thread1.start();
  24. thread2.start();
  25. thread3.start();
  26. }
  27. }

由此我们可以看出,只有当当前线程打印完毕后,其他的线程才可继续打印,线程打印的数据是分组打印,因为当前线程持有锁,但线程之间的打印顺序是随机的。

即调用lock.lock()代码的线程就持有了“对象监视器”,其他线程只有等待锁被释放再次争抢。

2、使用Condition实现等待/通知

synchronized关键字结合wait()和notify()及notifyAll()方法的使用可以实现线程的等待与通知模式。在使用notify()、notifyAll()方法进行通知时,被通知的线程是JVM随机选择的。

类ReentrantLock类同样可以实现该功能,需要借助Condition对象,可实现“选择性通知”。Condition类是jdk1.5提供的,且在一个Lock对象中可以创建多个Condition(对象监视器)实例。

Condition类的await():是当前执行任务的线程处于等待状态


  1. /*
  2. * 错误的使用Condition实现等待、通知
  3. * */
  4. class MyCondition implements Runnable{
  5. private Lock lock = new ReentrantLock();
  6. public Condition condition = lock.newCondition();
  7. public void run() {
  8. try {
  9. System.out.println("当前线程名:"+Thread.currentThread().getName()+" 开始等待时间:"+System.currentTimeMillis());
  10. //线程等待
  11. condition.await();
  12. System.out.println("我陷入了等待...");
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. }
  18. public class MyLock{
  19. public static void main(String[] args) {
  20. MyCondition myCondition = new MyCondition();
  21. Thread thread1 = new Thread(myCondition,"线程1");
  22. thread1.start();
  23. }
  24. }


观察运行结果可以发现,报出监视器出错的异常,解决的办法是我们必须在condition.await()方法调用前用lock.lock()代码获得同步监视器。对上述代码做出如下修改:


  1. /*
  2. * 使用Condition实现等待
  3. * */
  4. class MyCondition implements Runnable{
  5. private Lock lock = new ReentrantLock();
  6. public Condition condition = lock.newCondition();
  7. public void run() {
  8. try {
  9. //上锁
  10. lock.lock();
  11. System.out.println("当前线程名:"+Thread.currentThread().getName()+" 开始等待时间:"+System.currentTimeMillis());
  12. //线程等待
  13. condition.await();
  14. System.out.println("我陷入了等待...");
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }finally {
  18. //释放锁
  19. lock.unlock();
  20. System.out.println("锁释放了!");
  21. }
  22. }
  23. }
  24. public class MyLock{
  25. public static void main(String[] args) {
  26. MyCondition myCondition = new MyCondition();
  27. Thread thread1 = new Thread(myCondition,"线程1");
  28. thread1.start();
  29. }
  30. }

在控制台只打印出一句,原因是调用了Condition对象的await()方法,是的当前执行任务的线程进入等待状态。

Condition类的signal():是当前执行任务的线程处于等待状态


  1. /*
  2. * 使用Condition实现等待、通知
  3. * */
  4. class MyCondition implements Runnable{
  5. private Lock lock = new ReentrantLock();
  6. public Condition condition = lock.newCondition();
  7. public void run() {
  8. try {
  9. //上锁
  10. lock.lock();
  11. System.out.println(" 开始等待时间:"+System.currentTimeMillis());
  12. System.out.println("我陷入了等待...");
  13. //线程等待
  14. condition.await();
  15. //释放锁
  16. lock.unlock();
  17. System.out.println("锁释放了!");
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. //通知方法
  23. public void signal(){
  24. try {
  25. lock.lock();
  26. System.out.println("结束等待时间:"+System.currentTimeMillis());
  27. //通知等待线程
  28. condition.signal();
  29. } finally {
  30. lock.unlock();
  31. }
  32. }
  33. }
  34. public class MyLock{
  35. public static void main(String[] args) throws InterruptedException {
  36. MyCondition myCondition = new MyCondition();
  37. Thread thread1 = new Thread(myCondition,"线程1");
  38. thread1.start();
  39. Thread.sleep(3000);
  40. myCondition.signal();
  41. }
  42. }

观察结果我们成功地实现了等待通知。

可以得知:Object类中的wait()方法等同于Condition类中的await()方法。

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

Object类中的notify()方法等同于Condition类中的singal()方法。

Object类中的notifyAll()方法等同于Condition类中的singalAll()方法。

3、生产者消费者模式


  1. /*
  2. * 生产者、消费者模式
  3. * 一对一交替打印
  4. * */
  5. class MyServer{
  6. private ReentrantLock lock = new ReentrantLock();
  7. public Condition condition = lock.newCondition();
  8. public Boolean flag = false;
  9. public void set() {
  10. try {
  11. lock.lock();
  12. while(flag == true) {
  13. condition.await();
  14. }
  15. System.out.println("当前线程名:"+Thread.currentThread().getName()+" hello");
  16. flag = true;
  17. condition.signal();
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }finally {
  21. lock.unlock();
  22. }
  23. }
  24. public void get() {
  25. try {
  26. lock.lock();
  27. while(flag == false) {
  28. condition.await();
  29. }
  30. System.out.println("当前线程名:"+Thread.currentThread().getName()+" lemon");
  31. flag = false;
  32. condition.signal();
  33. } catch (InterruptedException e) {
  34. e.printStackTrace();
  35. }finally {
  36. lock.unlock();
  37. }
  38. }
  39. }
  40. class MyCondition1 extends Thread{
  41. private MyServer myServer;
  42. public MyCondition1(MyServer myServer) {
  43. super();
  44. this.myServer = myServer;
  45. }
  46. public void run() {
  47. for(int i = 0 ;i < Integer.MAX_VALUE;i++) {
  48. myServer.set();
  49. }
  50. }
  51. }
  52. class MyCondition2 extends Thread{
  53. private MyServer myServer;
  54. public MyCondition2(MyServer myServer) {
  55. super();
  56. this.myServer = myServer;
  57. }
  58. public void run() {
  59. for(int i = 0 ;i < Integer.MAX_VALUE;i++) {
  60. myServer.get();
  61. }
  62. }
  63. }
  64. public class MyLock{
  65. public static void main(String[] args) throws InterruptedException {
  66. MyServer myServer = new MyServer();
  67. MyCondition1 myCondition1 = new MyCondition1(myServer);
  68. MyCondition2 myCondition2 = new MyCondition2(myServer);
  69. myCondition1.start();
  70. myCondition2.start();
  71. }
  72. }


  1. /*
  2. * 生产者、消费者模式
  3. * 多对多交替打印
  4. * */
  5. class MyServer{
  6. private ReentrantLock lock = new ReentrantLock();
  7. public Condition condition = lock.newCondition();
  8. public Boolean flag = false;
  9. public void set() {
  10. try {
  11. lock.lock();
  12. while(flag == true) {
  13. System.out.println("可能会有连续的hello进行打印");
  14. condition.await();
  15. }
  16. System.out.println("当前线程名:"+Thread.currentThread().getName()+" hello");
  17. flag = true;
  18. condition.signal();
  19. } catch (InterruptedException e) {
  20. e.printStackTrace();
  21. }finally {
  22. lock.unlock();
  23. }
  24. }
  25. public void get() {
  26. try {
  27. lock.lock();
  28. while(flag == false) {
  29. System.out.println("可能会有连续的lemon进行打印");
  30. condition.await();
  31. }
  32. System.out.println("当前线程名:"+Thread.currentThread().getName()+" lemon");
  33. flag = false;
  34. condition.signal();
  35. } catch (InterruptedException e) {
  36. e.printStackTrace();
  37. }finally {
  38. lock.unlock();
  39. }
  40. }
  41. }
  42. class MyCondition1 extends Thread{
  43. private MyServer myServer;
  44. public MyCondition1(MyServer myServer) {
  45. super();
  46. this.myServer = myServer;
  47. }
  48. public void run() {
  49. for(int i = 0 ;i < Integer.MAX_VALUE;i++) {
  50. myServer.set();
  51. }
  52. }
  53. }
  54. class MyCondition2 extends Thread{
  55. private MyServer myServer;
  56. public MyCondition2(MyServer myServer) {
  57. super();
  58. this.myServer = myServer;
  59. }
  60. public void run() {
  61. for(int i = 0 ;i < Integer.MAX_VALUE;i++) {
  62. myServer.get();
  63. }
  64. }
  65. }
  66. public class MyLock{
  67. public static void main(String[] args) throws InterruptedException {
  68. MyServer myServer = new MyServer();
  69. MyCondition1[] myCondition1 = new MyCondition1[10];
  70. MyCondition2[] myCondition2 = new MyCondition2[10];
  71. for(int i = 0; i < 10; i++) {
  72. myCondition1[i] = new MyCondition1(myServer);
  73. myCondition2[i] = new MyCondition2(myServer);
  74. myCondition1[i].start();
  75. myCondition2[i].start();
  76. }
  77. }
  78. }

4、公平锁与非公平锁

锁Lock分为“公平锁”和“非公平锁”。

公平锁:表示线程获取锁的顺序是按照线程加锁的顺序来的进行分配的,即先来先得FIFO先进先出顺序。

非公平锁:一种获取锁的抢占机制,是随机拿到锁的,和公平锁不一样的是先来的不一定先拿到锁,这个方式可能造成某些线程一直拿不到锁,结果就是不公平的·。


  1. /*
  2. * 公平锁
  3. * */
  4. class MyService{
  5. private ReentrantLock lock;
  6. public MyService(boolean isFair) {
  7. super();
  8. lock = new ReentrantLock(isFair);
  9. }
  10. public void serviceMethod() {
  11. try {
  12. lock.lock();
  13. System.out.println("线程名:"+Thread.currentThread().getName()+"获得锁定");
  14. } finally {
  15. lock.unlock();
  16. }
  17. }
  18. }
  19. public class MyLock{
  20. public static void main(String[] args) {
  21. //设置当前为true公平锁
  22. final MyService myService = new MyService(true);
  23. Runnable runnable = new Runnable() {
  24. public void run() {
  25. System.out.println("线程名:"+Thread.currentThread().getName()+"运行了");
  26. myService.serviceMethod();
  27. }
  28. };
  29. Thread[] threads = new Thread[10];
  30. for(int i = 0;i < 10; i++) {
  31. threads[i] = new Thread(runnable);
  32. }
  33. for(int i = 0;i < 10; i++) {
  34. threads[i].start();
  35. }
  36. }
  37. }


由打印结果可以看出,基本呈现有序的状态,这就是公平锁的特点。


  1. /*
  2. * 非公平锁
  3. * */
  4. class MyService{
  5. private ReentrantLock lock;
  6. public MyService(boolean isFair) {
  7. super();
  8. lock = new ReentrantLock(isFair);
  9. }
  10. public void serviceMethod() {
  11. try {
  12. lock.lock();
  13. System.out.println("线程名:"+Thread.currentThread().getName()+"获得锁定");
  14. } finally {
  15. lock.unlock();
  16. }
  17. }
  18. }
  19. public class MyLock{
  20. public static void main(String[] args) {
  21. //设置当前为true公平锁
  22. final MyService myService = new MyService(false);
  23. Runnable runnable = new Runnable() {
  24. public void run() {
  25. System.out.println("线程名:"+Thread.currentThread().getName()+"运行了");
  26. myService.serviceMethod();
  27. }
  28. };
  29. Thread[] threads = new Thread[10];
  30. for(int i = 0;i < 10; i++) {
  31. threads[i] = new Thread(runnable);
  32. }
  33. for(int i = 0;i < 10; i++) {
  34. threads[i].start();
  35. }
  36. }
  37. }

非公平锁的运行结果基本都是无须的,则可以表明先start()启动的线程并不一定先获得锁。

5、使用ReentrantReadWriteLock类

类ReentrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行ReentrantLock.lock()方法后的任务。这样虽然保证了实例变量的线程安全性,但是效率低下。所以在Java中提供有读写锁ReentrantReadWriteLock类,使其效率可以加快。在某些不需要操作实例变量的方法中,完全可以使用ReentrantReadWriteLock来提升该方法代码运行速度。

读写锁表示两个锁:

读操作相关的锁,也成为共享锁。

写操作相关的锁,也叫排他锁。

多个读锁之间不互斥,读锁与写锁互斥,多个写锁互斥。

在没有线程Thread进行写入操作时,进行读操作的多个Thread可以获取读锁,但是进行写入操作时的Thread只有获取写锁后才能进行写入操作。

(1)多个读锁共享


  1. /*
  2. * 多个读锁共享
  3. * */
  4. class MyService{
  5. private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
  6. public void read() {
  7. try {
  8. //读锁
  9. lock.readLock().lock();
  10. System.out.println("线程名: "+Thread.currentThread().getName()+"获取读锁" );
  11. Thread.sleep(1000);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }finally {
  15. //释放读锁
  16. lock.readLock().unlock();
  17. }
  18. }
  19. }
  20. //线程1
  21. class Thread1 extends Thread{
  22. private MyService myService;
  23. public Thread1(MyService myService) {
  24. super();
  25. this.myService = myService;
  26. }
  27. public void run() {
  28. myService.read();
  29. }
  30. }
  31. //线程2
  32. class Thread2 extends Thread{
  33. private MyService myService;
  34. public Thread2(MyService myService) {
  35. super();
  36. this.myService = myService;
  37. }
  38. public void run() {
  39. myService.read();
  40. }
  41. }
  42. public class MyLock{
  43. public static void main(String[] args) {
  44. MyService myService = new MyService();
  45. Thread1 thread1 = new Thread1(myService);
  46. Thread2 thread2 = new Thread2(myService);
  47. thread1.start();
  48. thread2.start();
  49. }
  50. }

从打印结果可以看出,两个线程几乎同时进入lock()方法后面的代码。

说明在此时使用lock.readLock()读锁可以提高程序运行效率,允许多个线程同时执行lock()方法后的代码。

(2)多个写锁互斥


  1. /*
  2. * 多个写锁互斥
  3. * */
  4. class MyService{
  5. private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
  6. public void write() {
  7. try {
  8. //写锁
  9. lock.writeLock().lock();
  10. System.out.println("线程名: "+Thread.currentThread().getName()+"获取写锁,获得时间:"+System.currentTimeMillis() );
  11. Thread.sleep(1000);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }finally {
  15. //释放写锁
  16. lock.writeLock().unlock();
  17. }
  18. }
  19. }
  20. //线程1
  21. class Thread1 extends Thread{
  22. private MyService myService;
  23. public Thread1(MyService myService) {
  24. super();
  25. this.myService = myService;
  26. }
  27. public void run() {
  28. myService.write();
  29. }
  30. }
  31. //线程2
  32. class Thread2 extends Thread{
  33. private MyService myService;
  34. public Thread2(MyService myService) {
  35. super();
  36. this.myService = myService;
  37. }
  38. public void run() {
  39. myService.write();
  40. }
  41. }
  42. public class MyLock{
  43. public static void main(String[] args) {
  44. MyService myService = new MyService();
  45. Thread1 thread1 = new Thread1(myService);
  46. Thread2 thread2 = new Thread2(myService);
  47. thread1.start();
  48. thread2.start();
  49. }
  50. }

使用写锁代码writeLock.lock()的效果就是同一时间只允许一个线程执行lock()方法后的代码。

(3)读写/写读互斥


  1. /*
  2. * 读写/写读互斥,
  3. * */
  4. class MyService{
  5. private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
  6. public void read() {
  7. try {
  8. //读锁
  9. lock.readLock().lock();
  10. System.out.println("线程名: "+Thread.currentThread().getName()+"获取读锁,获得时间:"+System.currentTimeMillis() );
  11. Thread.sleep(1000);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. }finally {
  15. //释放读锁
  16. lock.readLock().unlock();
  17. }
  18. }
  19. public void write() {
  20. try {
  21. //写锁
  22. lock.writeLock().lock();
  23. System.out.println("线程名: "+Thread.currentThread().getName()+"获取写锁,获得时间:"+System.currentTimeMillis() );
  24. Thread.sleep(1000);
  25. } catch (InterruptedException e) {
  26. e.printStackTrace();
  27. }finally {
  28. //释放写锁
  29. lock.writeLock().unlock();
  30. }
  31. }
  32. }
  33. //线程1
  34. class Thread1 extends Thread{
  35. private MyService myService;
  36. public Thread1(MyService myService) {
  37. super();
  38. this.myService = myService;
  39. }
  40. public void run() {
  41. myService.read();
  42. }
  43. }
  44. //线程2
  45. class Thread2 extends Thread{
  46. private MyService myService;
  47. public Thread2(MyService myService) {
  48. super();
  49. this.myService = myService;
  50. }
  51. public void run() {
  52. myService.write();
  53. }
  54. }
  55. public class MyLock{
  56. public static void main(String[] args) {
  57. MyService myService = new MyService();
  58. Thread1 thread1 = new Thread1(myService);
  59. Thread2 thread2 = new Thread2(myService);
  60. thread1.start();
  61. thread2.start();
  62. }
  63. }

此运行结果说明“读写/写读”操作是互斥的。

由此可表明:只要出现“写”操作,就是互斥的。

Lock锁的使用的更多相关文章

  1. Lock锁的使用示例

    Lock锁是java5用来代替synchronized的一种面向对象的锁的方案 public class LockDemo { /** * Lock是用来替换synchronized, 优点是Lock ...

  2. Android(java)学习笔记69:JDK5之后的Lock锁的概述和使用

    1. Lock锁的概述: java.util.concurrent.locks,接口Lock 首先Lock是一个接口,Lock实现提供了比使用synchronized方法 和 同步代码块更为广泛的锁定 ...

  3. python多线程threading.Lock锁用法实例

    本文实例讲述了python多线程threading.Lock锁的用法实例,分享给大家供大家参考.具体分析如下: python的锁可以独立提取出来 mutex = threading.Lock() #锁 ...

  4. Lock锁_线程_线程域

    using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using Sy ...

  5. (删)Java线程同步实现二:Lock锁和Condition

    在上篇文章(3.Java多线程总结系列:Java的线程同步实现)中,我们介绍了用synchronized关键字实现线程同步.但在Java中还有一种方式可以实现线程同步,那就是Lock锁. 一.同步锁 ...

  6. 转: 【Java并发编程】之二十:并发新特性—Lock锁和条件变量(含代码)

    简单使用Lock锁 Java5中引入了新的锁机制--Java.util.concurrent.locks中的显式的互斥锁:Lock接口,它提供了比synchronized更加广泛的锁定操作.Lock接 ...

  7. 使用Lock锁生产者消费者模式

    package com.java.concurrent; import java.util.concurrent.locks.Condition; import java.util.concurren ...

  8. JAVA基础再回首(二十五)——Lock锁的使用、死锁问题、多线程生产者和消费者、线程池、匿名内部类使用多线程、定时器、面试题

    JAVA基础再回首(二十五)--Lock锁的使用.死锁问题.多线程生产者和消费者.线程池.匿名内部类使用多线程.定时器.面试题 版权声明:转载必须注明本文转自程序猿杜鹏程的博客:http://blog ...

  9. java并发编程的艺术——第五章总结(Lock锁与队列同步器)

    Lock锁 锁是用来控制多个线程访问共享资源的方式. 一般来说一个锁可以防止多个线程同时访问共享资源(但有些锁可以允许多个线程访问共享资源,如读写锁). 在Lock接口出现前,java使用synchr ...

  10. Java中的Lock锁

    Lock锁介绍: 在java中可以使用 synchronized 来实现多线程下对象的同步访问,为了获得更加灵活使用场景.高效的性能,java还提供了Lock接口及其实现类ReentrantLock和 ...

随机推荐

  1. oracle静默安装完成后,重启数据库,错误ORA-01102: cannot mount database in EXCLUSIVE mode

    静默安装oracle完成后,登录数据库激活用户,无法更改,提示未载入数据库,关闭后重启报错: 1.找到安装目录下的$ORACLE_HOME/dbs/ 目录下,查看当前使用lkORCL文件的用户(fus ...

  2. 利用Wireshark 解密HTTPS流量

    在我之前的一篇文章中已经介绍了一种解密HTTPS流量的一种方法,大致方法就是客户端手动信任中间人,然后中间人重新封包SSL流量. 文章地址: http://professor.blog.51cto.c ...

  3. 记一次线上环境 ES 主分片为分配故障

    故障前提 ElasticSearch 版本:5.2 集群节点数:5 索引主分片数:5 索引分片副本数:1 线上环境ES存储的数据量很大,当天由于存储故障,导致一时间 5个节点的 ES 集群,同时有两个 ...

  4. ELK集群之logstash(5)

    Logstash工作原理   Logstash事件处理有三个阶段:inputs → filters → outputs.是一个接收,处理,转发日志的工具.支持系统日志,webserver日志,错误日志 ...

  5. C++ 内存四区 理解总结

    内存模型图(4G) 整体简单说明 32位CPU可寻址4G线性空间,每个进程都有各自独立的4G逻辑地址,其中 03G是用户空间**,**34G是内核空间即3G用户空间和1G内核空间,不同进程相同的逻辑地 ...

  6. msfsploit框架的使用——ms17_010漏洞的利用

    开门见山,首先输入msfconsole打开msf控制台 全球最牛逼的渗透测试框架就是长这个样子(每次打开时,显示的图案都不一样) 然后搜索ms17_010的相关模块,得到了六条结果,我们需要用的是编号 ...

  7. Java反射判断对象实例所有属性是否为空

    https://www.jb51.net/article/201647.htm public static Boolean ObjectAllFieldsEmpty(Object obj) throw ...

  8. CodeGuide 300+文档、100+代码库,一个指导程序员写代码的,Github 仓库开源啦!

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.路怎样走,让你们自己挑 B站 视频:https://www.bilibili.com/vi ...

  9. selenium基本使用,及cannot find chrome binary解决方案

    什么是selenium? Selenium是一个用于Web应用程序测试的工具. Selenium 测试直接运行在浏览器中,就像真正的用户在操作一样. 支持通过各种driver(FirfoxDriver ...

  10. 基于Guava API实现异步通知和事件回调

    本文节选自<设计模式就该这样学> 1 基于Java API实现通知机制 当小伙伴们在社区提问时,如果有设置指定用户回答,则对应的用户就会收到邮件通知,这就是观察者模式的一种应用场景.有些小 ...