1、Java 实现同步的几种方式

方式一:使用 synchronized 关键字与 Object#wait()/notifyAll()
  • Object#wait():释放当前锁,使该线程进入等待状态(阻塞状态)。
  • Object#notify():在所有等待线程中随机唤醒一个线程,让它获得锁。
  • Object#notifyAll():唤醒所有等待的线程,让它们一起竞争锁,最后其中之一获得锁。
方式二:使用 ReentrantLock(可重入锁)以及 Condition(条件)。
  • Lock#lock():加锁。
  • Lock#unlock():释放锁。
  • Condition#await():释放当前锁,使该线程进入等待状态(阻塞状态)。
  • Condition#signal():在所有等待线程中随机唤醒一个线程,让它获得锁。
  • Condition#signalAll():唤醒所有等待的线程,让它们一起竞争锁,最后其中之一获得锁。
方式三:使用 Semaphore(信号量)
  • new Semaphore(1):互斥的信号量。
  • Semaphore#acquire():获取一个许可,如果没有就等待。
  • Semaphore#release():释放一个许可。
注意:方式可能不仅仅上述几种,这里只是写出来相对应常见的三种方式。
  • java.util.concurrent.* 并发包下的工具类,分别有:
  • (1)ReentrantLock(可重入锁):指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提锁对象得是同一个对象或者class),不会因为之前已经获取过还没释放而阻塞。
  • (2)Semaphore(信号量):是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。
  • (3)CountDownLatch: 允许一个或多个线程等待其他线程完成操作。
  • (4)CyclicBarrier :字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。

2、Java 死锁

  • 所谓死锁,就是多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
2.1 死锁触发的四大条件?
  • (1)互斥使用:即当资源被一个线程使用(占有)时,别的线程不能使用;
  • (2)不可抢占:即资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放;
  • (3)请求与保持:即当资源请求者在请求其他的资源的同时保持对原有资源的占有;
  • (4)循环等待:即存在一个等待队列:P1 占有 P2 的资源,P2 占有 P1 的资源,这样就形成了一个等待环路。
2.2 如何避免死锁?
  • (1)加锁顺序:即线程按照一定的顺序加锁;
  • (2)加锁时限:即线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁;
  • (3)死锁检测。死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph 等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。
2.3 当检测出死锁后,这些线程该做些什么?
  • 方案一:释放所有锁,回退,并且等待一段随机的时间后重试;
  • 方案二:给这些线程设置优先级,让一个(或几个)线程回退,剩下的线程就像没发生死锁一样继续保持着它们需要的锁。如果赋予这些线程的优先级是固定不变的,同一批线程总是会拥有更高的优先级。为避免这个问题,可以在死锁发生的时候设置随机的优先级。
