zoukankan      html  css  js  c++  java
  • synchronized关键字,Lock对象,阻塞队列问题

    一.   请你谈一谈synchronized和lock有什么区别?

    1.synchronized是java的关键字,属于jvm层面,底层是通过moninter对象实现的.Lock是具体的接口,属于api层面.

    2.synchronized不需要用户去手动释放锁,当synchronized的代码执行完成后,系统会自动释放线程对锁的占用,Lock

    则需要用户去手动释放锁,如果没有主动去释放锁,就会导致死锁的发生.

    3.synchronized不可被中断,除非程序执行完毕或抛出异常.Lock可以被中断(1.设置超时方法 tryLock() 2.lockInterruptibly()

    放代码块中,调用interrupt()可中断)

    4.synchronized是非公平锁,Lock默认也是非公平锁,但可以通过构造方法,传入布尔值来确定锁的类型.(true 公平锁,false 非公平锁)

    5.synchronized没有绑定多个条件,Lock可以分组唤醒需要唤醒的线程,实现精确唤醒,而不像synchronized那样随机唤醒一个线程

    要么全部唤醒.

    案例:实现多线程之间的精确调用(实现A->B->C三个线程之间的启动),

    lock优于synchronized的举例

    要求:A线程打印5次,B线程打印10次,C线程打印15次,紧接着A在打印5次

    B打印10次,C打印15次.循环执行10次

    class ShareResource{
        private int num = 1; // 标志位 1表示A线程执行,2表示B线程执行,3表示C线程执行
        private Lock lock = new ReentrantLock();
        private Condition c1 = lock.newCondition(); // A线程执行条件
        private Condition c2 = lock.newCondition(); // B线程执行条件
        private Condition c3 = lock.newCondition(); // C线程执行条件
    
        public void print5(){
            lock.lock();
            try {
                //如果不是A线程执行
                while (num != 1){
                    //A线程等待
                    c1.await();
                }
                // 切换到B线程
                num = 2;
                // 唤醒B线程
                c2.signal();
                for (int i = 1; i <= 5; i++) {
                    System.out.println(Thread.currentThread().getName() + "	"+i);
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    
        public void print10(){
            lock.lock();
            try {
                while (num != 2){
                    c2.await();
                }
                num = 3;
                c3.signal();
                for (int i = 1; i <= 10; i++) {
                    System.out.println(Thread.currentThread().getName() + "	"+i);
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    
        public void print15(){
            lock.lock();
            try {
                while (num != 3){
                    c3.await();
                }
                num = 1;
                c1.signal();
                for (int i = 1; i <= 15; i++) {
                    System.out.println(Thread.currentThread().getName() + "	"+i);
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }            
     public static void main(String[] args) {
            ShareResource sr = new ShareResource(); //获取对象
            // 执行10次A线程
            new Thread(()->{
                for (int i = 1; i <= 10; i++) {
                    sr.print5();
                }
            },"A").start();
       // 执行10次B线程
            new Thread(()->{
                for (int i = 1; i <= 10; i++) {
                    sr.print10();
                }
            },"B").start();
       // 执行10次C线程
            new Thread(()->{
                for (int i = 1; i <= 10; i++) {
                    sr.print15();
                }
            },"C").start();
        }

    执行结果:

     A,B,C三个线程依次执行,没有插队的情况,结果就是正确的

    二 .    请你谈一谈生产者/消费者模型,并结合业务,写一个案例

    举例:多个线程同时操作一份数据,生产者+1,消费者-1

    1.使用Lock实现

    class ShareData{
        private int number = 0;   // 资源数据
        private Lock lock = new ReentrantLock(); // 锁对象
        private Condition condition = lock.newCondition(); // 条件
    
        /*生产者 +1操作*/
        public void increment(){  
            lock.lock();
            try {
                // 如果生产者已经满了,就进入等待状态
                // 要使用while防止虚假唤醒(jdk1.8文档源码,不能使用if !!!)
                while (number != 0){
                    // 等待
                    condition.await();
                }
                // 业务逻辑 +1
                number++;
                System.out.println(Thread.currentThread().getName() + "	"+number);
                //唤醒消费者线程进行-1
                condition.signalAll();
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
        /*消费者线程 -1操作*/
        public void decrement(){
            lock.lock();
            try {
                // 如果已经没有资源了,number = 0 ,等待生产者线程生产
                while (number == 0){
                    condition.await();
                }
                // 业务逻辑 -1
                number--;
                System.out.println(Thread.currentThread().getName() + "	"+number);
                // 唤醒生产者线程
                condition.signalAll();
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
     public static void main(String[] args) {
    
            ShareData shareData = new ShareData();
            // 生产者
            new Thread(()->{
                for (int i = 1; i <= 5; i++) {
                    shareData.increment();
                }
            },"T1").start();
    
            //消费者
            new Thread(()->{
                for (int i = 1; i <= 5; i++) {
                    shareData.decrement();
                }
            },"T2").start();
    
            
        }

    执行结果:

     生产一个,消费一个,生成一个,消费一个 .......

    2. 使用BlockingQueue实现

    class ShareMessage{
        // 开关,true表示执行,false表示关闭
        private volatile boolean FLAG = true;
        // 原子整型,操作不能被分割
        private AtomicInteger atomicInteger = new AtomicInteger();
        // 阻塞队列,用于存放数据
        private BlockingQueue<String> blockingQueue = null;
    
        // 初始化BlockingQueue 对象的具体的实现类
        public ShareMessage(BlockingQueue<String> blockingQueue) {
            this.blockingQueue = blockingQueue;
            System.out.println(blockingQueue.getClass().getName());
        }
    
       /*生产者模型,*/
        public void produce() throws InterruptedException{
            String data = null; 
            boolean retValue ;
            while (FLAG){ // 如果开关打开
                 // 等同于 i++ + ""
                data = atomicInteger.incrementAndGet() + "";
                // 把数据放到阻塞队列中,并设置超时的时间
                retValue = blockingQueue.offer(data,2L, TimeUnit.SECONDS);
                // 如果放置成功
                if(retValue){
                    System.out.println(Thread.currentThread().getName() + "	"+ data + "插入队列成功");
                }else {
                    System.out.println(Thread.currentThread().getName() + "	"+ data + "插入队列失败");
                }
                 // 让消费者线程取,方便显示
                TimeUnit.SECONDS.sleep(1);
            }
            System.out.println(Thread.currentThread().getName() + "生产结束,FLAG设置为false");
    
        }
        /*消费者线程*/
        public void consume() throws InterruptedException{
            String res = null;
            while (FLAG){
                // 从阻塞队列中获取数据
                res = blockingQueue.poll(2L,TimeUnit.SECONDS);
                // 如果没有获取到数据,
                if(null == res || res.equalsIgnoreCase("")){
                    // 停止生产者线程,并退出当前的消费者线程
                    FLAG = false;
                    System.out.println(Thread.currentThread().getName()+"	超过2秒钟没有取到数据,消费退出");
                    return;
                }
                System.out.println(Thread.currentThread().getName()+"	"+res+"消费队列成功");
            }
        }
    
        /*关闭生产/消费模型*/
        public void stop(){
            this.FLAG = false;
        }
    
    }
    public static void main(String[] args) {
            ShareMessage sm = new ShareMessage(new ArrayBlockingQueue<>(10));
    
            // 启动生产者
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"生产线程启动");
                try {
                    sm.produce();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"produce").start();
    
            // 启动消费者
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"消费线程启动");
                try {
                    sm.consume();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"consume").start();
    
           // 执行5秒钟后,停止运行
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            sm.stop();
        }

    执行结果:

    j结论:

    使用阻塞队列实现生产者/消费者模型更优于使用Lock的方式,因为阻塞队列中不需要手动的去停止/唤醒线程.

  • 相关阅读:
    DLL内存加载
    Intel汇编程序设计-高级过程(上)
    Intel汇编程序设计-高级过程(上)
    C#-常用
    C#-常用
    C#-MD5
    C#-MD5
    C#-文件操作
    POJ 3320 Jessica's Reading Problem
    POJ 2456 Aggressive cows
  • 原文地址:https://www.cnblogs.com/luhuajun/p/12130819.html
Copyright © 2011-2022 走看看