Java多线程之线程虚假唤醒

Wesley13
• 阅读 384
本文目录提纲
问题:两个线程对一个初始值为零的变量操作,实现一个线程加一,另一个线程减一,来十次。
问题:四个线程对一个初始值为零的变量操作,实现两个线程加一,另外两个线程减一,来十次。
1. 两个线程对一个初始值为零的变量操作,实现一个线程加一,另一个线程减一,来十次。

代码实现:

class ShareData{
    private int number = 0;

    public synchronized void increment() throws InterruptedException {
//        判断
        if (number!=0){
            this.wait();
        }
//        干活
        ++number;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
//        通知唤醒
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        if (number==0){
            this.wait();
        }
        --number;
        System.out.println(Thread.currentThread().getName()+"\t"+number);
        this.notifyAll();
    }
}

public class ThreadDemo2 {
    public static void main(String[] args) {
     ShareData sd = new ShareData();

     new Thread(()->{
         for (int i = 1; i <=10 ; i++) {
             try {
                 sd.increment();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }  
     },"A").start();

        new Thread(()->{
            for (int i = 1; i <=10 ; i++) {
                try {
                    sd.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}

编译结果:

Java多线程之线程虚假唤醒
2. 问题:四个线程对一个初始值为零的变量操作,实现两个线程加一,另外两个线程减一,来十次。
于是把上面的代码,线程弄多两个,运行发现出现问题。编译结果如下:

Java多线程之线程虚假唤醒
通过阅读文档,发现:

As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:

synchronized (obj) {
     while (<condition does not hold>)
         obj.wait();
     ... // Perform action appropriate to condition
 }

即中断和伪唤醒是可能的,并且这个方法应该在while循环中使用
画图:

Java多线程之线程虚假唤醒
将上面代码if换成while再执行,发现没有问题,代码如下:

class ShareData {
    private int number = 0;

    public synchronized void increment() throws InterruptedException {
//        判断
        while (number != 0) {
            this.wait();
        }
//        干活
        ++number;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
//        通知唤醒
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        while (number == 0) {
            this.wait();
        }
        --number;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        this.notifyAll();
    }
}

public class ThreadDemo2 {
    public static void main(String[] args) {
        ShareData sd = new ShareData();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(200);
                    sd.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(300);     //模拟执行其他代码
                    sd.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(400);
                    sd.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(500);
                    sd.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

编译结果:

Java多线程之线程虚假唤醒
使用Lock优化
前置知识:

Condition factors out the Object monitor methods (wait, notify and notifyAll) into distinct objects to give the effect of having multiple wait-sets per object, by combining them with the use of arbitrary Lock implementations. Where a Lock replaces the use ofsynchronized methods and statements, a Condition replaces the use of the Object monitor methods.

优化代码:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class ShareData {
    private int number = 0;
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public synchronized void increment() throws InterruptedException{
        lock.lock();
        try{
            while (number != 0) {
//                this.wait();
                condition.await();
            }
//        干活
            ++number;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
//        通知唤醒
//            this.notifyAll();
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public synchronized void decrement() throws InterruptedException{
        lock.lock();
        try{
            while (number == 0) {
//                this.wait();
                condition.await();
            }
//        干活
            --number;
            System.out.println(Thread.currentThread().getName() + "\t" + number);
//        通知唤醒
//            this.notifyAll();
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

public class ThreadDemo2 {
    public static void main(String[] args) {
        ShareData sd = new ShareData();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(200);
                    sd.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(300);
                    sd.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(400);
                    sd.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();

        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                try {
                    Thread.sleep(500);
                    sd.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}
点赞
收藏
评论区
推荐文章
待兔 待兔
2年前
ThreadLocal源码分析
最近在学多线程并发的知识,发现好像ThreadLoca还挺重要,决定看看源码以及查找各方资料来学习一下。ThreadLocal能够提供线程的局部变量,让每个线程都可以通过set/get来对这个局部变量进行操作,不会和其它线程的局部变量进行冲突,实现了线程的数据隔离。首先是ThreadLocal的结构:每个Thread维护一个ThreadLocalMap,这个
Wesley13 Wesley13
2年前
java并发编程之二
CountDownLatch类  允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。  CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成了任务,然后在
Wesley13 Wesley13
2年前
java多线程的常见例子
一.相关知识:Java多线程程序设计到的知识:(一)对同一个数量进行操作(二)对同一个对象进行操作(三)回调方法使用(四)线程同步,死锁问题(五)线程通信 等等二.示例一:三个售票窗口同时出售20张票;程序分析:1.票数要使用同一个静态值 2.为保证不会出现卖出同一个票数,要java多线程同步锁。设计思路:1.创建
Wesley13 Wesley13
2年前
java 面试知识点笔记(十)多线程与并发
问:线程安全问题的主要诱因?1.存在共享数据(也称临界资源)2.存在多条线程共同操作这些共享数据解决方法:同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再对共享数据进行操作互斥锁的特征:1.互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程协调机制,这样在同一时间只有一
Wesley13 Wesley13
2年前
Java多线程与并发之ThreadLocal
1\.ThreadLocal是什么?使用场景ThreadLocal简介ThreadLocal是线程本地变量,可以为多线程的并发问题提供一种解决方式,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,
Wesley13 Wesley13
2年前
Java并发编程:多线程如何实现阻塞与唤醒
线程的阻塞和唤醒在多线程并发过程中是一个关键点,当线程数量达到很大的数量级时,并发可能带来很多隐蔽的问题。如何正确暂停一个线程,暂停后又如何在一个要求的时间点恢复,这些都需要仔细考虑的细节。Java为我们提供了多种API来对线程进行阻塞和唤醒操作,比如suspend与resume、sleep、wait与notify以及park与unpark等等。!(
Wesley13 Wesley13
2年前
Java中的多线程你只要看这一篇就够了
<divid"cnblogs\_post\_body"class"blogpostbody"<h2引</h2<p如果对什么是线程、什么是进程仍存有疑惑,请先Google之,因为这两个概念不在本文的范围之内。</p<p用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,
Wesley13 Wesley13
2年前
Java 并发编程:多线程如何实现阻塞与唤醒
线程的阻塞和唤醒在多线程并发过程中是一个关键点,当线程数量达到很大的数量级时,并发可能带来很多隐蔽的问题。如何正确暂停一个线程,暂停后又如何在一个要求的时间点恢复,这些都需要仔细考虑的细节。Java为我们提供了多种API来对线程进行阻塞和唤醒操作,比如suspend与resume、sleep、wait与notify以及park与unpark等等。!(
Wesley13 Wesley13
2年前
Java线程与多线程
1线程与多线程1.1线程是什么?线程(Thread)是一个对象(Object)。用来干什么?Java线程(也称JVM线程)是Java进程内允许多个同时进行的任务。该进程内并发的任务成为线程(Thread),一个进程里至少一个线程。Java程序采用多线程方式来支持大量的并发请求处理,程序如果在
Wesley13 Wesley13
2年前
Java多线程消费者、生产者的基本思路
多线程主要考察的就是线程的同步控制  生产者消费者的思路就是,当一个线程执行时让另一个线程挂起就行了ThreadOne、ThreadTwo同时运行,添加一个变量在一个公共类(下边的Function类)中,例如:当变量为true,ThreadOne执行ThreadTwo挂起;当变量为false,ThreadOne挂起ThreadTw