2.4 死锁示例:
/*** 题目:* 写一个死锁程序* <p>* 解题思路: synchronized 或 Lock*/
public class TH_01_死锁 {private final static Object lockA = new Object();private final static Object lockB = new Object();public static void main(String[] args) {new ThreadA().start();new ThreadB().start();}static class ThreadA extends Thread {@Overridepublic void run() {try {System.out.println("ThreadA 开始执行");synchronized (lockA) {System.out.println("ThreadA 锁住了 lockA");Thread.sleep(1000);// 此处等待是给 ThreadB 能锁住机会synchronized (lockB) {System.out.println("ThreadA 锁住了 lockB");}}} catch (Exception e) {e.printStackTrace();}}}static class ThreadB extends Thread {@Overridepublic void run() {System.out.println("ThreadB 开始执行");try {synchronized (lockB) {System.out.println("ThreadB 锁住了 lockB");Thread.sleep(1000);// 此处等待是给 ThreadB 能锁住机会synchronized (lockA) {System.out.println("ThreadB 锁住了 lockA");}}} catch (Exception e) {e.printStackTrace();}}}
}========== 运行结果 ==========
ThreadA 开始执行
ThreadA 锁住了 lockA
ThreadB 开始执行
ThreadB 锁住了 lockB
2.5 死锁解决方案示例
/*** 题目:* 写一个死锁解决方案程序* <p>* 解题思路: synchronized 或 Lock*/
public class TH_01_死锁解决方案 {private final static Object lockA = new Object();private final static Object lockB = new Object();public static void main(String[] args) {new ThreadA().start();new ThreadB().start();}static class ThreadA extends Thread {@Overridepublic void run() {try {System.out.println("ThreadA 开始执行");synchronized (lockA) {System.out.println("ThreadA 锁住了 lockA");Thread.sleep(1000);// 此处等待是给 ThreadB 能锁住机会synchronized (lockB) {System.out.println("ThreadA 锁住了 lockB");}}} catch (Exception e) {e.printStackTrace();}}}static class ThreadB extends Thread {@Overridepublic void run() {System.out.println("ThreadB 开始执行");try {synchronized (lockA) {System.out.println("ThreadB 锁住了 lockA");Thread.sleep(1000);// 此处等待是给 ThreadB 能锁住机会synchronized (lockB) {System.out.println("ThreadB 锁住了 lockB");}}} catch (Exception e) {e.printStackTrace();}}}
}========== 运行结果 ==========
ThreadA 开始执行
ThreadA 锁住了 lockA
ThreadB 开始执行
ThreadA 锁住了 lockB
ThreadB 锁住了 lockA
ThreadB 锁住了 lockB

3、AB 两条线程交替打印

/*** 题目:* AB两个线程交替打印0-100的数字* <p>* 解题思路:* 基于 synchronized 与 wait()/notifyAll()* 基于 ReentractLock(可重入锁) 和 Condition(条件)* 基于 Semaphore(信号量)*/
public class TH_02_AB两条线程交替打印 {private static int count = 0;private final static Object object = new Object();public static void main(String[] args) {new ThreadA().start();new ThreadB().start();}static class ThreadA extends Thread {@Overridepublic void run() {while (true) {try {synchronized (object) {if (count < 100) {if ((count & 1) == 1) {object.wait();}System.out.println("ThreadA:" + count);count++;object.notifyAll();}}} catch (InterruptedException e) {e.printStackTrace();}}}}static class ThreadB extends Thread {@Overridepublic void run() {while (true) {try {synchronized (object) {if (count < 100) {if ((count & 1) == 0) {object.wait();}System.out.println("ThreadB:" + count);count++;object.notifyAll();}}} catch (InterruptedException e) {e.printStackTrace();}}}}
}

4、生产消费者

