【线程锁 Synchronized与ReentrantLock 示例】

萝らか妹 提交于 2020-03-20 00:13:25

前言:

   之前一直对线程锁不理解,去网上找了很多讲解Synchronized关键字和ReentrantLock的帖子,大致了解了用法,写个小程序记录一下。有不足和错误欢迎留言评论,大家一起学习进步。

一、为什么要用线程锁

   Java多线程同时处理任务会显著提高工作效率。但是,如果多个线程同时操作某一资源,可能会导致数据的不同步,引发错误。因此对于某些资源我们要对线程进行控制,同一时间只允许单个线程进行操作。这时我们就需要使用线程锁。

场景:

   书店生产书,直到书总量达到10本,顾客买书,总共要买10本

   书店:书店只要书不足10本就一直生产,直到书店内书个数达到10,停止生产,如果此时有书被顾客买走就继续生产,直至存货10本...

   顾客:只要书店有书就从书店购买,没有书就停止购买,等有书产出就继续,直到购买10本

二、Synchronized关键字实现线程锁

   书店:

public class SynchronizedProviderThread extends Thread {
    public static List<String> books=new ArrayList<String>();
    public static int MAX_BOOK_NUM=10;
    public void run(){
        try{
            while(!Thread.interrupted()){
                synchronized (books){
                    if(books.size()>=10){
                        System.out.println("书已达最大总量:"+books.size());
                        books.wait();
                        System.out.println("已经有书被买走了,可以继续生产了");
                    }else{
                        books.add("BOOK");
                        System.out.println("生产书一本。。。。当前书总量为:"+books.size());
                        books.notifyAll();
                    }
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }


    }
}

  顾客:

public class SynchronizedConsumerThread extends Thread{
    public int sellCount=0;
    public void run(){
        try{
            while(sellCount<10){
                synchronized (SynchronizedProviderThread.books){
                    if(SynchronizedProviderThread.books.size()>0){
                        sellCount++;
                        SynchronizedProviderThread.books.remove(SynchronizedProviderThread.books.get(0));
                        System.out.println("购买书一本。。。当前书总量为:"+ SynchronizedProviderThread.books.size()+"  已购买"+sellCount+"本书");
                        SynchronizedProviderThread.books.notifyAll();
                    }else{
                        System.out.println("书已售空,无法继续购买");
                        SynchronizedProviderThread.books.wait();
                        System.out.println("已经有书了,可以开始购买了"+"   当前书量:"+ SynchronizedProviderThread.books.size());
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }


    }
}

    总线程:

public class SychronizedTestMain {
    public static void main(String args[]){
        SynchronizedProviderThread provider=new SynchronizedProviderThread();
        SynchronizedConsumerThread consumer1=new SynchronizedConsumerThread();
        provider.start();
        consumer1.start();
    }
}

    运行结果:

书已售空,无法继续购买
生产书一本。。。。当前书总量为:1
已经有书了,可以开始购买了   当前书量:1
购买书一本。。。当前书总量为:0  已购买1本书
书已售空,无法继续购买
生产书一本。。。。当前书总量为:1
生产书一本。。。。当前书总量为:2
生产书一本。。。。当前书总量为:3
生产书一本。。。。当前书总量为:4
生产书一本。。。。当前书总量为:5
生产书一本。。。。当前书总量为:6
生产书一本。。。。当前书总量为:7
生产书一本。。。。当前书总量为:8
生产书一本。。。。当前书总量为:9
生产书一本。。。。当前书总量为:10
书已达最大总量:10
已经有书了,可以开始购买了   当前书量:10
购买书一本。。。当前书总量为:9  已购买2本书
购买书一本。。。当前书总量为:8  已购买3本书
购买书一本。。。当前书总量为:7  已购买4本书
购买书一本。。。当前书总量为:6  已购买5本书
购买书一本。。。当前书总量为:5  已购买6本书
购买书一本。。。当前书总量为:4  已购买7本书
购买书一本。。。当前书总量为:3  已购买8本书
购买书一本。。。当前书总量为:2  已购买9本书
购买书一本。。。当前书总量为:1  已购买10本书
已经有书被买走了,可以继续生产了
生产书一本。。。。当前书总量为:2
生产书一本。。。。当前书总量为:3
生产书一本。。。。当前书总量为:4
生产书一本。。。。当前书总量为:5
生产书一本。。。。当前书总量为:6
生产书一本。。。。当前书总量为:7
生产书一本。。。。当前书总量为:8
生产书一本。。。。当前书总量为:9
生产书一本。。。。当前书总量为:10
书已达最大总量:10
View Code

 分析:

   书店、顾客中被synchronized标注的方法同步运行,为books添加了线程锁监控,当多个线程同时要调用books时,要先争夺books的线程锁,由JVM按照一定的选举规则从中选出一个线程获得线程锁,执行synchronized()中的部分,执行完毕后会释放线程锁,其余线程继续争锁......这样一来同一时间只有一个线程调用books。

   books.wait()方法:暂停当前线程,开始等待,并释放线程锁,直到其他线程调用books.notifyAll()或books.notify()时,重新争夺线程锁,如果争到线程锁,就从刚刚books.wait()暂停处继续运行。

   books.notifyAll():释放线程锁,并告知所有正在等待books锁的线程不再等待,开始争夺锁。

   books.notify():释放当前线程锁,并告知一个正在等待books锁的线程不再等待,获取锁,其余等待的线程继续等待,直到再有notify()或notifyAll()被调用。

不足:当有书生产出时,会告知正在等待的线程不再等待,开始争夺锁,这时候可能其他书店线程在争夺books的锁,因此有可能下一个获取到锁的线程又是书店,而不是顾客,所以会导致书店已经连续生产好几本书了,而有的顾客还在等待。同理,顾客买了一本书之后,同样也会唤醒其他等待顾客的线程,而不是第一时间告知书店可以继续生产书了。

可改善:当有书生产时,只告知正在等待的顾客不再等待。当顾客购买书后只告知正在等待的书店不再等待。

ReentrantLock就可以实现这样的效果

三、ReentrantLock实现线程锁

Lock:

public class LockForSell {
    public static ReentrantLock reentrantLock=new ReentrantLock(true);
    public static Condition providerCondition=reentrantLock.newCondition();
    public static Condition consumerCondition=reentrantLock.newCondition();
}

书店:

public class ReentrantLockProviderThread extends Thread{
    public static List<String> books=new ArrayList<String>();
    public static int MAX_BOOK_SIZE=10;
    public void run(){
            while(!Thread.interrupted()){
                LockForSell.reentrantLock.lock();
                try{
                    if(books.size()>=MAX_BOOK_SIZE){
                        System.out.println("书已达最大总量:"+MAX_BOOK_SIZE+"  无法继续生产");
                        LockForSell.providerCondition.await();//生产线程释放锁,开始等待
                        System.out.println("已经有顾客买书了,可以继续生产");
                    }else{
                        books.add("BOOK");
                        System.out.println("生产书一本,当前书总量:"+books.size());
                        LockForSell.consumerCondition.signal();
                    }
                }catch(Exception e){
                    e.printStackTrace();
                }finally{
                    LockForSell.reentrantLock.unlock();
                }

            }
    }
}

顾客:

public class ReentrantLockConsumerThread extends Thread{
    public int sellCount=0;
    public void run(){
       while(sellCount<10){
           LockForSell.reentrantLock.lock();
           try{
               if(ReentrantLockProviderThread.books.size()<=0){
                   System.out.println("书已售空 无法继续购买");
                   LockForSell.consumerCondition.await();
                   System.out.println("有新书了,可以继续购买");
               }else{
                   sellCount++;
                   ReentrantLockProviderThread.books.remove(ReentrantLockProviderThread.books.get(0));
                   System.out.println("购买书一本,当前书量:"+ReentrantLockProviderThread.books.size()+"  已购买"+sellCount+"本书");
                   LockForSell.providerCondition.signal();
               }
           }catch(Exception e){
               e.printStackTrace();
           }finally{
               LockForSell.reentrantLock.unlock();
           }

       }
    }
}

总线程:

public class ReentrantLockMainTest {
    public static void main(String args[]){
        LockForSell lockForSell=new LockForSell();
        ReentrantLockProviderThread providerThread=new ReentrantLockProviderThread();
        ReentrantLockConsumerThread consumerThread=new ReentrantLockConsumerThread();
        providerThread.start();
        consumerThread.start();
    }
}

运行结果:

书已售空 无法继续购买
生产书一本,当前书总量:1
有新书了,可以继续购买
生产书一本,当前书总量:2
购买书一本,当前书量:1  已购买1本书
生产书一本,当前书总量:2
购买书一本,当前书量:1  已购买2本书
生产书一本,当前书总量:2
购买书一本,当前书量:1  已购买3本书
生产书一本,当前书总量:2
购买书一本,当前书量:1  已购买4本书
生产书一本,当前书总量:2
购买书一本,当前书量:1  已购买5本书
生产书一本,当前书总量:2
购买书一本,当前书量:1  已购买6本书
生产书一本,当前书总量:2
购买书一本,当前书量:1  已购买7本书
生产书一本,当前书总量:2
购买书一本,当前书量:1  已购买8本书
生产书一本,当前书总量:2
购买书一本,当前书量:1  已购买9本书
生产书一本,当前书总量:2
购买书一本,当前书量:1  已购买10本书
生产书一本,当前书总量:2
生产书一本,当前书总量:3
生产书一本,当前书总量:4
生产书一本,当前书总量:5
生产书一本,当前书总量:6
生产书一本,当前书总量:7
生产书一本,当前书总量:8
生产书一本,当前书总量:9
生产书一本,当前书总量:10
书已达最大总量:10  无法继续生产
View Code

 

参考内容:

https://www.cnblogs.com/cisol/p/6673190.html

https://www.jianshu.com/p/96c89e6e7e90

https://www.cnblogs.com/csuwater/p/5411693.html

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!