• 多线程下的锁


    1. synchronized和lock的区别:

    1. synchronized 是Java内置关键字,lock 是Java类;
    2. synchronized 无法判断是否获得锁,lock 可以;
    3. synchronized 会自动释放,lock 需要手动释放;
    4. synchronized 线程1阻塞,线程2会永远等待下去,lock 线程1阻塞,线程2lock.trylock() // 会尝试去获得锁,若获取不到会结束等待;
    5. synchronized 可重入,不可中断,非公平, lock可重入,可判断,可公平;

      共同注意点:

      睡眠建议使用TimeUnit类,不建议使用sleep;  

      在多线程下用 if () 判断条件是不安全的, 应该用while () 进行判断。

        private int num = 0;
        public synchronized void add() throws Exception {
            // 判断
            while (num != 0) { // 若此处用if()进行判断,在多线程下结果错误
                this.wait();
            }
            // 进行业务编码
            num++;
            System.out.println(Thread.currentThread().getName()+"\t"+num);
            // 通知
            this.notifyAll();
        }

      使用synchronized 关键字,notify() (Object类中方法) 不能精准唤醒线程,唤醒线程由Java虚拟机决定。

      使用lock锁,可实现Condition接口,signal() 方法可精准唤醒线程。

        private int num = 1;
        Lock lock = new ReentrantLock();
        private Condition condition1 = lock.newCondition();
        private Condition condition2 = lock.newCondition();
        private Condition condition3 = lock.newCondition();
    
        public void print5() {
            lock.lock();
    
            try {
                while (num != 1) {
                    condition1.await();
                }
                for (int i = 1; i <= 5; i++) {
                    System.out.print(Thread.currentThread().getName() + "\t" + i + "->");
                }
                System.out.println();
                num = 2;
                condition2.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void print10() {
            lock.lock();
    
            try {
                while (num != 2) {
                    condition2.await();
                }
                for (int i = 1; i <= 10; i++) {
                    System.out.print(Thread.currentThread().getName() + "\t" + i + "->");
                }
                System.out.println();
                num = 3;
                condition3.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    
        public void print15() {
            lock.lock();
    
            try {
                while (num != 3) {
                    condition3.await();
                }
                for (int i = 1; i <= 15; i++) {
                    System.out.print(Thread.currentThread().getName() + "\t" + i + "->");
                }
                System.out.println();
                num = 1;
                condition1.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

      

    2. 多线程下使用的辅助类

    • CountDownLatch 倒计时锁存器 (Object类)

      倒计时计数达到参数数量,即释放阻塞,执行后续操作

            CountDownLatch countDownLatch = new CountDownLatch(8); // 参数:线程数
        
            for (int i = 0; i < 6; i++) {
                new Thread(() -> {
                    System.out.println(Thread.currentThread().getName()+"_start");
                    countDownLatch.countDown(); // 每执行一个线程,参数-1
                }).start();
            }
            
            countDownLatch.await(); // 当达到参数指定的线程数,释放阻塞
            System.out.println(Thread.currentThread().getName()+"_end");
    • CyclicBarrier 循环阻塞计数器

      举例:集龙珠,每集齐一颗龙珠(线程),计数+1 ,当龙珠数量达到7 ,即可召唤神龙 (只要执行的线程足够,即可循环召唤神龙)

    CyclicBarrier cyclicBarrier = new CyclicBarrier(7, new Runnable() { // 参数1:线程数量 参数2:待执行的操作
         @Override
         public void run() {
            System.out.println("hello...");
        }
    });
    
    for (int i = 0; i < 7; i++) {
            new Thread(()-> {
                System.out.println(Thread.currentThread().getName()+"_start");
            try {
                cyclicBarrier.await(); // 循环计数并等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }).start();
    }                        
    • Semaphore 一个计数信号量

      举例:抢车位,参数为车位数,同时获得许可证的车(线程)一共3个,当车开走,后面的才可获得许可证进入

    Semaphore semaphore = new Semaphore(3); 参数:线程数量
    
    for (int i = 0; i < 6; i++) {
        new Thread(() -> {
            try {
                semaphore.acquire(); // 获取许可证
                System.out.println(Thread.currentThread().getName()+"到了");
                TimeUnit.SECONDS.sleep(3);  // 睡眠建议用TimeUnit类,不建议使用sleep
                System.out.println(Thread.currentThread().getName()+"走了");
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphore.release(); // 释放许可证
            }
        }).start();
    }

    3. callable 和 runnable 的区别:

    1. callable 有返回值,runnable 没有返回值
    2. callable 会抛出异常,runnable 不会
    3. 方法不同 call() , run()    

      通过源码发现 FutrueTask<> 类可使两者产生联系

  • 相关阅读:
    DP问题之最长非降子序列
    CentOS 6.8 编译安装MySQL5.5.32
    [Linux] killall 、kill 、pkill 命令详解
    编写登陆接口
    python学习day01
    python购物车程序
    ERROR:Attempting to call cordova.exec() before 'deviceready'
    BSF脚本引擎‘改变’Bean
    Solr安装配置
    amchart配置备忘
  • 原文地址:https://www.cnblogs.com/ShallowPen/p/12398254.html
Copyright © 2020-2023  润新知