尚硅谷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高并发编程学习笔记线程通信与集合线程安全的主要内容,如果未能解决你的问题,请参考以下文章

尚硅谷JUC高并发编程学习笔记线程通信与集合线程安全

尚硅谷JUC高并发编程学习笔记JUC简介与Lock接口

尚硅谷JUC高并发编程学习笔记JUC简介与Lock接口

尚硅谷JUC高并发编程学习笔记多线程锁

尚硅谷JUC高并发编程学习笔记多线程锁

尚硅谷JUC高并发编程学习笔记多线程锁