IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> JUC学习(三):synchronized和Lock实现线程间通信(包含虚假唤醒的讲解) -> 正文阅读

[Java知识库]JUC学习(三):synchronized和Lock实现线程间通信(包含虚假唤醒的讲解)

????????线程间通信的模型有两种:共享内存和消息传递,以下方式都是基本这两种模型来实现的。我们来基本一道面试常见的题目来分析 :

????????场景---两个线程,一个线程对当前数值加 1,另一个线程对当前数值减 1,要求用线程间通信

?

一、synchronized实现?

/**
 * 实现线程A对一个值+1,线程B对该值-1
 */

//第一步:创建资源类,定义属性和操作方法
class Share{
    //目标值
    int number = 0;

    //+1操作
    public synchronized void incr() throws InterruptedException {
        //第二步:判断->操作->通知
        //判断
        if (number != 0){
            this.wait();
        }
        //操作
        number++;
        System.out.println(Thread.currentThread().getName()+":"+number);
        //通知其他线程
        this.notifyAll();
    }

    //-1操作
    public synchronized void decr() throws InterruptedException {
        if (number != 1){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+":"+number);
        this.notifyAll();
    }
}

public class ThreadDemo01 {

    //第二步,创建多个线程,调用资源类中的操作方法
    public static void main(String[] args) {

        Share share = new Share();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程B").start();
    }
}

?

二、虚假唤醒问题?

? ? ? ? 现在我们将进程变为四个,A、C进程负责加,B、D进程负责减??

//第一步:创建资源类,定义属性和操作方法
class Share{
    //目标值
    int number = 0;

    //+1操作
    public synchronized void incr() throws InterruptedException {
        //第二步:判断->操作->通知
        //判断
        if (number != 0){
            this.wait();
        }
        //操作
        number++;
        System.out.println(Thread.currentThread().getName()+":"+number);
        //通知其他线程
        this.notifyAll();
    }

    //-1操作
    public synchronized void decr() throws InterruptedException {
        if (number != 1){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+":"+number);
        this.notifyAll();
    }
}

public class ThreadDemo01 {

    //第二步,创建多个线程,调用资源类中的操作方法
    public static void main(String[] args) {

        Share share = new Share();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程B").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程C").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程D").start();
    }
}

?

?????????????????????????????????????????????????????????????????????????????

????????我们可以看到,结果并不是严格的一加一减,而是出现了2、3这样的数字,这是为什么呢?

? ? ? ? 这就是要讲的虚假唤醒的问题。

? ? ? ? 查阅jdk1.8版本的API,可以看到wait方法中有这样一段话:

? ? ? ? ?意思是,我们代码中的wait应该放在循环中来避免虚假唤醒,不应该写成:

if (number != 0){
    this.wait();
}

? ? ? ? 而应该写成:

while (number != 0){
    this.wait();
}

? ? ? ? 为什么会导致这样呢?什么是虚假唤醒呢?

? ? ? ? 简而言之,就是wait()方法在唤醒之后,会直接在之前等待的地方继续执行,而不会再执行前面的判断了,这就叫做虚假唤醒。所以要放在循环中,让他唤醒后重新去做判断,避免虚假唤醒的问题。

? ? ? ? 所以Share类中正确的代码应是:

class Share{
    //目标值
    int number = 0;

    //+1操作
    public synchronized void incr() throws InterruptedException {
        //第二步:判断->操作->通知
        //判断
        while (number != 0){
            this.wait();
        }
        //操作
        number++;
        System.out.println(Thread.currentThread().getName()+":"+number);
        //通知其他线程
        this.notifyAll();
    }

    //-1操作
    public synchronized void decr() throws InterruptedException {
        while (number != 1){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+":"+number);
        this.notifyAll();
    }
}

? ? ? ? 运行结果:

线程A:1
线程B:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程D:0
线程C:1
线程B:0
线程C:1
线程D:0
线程A:1
线程D:0
线程C:1
线程B:0
线程C:1
线程D:0
线程A:1
线程D:0
线程C:1
线程B:0
线程C:1
线程D:0
线程A:1
线程D:0
线程C:1
线程B:0
线程C:1
线程D:0
线程A:1
线程D:0
线程C:1
线程B:0
线程A:1
线程B:0
线程A:1
线程B:0
线程A:1
线程B:0

Process finished with exit code 0

?

?

三、Lock实现四线程操作

/**
 * Lock实现:线程A、C将number值从0变为1,线程B、D将number值从1变为0
 */

class Share{
    private int number = 0;

    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    //+1操作
    public void incr() throws InterruptedException {

        lock.lock();

        try{
            while(number != 0){
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+":"+number);
            condition.signalAll();
        }finally {
            lock.unlock();
        }
    }

    //-1操作
    public void decr() throws InterruptedException {

        lock.lock();

        try {
            while(number != 1){
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+":"+number);
            condition.signalAll();

        } finally {
            lock.unlock();
        }
    }
}

public class ThreadDemo02 {

    public static void main(String[] args) {

        Share share = new Share();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程B").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程C").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "线程D").start();
    }
}

?

? ? ? ? 运行结果:

线程A:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程A:1
线程B:0
线程C:1
线程D:0
线程C:1
线程B:0

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 20:35:37  更:2022-03-21 20:37:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:00:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码