  • 基于 synchronized 与 wait()/notifyAll()
/*** 题目:* 写一个程序实现生产者消费者* <p>* 解题思路:* 基于 synchronized 关键字与 Object#wait()/notifyAll()* Object#wait():释放当前锁,使该线程进入等待状态(阻塞状态)* Object#notify():在所有等待线程中随机唤醒一个线程,让它获得锁* Object#notifyAll():唤醒所有等待的线程,让它们一起竞争锁,最后其中之一获得锁* ThreadA 为生产者,ThreadB 为消费者*/
public class TH_03_1_生产消费者模式synchronized {private static int count = 0;private final static int MAX_COUNT = 5;private final static Object object = new Object();public static void main(String[] args) {for (int i = 0; i < 3; i++) {new ThreadA().start();new ThreadB().start();}}static class ThreadA extends Thread {@Overridepublic void run() {while (true) {try {synchronized (object) {while (count == MAX_COUNT) {System.out.println("数据已满");object.wait();}count++;System.out.println("生产者产生了一个数据,当前总数:" + count);object.notifyAll();}Thread.sleep(new Random().nextInt(1000));} catch (InterruptedException e) {e.printStackTrace();}}}}static class ThreadB extends Thread {@Overridepublic void run() {while (true) {try {synchronized (object) {while (count <= 0) {System.out.println("数据为空");object.wait();}count--;System.out.println("消费者消费了一个数据,当前总数:" + count);object.notifyAll();}Thread.sleep(new Random().nextInt(1000));} catch (InterruptedException e) {e.printStackTrace();}}}}
}
  • 基于 ReentractLock(可重入锁) 和 Condition(条件)
/*** 题目:* 写一个程序实现生产者消费者* <p>* 解题思路:* 基于 ReentractLock(可重入锁) 和 Condition(条件)* Lock#lock():加锁* Lock#unlock():释放锁* Condition#await():释放当前锁,使该线程进入等待状态(阻塞状态)* Condition#signal():在所有等待线程中随机唤醒一个线程,让它获得锁* Condition#signalAll():唤醒所有等待的线程,让它们一起竞争锁,最后其中之一获得锁* ThreadA 为生产者,ThreadB 为消费者*/
public class TH_03_2_生产消费者模式ReentractLock {private static int count = 0;private final static int MAX_COUNT = 5;private final static Lock lock = new ReentrantLock();private final static Condition fullCondition = lock.newCondition();private final static Condition emptyCondition = lock.newCondition();public static void main(String[] args) {for (int i = 0; i < 3; i++) {new ThreadA().start();new ThreadB().start();}}static class ThreadA extends Thread {@Overridepublic void run() {while (true) {try {lock.lock();while (count == MAX_COUNT) {System.out.println("数据已满");fullCondition.await();}count++;System.out.println("生产者产生了一个数据,当前总数:" + count);// 唤醒所有消费者emptyCondition.signalAll();Thread.sleep(new Random().nextInt(1000));} catch (InterruptedException e) {e.printStackTrace();} finally {// 最后要注意手动释放锁lock.unlock();}}}}static class ThreadB extends Thread {@Overridepublic void run() {while (true) {try {lock.lock();while (count < 1) {System.out.println("数据为空");emptyCondition.await();}count--;System.out.println("消费者消费了一个数据,当前总数:" + count);// 唤醒所有生产者fullCondition.signalAll();Thread.sleep(new Random().nextInt(1000));} catch (InterruptedException e) {e.printStackTrace();} finally {// 最后要注意手动释放锁lock.unlock();}}}}
}
  • 基于 Semaphore(信号量)
/*** 题目:* 写一个程序实现生产者消费者* <p>* 解题思路:* 基于 Semaphore(信号量)* new Semaphore(1):互斥的信号量* Semaphore#acquire():获取一个许可,如果没有就等待* Semaphore#release():释放一个许可* ThreadA 为生产者,ThreadB 为消费者*/
public class TH_03_3_生产消费者模式Semaphore {private static int count = 0;private static final int MAX_COUNT = 5;// 实现数据消耗互斥的信号量private static final Semaphore semaphore = new Semaphore(1);// 控制生产者的信号量private static final Semaphore fullSemaphore = new Semaphore(MAX_COUNT);// 控制消费者的信号量private static final Semaphore emptySemaphore = new Semaphore(0);public static void main(String[] args) {for (int i = 0; i < 3; i++) {new ThreadA().start();new ThreadB().start();}}static class ThreadA extends Thread {@Overridepublic void run() {while (true) {try {// 请求一个生产者信号量,如果当前信号量为 MAX_COUNT 时,则阻塞fullSemaphore.acquire();semaphore.acquire();count++;System.out.println("生产者产生了一个数据,当前总数:" + count);semaphore.release();// 设置消费者可以消费的信号量个数 +1emptySemaphore.release();Thread.sleep(new Random().nextInt(1000));} catch (InterruptedException e) {e.printStackTrace();}}}}static class ThreadB extends Thread {@Overridepublic void run() {while (true) {try {// 请求一个消费者信号量,如果当前信号量为 0 时,则阻塞emptySemaphore.acquire();semaphore.acquire();count--;System.out.println("消费者消费了一个数据,当前总数:" + count);semaphore.release();fullSemaphore.release();Thread.sleep(new Random().nextInt(1000));} catch (InterruptedException e) {e.printStackTrace();}}}}
}

5、让多线程按照自己指定的顺序执行

