浅谈多线程下if和while循环不同的原因

今天跟着狂神的视频学习了一下多线程,发现自己以前还是太想当然了。 

借着工作之余有学习的机会,我把我的一些心得和想法  写出来。 不对的地方 还望大家指正。

package JUC.demo03;

public class D {

    public static void main(String[] args) {

        Data4 data = new Data4();

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

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

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

    }
}

//
class Data4{
    private  int number = 0;

   
    public synchronized  void increment() throws InterruptedException {
        if(number != 0){
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>加法"+number);
        this.notifyAll();
    }


    public synchronized  void decrement() throws InterruptedException {
        while(number == 0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>减法"+number);
        this.notifyAll();
    }





}

 输出结果:

  

A=>加法1
B=>减法0
A=>加法1
B=>减法0
C=>加法1
A=>加法2
C=>加法3
D=>减法2
D=>减法1
D=>减法0
B=>减法-1
B=>减法-2
B=>减法-3
B=>减法-4
B=>减法-5
B=>减法-6
B=>减法-7
B=>减法-8
D=>减法-9
D=>减法-10
D=>减法-11
D=>减法-12
D=>减法-13
D=>减法-14
D=>减法-15
C=>加法-14
A=>加法-13
C=>加法-12
A=>加法-11
C=>加法-10
A=>加法-9
C=>加法-8
A=>加法-7
C=>加法-6
A=>加法-5
C=>加法-4
A=>加法-3
C=>加法-2
A=>加法-1
C=>加法0

 

while 循环判断代码:

package JUC.demo03;

public class D {

    public static void main(String[] args) {

        Data4 data = new Data4();

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

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

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

    }
}

//
class Data4{
    private  int number = 0;


    public synchronized  void increment() throws InterruptedException {
        while(number != 0){
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>加法"+number);
        this.notifyAll();
    }


    public synchronized  void decrement() throws InterruptedException {
        while(number == 0){
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>减法"+number);
        this.notifyAll();
    }





}

输出结果:

A=>加法1
B=>减法0
A=>加法1
B=>减法0
A=>加法1
B=>减法0
A=>加法1
B=>减法0
A=>加法1
B=>减法0
A=>加法1
B=>减法0
A=>加法1
B=>减法0
A=>加法1
B=>减法0
A=>加法1
B=>减法0
A=>加法1
B=>减法0
C=>加法1
D=>减法0
C=>加法1
D=>减法0
C=>加法1
D=>减法0
C=>加法1
D=>减法0
C=>加法1
D=>减法0
C=>加法1
D=>减法0
C=>加法1
D=>减法0
C=>加法1
D=>减法0
C=>加法1
D=>减法0
C=>加法1
D=>减法0

我相信 对多线程了解不深的人  肯定也跟我一样有疑问。 为什么结果偏差会这么大。

 

首先明确一些基本概念:

1.  注意:wait会释放锁。这个时候其他线程就会来抢这把锁。

2.  同时需要注意的一点是 synchronized 声明锁 ,锁的是这个Data4 对象,而并不是里面的方法体。 这一点的理解尤为重要。也就是说 在main方法里 我实例化了一次Data4 。 就只会有一把锁 是用来同步data 这个对象的。  如果实例化两次 就会有两把锁。

搞清楚这些点,下面进入正题

比如 当A线程进入了if判断语句内 A线程 wait。 B线程去抢到了这把锁, 并且number--,然后 notifyAll(), 这个时候就会告诉所有的线程, 锁我释放了!你们都来抢,包括B线程它自己也可以继续去抢这把锁。   但是由于是多线程,很有可能在 A wait的同时 ,C也进入了判断 C线程也在wait。 

1.if判断的时候 -> 当B释放完锁以后,由于if只会判断一次。  A抢到了这把锁, 执行了后面的number++, 之后C抢到了这把锁,但是由于if只会判断一次, C并没有经过判断, C也number++ 。 就会导致number多次连续++的情况。

2.while判断的时候  -> 当B释放完锁以后,A抢到了锁,while会判断 number满不满足条件,当满足条件 A执行, number++,  之后notifyAll ,假设C会是下一个抢到锁的线程, while判断 这个时候number已经不满足条件了! 所以C会继续等待wait。然后其他线程继续抢! 如此反复。

重要的一点是 弄清楚notifyAll 之后 所有线程都可以去抢这把锁。 而且同时有且仅有一个线程可以拿到锁,没有拿到锁的就靠边站!等待下一次释放锁的时刻。并且一个实例化的对象  锁只有一把。

posted @ 2021-11-11 16:15  斯拉克  阅读(365)  评论(0编辑  收藏  举报