zoukankan      html  css  js  c++  java
  • 浅谈多线程下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 之后 所有线程都可以去抢这把锁。 而且同时有且仅有一个线程可以拿到锁,没有拿到锁的就靠边站!等待下一次释放锁的时刻。并且一个实例化的对象  锁只有一把。

  • 相关阅读:
    cache buffers chains latch
    freemarker自定义标签报错(七)
    freemarker自定义标签(三)-nested指令
    freemarker自定义标签(二)
    Buffer Cache 原理
    JavaScript去除日期中的“-”
    JavaScript替换HTML标签
    JavaScript获取地址栏中的参数
    JavaScript中的indexOf
    Java中的字符串拼接
  • 原文地址:https://www.cnblogs.com/wuhaojs/p/15540440.html
Copyright © 2011-2022 走看看