  • 基于 Thread.join()
/*** 题目:* 让多线程按照自己指定的顺序执行* <p>* 解题思路:* 基于 Thread.join():是 Theard 的方法,作用是调用线程需等待该 join() 线程执行完成后,才能继续用下运行。* 基于 synchronized 与 wait()/notifyAll()* 基于 ReentractLock(可重入锁) 和 Condition(条件)* 基于 Semaphore(信号量)*/
public class TH_04_1_多线程按指定顺序执行join {public static void main(String[] args) {Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("线程1执行了...");}});Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {try {thread1.join();System.out.println("线程2执行了...");} catch (InterruptedException e) {e.printStackTrace();}}});Thread thread3 = new Thread(new Runnable() {@Overridepublic void run() {try {thread2.join();System.out.println("线程3执行了...");} catch (InterruptedException e) {e.printStackTrace();}}});// 线程随机打乱开启都能保证先1后2再3thread2.start();thread3.start();thread1.start();}
}
  • 基于 synchronized 与 wait()/notifyAll()
/*** 题目:* 让多线程按照自己指定的顺序执行* <p>* 解题思路:* 基于 Thread.join():是 Theard 的方法,作用是调用线程需等待该 join() 线程执行完成后,才能继续用下运行。* 基于 synchronized 与 wait()/notifyAll()* 基于 ReentractLock(可重入锁) 和 Condition(条件)* 基于 Semaphore(信号量)*/
public class TH_04_2_多线程按指定顺序执行synchronized {private static Object object1 = new Object();private static Object object2 = new Object();private static Boolean threadRun1 = false;private static Boolean threadRun2 = false;public static void main(String[] args) {final Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (object1) {System.out.println("线程1执行了...");threadRun1 = true;object1.notify();}}});final Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (object1) {try {if (!threadRun1) object1.wait();synchronized (object2) {System.out.println("线程2执行了...");object2.notify();}} catch (InterruptedException e) {e.printStackTrace();}}}});Thread thread3 = new Thread(new Runnable() {@Overridepublic void run() {synchronized (object2) {try {if (!threadRun2) object2.wait();System.out.println("线程3执行了...");} catch (InterruptedException e) {e.printStackTrace();}}}});// 线程随机打乱开启都能保证先1后2再3thread3.start();thread2.start();thread1.start();}
}
  • 基于 ReentractLock(可重入锁) 和 Condition(条件)
/*** 题目:* 让多线程按照自己指定的顺序执行* <p>* 解题思路:* 基于 Thread.join():是 Theard 的方法,作用是调用线程需等待该 join() 线程执行完成后,才能继续用下运行。* 基于 synchronized 与 wait()/notifyAll()* 基于 ReentractLock(可重入锁) 和 Condition(条件)* 基于 Semaphore(信号量)*/
public class TH_04_3_多线程按指定顺序执行ReentrantLock {private static Lock lock = new ReentrantLock();private static Condition condition1 = lock.newCondition();private static Condition condition2 = lock.newCondition();private static Boolean threadRun1 = false;private static Boolean threadRun2 = false;public static void main(String[] args) throws InterruptedException {final Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {lock.lock();System.out.println("线程1执行了...");threadRun1 = true;condition1.signalAll();lock.unlock();}});final Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {lock.lock();try {if (!threadRun1) condition1.await();System.out.println("线程2执行了...");threadRun2 = true;condition2.signalAll();} catch (InterruptedException e) {e.printStackTrace();}lock.unlock();}});Thread thread3 = new Thread(new Runnable() {@Overridepublic void run() {lock.lock();try {if (!threadRun2) condition2.await();System.out.println("线程3执行了...");lock.unlock();} catch (InterruptedException e) {e.printStackTrace();}}});// 线程随机打乱开启都能保证先1后2再3thread3.start();thread2.start();thread1.start();}
}
  • 基于 Semaphore(信号量)
/*** 题目:* 让多线程按照自己指定的顺序执行* <p>* 解题思路:* 基于 Thread.join():是 Theard 的方法,作用是调用线程需等待该 join() 线程执行完成后,才能继续用下运行。* 基于 synchronized 与 wait()/notifyAll()* 基于 ReentractLock(可重入锁) 和 Condition(条件)* 基于 Semaphore(信号量)*/
public class TH_04_4_多线程按指定顺序执行Semaphore {private static Semaphore semaphore1 = new Semaphore(1,true);private static Semaphore semaphore2 = new Semaphore(0);private static Semaphore semaphore3 = new Semaphore(0);public static void main(String[] args) {final Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {try {semaphore1.acquire();System.out.println("线程1执行了...");semaphore2.release();} catch (InterruptedException e) {e.printStackTrace();}}});final Thread thread2 = new Thread(new Runnable() {@Overridepublic void run() {try {semaphore2.acquire();System.out.println("线程2执行了...");semaphore3.release();} catch (InterruptedException e) {e.printStackTrace();}}});final Thread thread3 = new Thread(new Runnable() {@Overridepublic void run() {try {semaphore3.acquire();System.out.println("线程3执行了...");semaphore1.release();} catch (InterruptedException e) {e.printStackTrace();}}});// 线程随机打乱开启都能保证先1后2再3thread3.start();thread2.start();thread1.start();}
}

6、参考文献

  • https://developer.aliyun.com/article/52846#slide-4
  • https://www.liaoxuefeng.com/wiki/1252599548343744/1306580960149538
  • https://fangjian0423.github.io/2016/04/18/java-synchronize-way/
  • https://zhuanlan.zhihu.com/p/80787379

