尚硅谷JUC高并发编程学习笔记线程通信与集合线程安全
Posted exodus3
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了尚硅谷JUC高并发编程学习笔记线程通信与集合线程安全相关的知识,希望对你有一定的参考价值。
一、线程间通信
线程间通信的模型有两种:共享内存和消息传递
线程间的通信具体步骤:(涉及上中下部)
1、创建资源类,在资源类中船舰属性和操作方法
2、在资源类操作方法:判断、操作、通知
3、创建多个线程,调用资源类的操作方法
4、防止虚拟唤醒问题
1、synchronized案例
操作线程的时候,等待线程使用wait()
通知另外的线程操作用notify()、notifyAll()
假设有两个线程,该线程在执行过程中,判断值(不是该值等待,让其他线程抢),操作值,通知另外一个线程的调度。
通过使用两个线程对0这个值操作,一个线程加1,一个线程减1,交替实现多次
//第一步 创建资源类,定义属性和操作方法
class Share
//初始值
private int number = 0;
//+1的方法
public synchronized void incr() throws InterruptedException
//第二步 判断 干活 通知
if(number != 0) //判断number值是否是0,如果不是0,等待
this.wait(); //在哪里睡,就在哪里醒
//如果number值是0,就+1操作
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 ThreadDemo1
//第三步 创建多个线程,调用资源类的操作方法
public static void main(String[] args)
Share share = new Share();
//创建线程
new Thread(()->
for (int i = 1; i <=10; i++)
try
share.incr(); //+1
catch (InterruptedException e)
e.printStackTrace();
,"AA").start();
new Thread(()->
for (int i = 1; i <=10; i++)
try
share.decr(); //-1
catch (InterruptedException e)
e.printStackTrace();
,"BB").start();
效果
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
Process finished with exit code 0
如果使用多个线程,添加额外两个线程,且操作要依次执行
new Thread(()->
for (int i = 1; i <=10; i++)
try
share.incr(); //+1
catch (InterruptedException e)
e.printStackTrace();
,"CC").start();
new Thread(()->
for (int i = 1; i <=10; i++)
try
share.decr(); //-1
catch (InterruptedException e)
e.printStackTrace();
,"DD").start();
就会出现这样的情况
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
CC :: 1
AA :: 2
CC :: 3
BB :: 2
CC :: 3
AA :: 4
CC :: 5
DD :: 4
BB :: 3
DD :: 2
CC :: 3
AA :: 4
CC :: 5
DD :: 4
BB :: 3
DD :: 2
CC :: 3
AA :: 4
CC :: 5
DD :: 4
BB :: 3
DD :: 2
CC :: 3
AA :: 4
CC :: 5
DD :: 4
BB :: 3
DD :: 2
AA :: 3
DD :: 2
BB :: 1
DD :: 0
Process finished with exit code 0
主要是虚拟唤醒导致:如果一个线程执行完毕后,通知其他线程,该线程又进入等待睡眠,可能会因为某些原因被唤醒后,if
结构的语句就不会判断了,一直往下执行,所以需要将if
换成while
结构,每次都判断。因为wait
在哪里睡眠就在哪里被唤醒,结果被某个异常唤醒了后回不去了,if
结构不会在判断了,需要更改为while
while(number != 0) //判断number值是否是0,如果不是0,等待
this.wait(); //在哪里睡,就在哪里醒
实现中断和虚假唤醒是可能的,需要将其while方法用在循环中。
最后的结果:
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
AA :: 1
BB :: 0
CC :: 1
BB :: 0
AA :: 1
BB :: 0
CC :: 1
BB :: 0
AA :: 1
DD :: 0
CC :: 1
BB :: 0
AA :: 1
DD :: 0
CC :: 1
BB :: 0
AA :: 1
DD :: 0
CC :: 1
DD :: 0
AA :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
CC :: 1
DD :: 0
Process finished with exit code 0
2、Lock案例
使用lock先要创建锁的对象以及通知的对象
放置在资源类中
//创建Lock
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
上锁lock.lock();
解锁lock.unlock();
以下都为condition
类:
唤醒所有等待的线程signalAll()
,带上类名condition.signalAll()
;
唤醒一个等待线程signal()
,带上类名,condition.signal()
;
造成当前线程在接到信号或者被中断之前一直处于等待状态await()
,带上类名,condition.await()
;
同样是上面的案例题目换成lock
//第一步 创建资源类,定义属性和操作方法
class Share
private int number = 0;
//创建Lock
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 ThreadDemo2
public static void main(String[] args)
Share share = new Share();
new Thread(()->
for (int i = 1; i <=10; i++)
try
share.incr();
catch (InterruptedException e)
e.printStackTrace();
,"AA").start();
new Thread(()->
for (int i = 1; i <=10; i++)
try
share.decr();
catch (InterruptedException e)
e.printStackTrace();
,"BB").start();
new Thread(()->
for (int i = 1; i <=10; i++)
try
share.incr();
catch (InterruptedException e)
e.printStackTrace();
,"CC").start();
new Thread(()->
for (int i = 1; i <=10; i++)
try
share.decr();
catch (InterruptedException e)
e.printStackTrace();
,"DD").start();
实现的结果是一样的。
二、线程间定制化通信
所谓定制化通信,需要让线程进行一定的顺序操作
案列:启动三个线程,按照如下要求:
AA打印5此,BB打印10次,CC打印15次,一共进行10轮
具体思路:
每个线程添加一个标志位,是该标志位则执行操作,并且修改为下一个标志位,通知下一个标志位的线程
代码
//第一步 创建资源类
class ShareResource
//定义标志位
private int flag = 1; // 1 AA 2 BB 3 CC
//创建Lock锁
private Lock lock = new ReentrantLock();
//创建三个condition
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
//打印5次,参数第几轮
public void print5(int loop) throws InterruptedException
//上锁
lock.lock();
try
//判断
while(flag != 1)
//等待
c1.await();
//干活
for (int i = 1; i <=5; i++)
System.out.println(Thread.currentThread().getName()+" :: "+i+" :轮数:"+loop);
//通知
flag = 2; //修改标志位 2
c2.signal(); //通知BB线程
finally
//释放锁
lock.unlock();
//打印10次,参数第几轮
public void print10(int loop) throws InterruptedException
lock.lock();
try
while(flag != 2)
c2.await();
for (int i = 1; i <=10; i++)
System.out.println(Thread.currentThread().getName()+" :: "+i+" :轮数:"+loop);
//修改标志位
flag = 3;
//通知CC线程
c3.signal();
finally
lock.unlock();
//打印15次,参数第几轮
public void print15(int loop) throws InterruptedException
lock.lock();
try
while(flag != 3)
c3.await();
for (int i = 1; i <=15; i++)
System.out.println(Thread.currentThread().getName()+" :: "+i+" :轮数:"+loop);
//修改标志位
flag = 1;
//通知AA线程
c1.signal();
finally
lock.unlock();
public class ThreadDemo3
public static void main(String[] args)
ShareResource shareResource = new ShareResource();
new Thread(()->
for (int i = 1; i <=10; i++)
try
shareResource.print5(i);
catch (InterruptedException e)
e.printStackTrace();
,"AA").start();
new Thread(()->
for (int i = 1; i <=10; i++)
try
shareResource.print10(i);
catch (InterruptedException e)
e.printStackTrace();
,"BB").start();
new Thread(()->
for (int i = 1; i <=10; i&以上是关于尚硅谷JUC高并发编程学习笔记线程通信与集合线程安全的主要内容,如果未能解决你的问题,请参考以下文章