今天跟着狂神的视频学习了一下多线程,发现自己以前还是太想当然了。
借着工作之余有学习的机会,我把我的一些心得和想法 写出来。 不对的地方 还望大家指正。
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 之后 所有线程都可以去抢这把锁。 而且同时有且仅有一个线程可以拿到锁,没有拿到锁的就靠边站!等待下一次释放锁的时刻。并且一个实例化的对象 锁只有一把。