Java 实现同步的几种方式相关推荐

  1. Java线程同步的几种方式

    Java线程同步的几种方式 1.使用synchronized关键字  它的工作是对同步的代码加锁,使得每一次只能有一个线程进入同步块,从而保证线程间的安全性.  synchronized关键字的用法: ...

  2. java实现同步的两种方式

    同步是多线程中的重要概念.同步的使用可以保证在多线程运行的环境中,程序不会产生设计之外的错误结果.同步的实现方式有两种,同步方法和同步块,这两种方式都要用到synchronized关键字. 给一个方法 ...

  3. Java字符串连接的几种方式

    Java字符串连接的几种方式 字符串表现的几种方式 StringBuffer和StringBuilder及String的继承关系 字符串的连接 1.String的连接方法 可以看出连接方式是新建了一个 ...

  4. java多线程同步的四种方法_java中实现多线程的两种方法

    java多线程有几种实现方法,都是什么?同步有几种实java中多线程的实现方法有两种:1.直接继承thread类:2.实现runnable接口:同步的实现方法有五种:1.同步方法:2.同步代码块:3. ...

  5. 最优雅的Java字符串拼接是哪种方式?

    title shortTitle category tag description head 最优雅的Java字符串拼接是哪种方式? Java字符串拼接 Java核心 数组&字符串 Java程 ...

  6. java怎样输入五个数字打一成语,Java的线程安全四种方式五个等级[1]

    Java的线程安全四种方式五个等级[1]以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 四种方式 sychronized ...

  7. 12月18日云栖精选夜读 | Java 中创建对象的 5 种方式!...

    作为Java开发者,我们每天创建很多对象,但我们通常使用依赖管理系统,比如Spring去创建对象.然而这里有很多创建对象的方法,我们会在这篇文章中学到. Java中有5种创建对象的方式,下面给出它们的 ...

  8. Java中创建对象的几种方式

    Java中创建对象的几种方式 1.使用new创建对象,在堆上创建. 2.克隆 3.反序列化 4.反射创建对象 5.NIO中可以使用本地方法直接分配堆外内存. 转载于:https://www.cnblo ...

  9. Java中创建对象的四种方式

    为什么80%的码农都做不了架构师?>>>    Java中创建对象的四种方式 (1) 用new语句创建对象,这是最常见的创建对象的方法.    (2) 运用反射手段,调用java.l ...

  10. Linux C++多线程同步的四种方式

    目录 一.互斥锁 二.条件变量 三.读写锁 原文链接:Linux C++多线程同步的四种方式(非常详细)_Y先森0.0-CSDN博客 背景问题:在特定的应用场景下,多线程不进行同步会造成什么问题? 通 ...

最新文章

  1. Intent 的Flag属性(Activity在栈位置的主宰者)
  2. 高校选课成绩管理系统
  3. MATLAB画高斯曲线
  4. Jinja2 模板用法
  5. Windows环境安装运行:Angular.js
  6. c++primer 3.4练习题
  7. python学习笔记之random模块
  8. spark学习-20-Spark的sample理解
  9. hdu 5437 Alisha’s Party 优先队列
  10. list scala 当前位置 遍历_Scala学习七之集合了解
  11. 计算机组成原理补充实验,计算机组成原理补充实验.doc
  12. VS2005制作安装包
  13. IIS允许下载APK安装包
  14. iOS解决“The ‘Pods-XXX‘ target has transitive dependencies that include statically linked binaries”报错
  15. Android统一推送联盟成立
  16. 雷电三接口有什么用_三坐标中的矢量是干什么用的?
  17. jspdf添加宋体_JSPDF支持中文(思源黑体)采坑之旅,JSPDF中文字体乱码解决方案...
  18. 《一键下单:杰夫·贝佐斯与亚马逊的崛起》—— 读后总结
  19. python生成指定长度的列表_python怎样创建具有一定长度和初始值的列表
  20. sequoia中的日志

热门文章

  1. Linux课程设计:Linux系统下多进程的创建与通信
  2. OGNL表达式【mybatis】
  3. python花瓣飘零_Python 爬虫: 抓取花瓣网图片
  4. java实现房屋出租系统
  5. 1月16日英语计算机统考,高考英语听力首次机考16日开考
  6. Apache JMeter使用教程
  7. CSS(刷漆)学习总结
  8. CBoard 0.4.2环境搭建
  9. 程序员常见面试题汇总
  10. 模式识别工具箱安装及使用