多线程下的锁

Posted shallowpen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程下的锁相关的知识,希望对你有一定的参考价值。

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()+"	"+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() + "	" + 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() + "	" + 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() + "	" + 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<> 类可使两者产生联系

以上是关于多线程下的锁的主要内容,如果未能解决你的问题,请参考以下文章

多线程:synchronized代码块synchronized方法静态synchronized方法使用的锁

线程同步-使用ReaderWriterLockSlim类

Java多线程——Lock&Condition

Java多线程与并发库高级应用-工具类介绍

Java多线程与并发库高级应用-工具类介绍

Java多线程——锁概念与锁优化