多线程下的锁
Posted shallowpen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程下的锁相关的知识,希望对你有一定的参考价值。
1. synchronized和lock的区别:
- synchronized 是Java内置关键字,lock 是Java类;
- synchronized 无法判断是否获得锁,lock 可以;
- synchronized 会自动释放,lock 需要手动释放;
- synchronized 线程1阻塞,线程2会永远等待下去,lock 线程1阻塞,线程2lock.trylock() // 会尝试去获得锁,若获取不到会结束等待;
- 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 的区别:
- callable 有返回值,runnable 没有返回值
- callable 会抛出异常,runnable 不会
- 方法不同 call() , run()
通过源码发现 FutrueTask<> 类可使两者产生联系
以上是关于多线程下的锁的主要内容,如果未能解决你的问题,请参考以下文章