共计 2149 个字符,预计需要花费 6 分钟才能阅读完成。
JUC 的学习与应用(是对文章的补充)
sleep 和 wait 的区别
(两者都须要捕捉异样,这个异样不是重点)
1、每个对象都有一个锁来管制同步拜访,Synchronized 关键字能够和对象的锁交互,来实现同步办法或同步块。sleep()办法 正在执行的线程被动让出 CPU(而后 CPU 就能够去执行其余工作),在 sleep 指定工夫后 CPU 再回到该线程持续往下执行 (留神:sleep 办法只让出了 CPU,而并不会开释同步资源锁!!!);wait() 办法 则是指以后线程让本人临时让步出同步资源锁,以便其余正在期待该资源的线程失去该资源进而运行,只有调用了 notify()办法,之前调用 wait()的线程才会解除 wait 状态,能够去参加竞争同步资源锁,进而失去执行。(留神:notify 的作用相当于叫醒睡着的人,而并不会给他分配任务,就是说 notify 只是让之前调用 wait 的线程有权力从新参加线程的调度);
2、sleep()办法 能够在任何中央应用;wait()办法 则只能在同步办法或同步块中应用(否则执行的时候就会报错java.lang.IllegalMonitorStateException
)详情能够参考这个博客;
3、sleep()是线程线程类(Thread)的办法,调用会暂停此线程指定的工夫,但监控仍然放弃,不会开释对象锁,到工夫主动复原;wait()是 Object 的办法,调用会放弃对象锁,进入期待队列,待调用 notify()/notifyAll()唤醒指定的线程或者所有线程,才会进入锁池,不再次取得对象锁才会进入运行状态;
synchronized 和 lock
lock 锁的几种机制
上面举例具体阐明这四种办法的应用:如果线程 A 和线程 B 应用同一个锁 LOCK,此时线程 A 首先获取到锁 LOCK.lock(),并且始终持有不开释。如果此时 B 要去获取锁,有四种形式:
①LOCK.lock(): 此形式会始终处于期待中,即便调用 B.interrupt()也不能中断,除非线程 A 调用 LOCK.unlock()开释锁。②LOCK.lockInterruptibly(): 此形式会期待,但当调用 B.interrupt()会被中断期待,并抛出 InterruptedException 异样,否则会与 lock()一样始终处于期待中,直到线程 A 开释锁。
③LOCK.tryLock(): 该处不会期待,获取不到锁并间接返回 false,去执行上面的逻辑。
④LOCK.tryLock(10, TimeUnit.SECONDS):该处会在 10 秒工夫内处于期待中,但当调用 B.interrupt()会被中断期待,并抛出 InterruptedException。10 秒工夫内如果线程 A 开释锁,会获取到锁并返回 true,否则 10 秒过后会获取不到锁并返回 false,去执行上面的逻辑。
两者比照
- Synchronized 内置的 Java 关键字,Lock 是一个 Java 类
- Synchronized 无奈判断获取锁的状态,Lock 能够判断是否获取到了锁
- Synchronized 会主动开释锁,lock 必须要手动开释锁!如果不开释锁,死锁
- Synchronized 线程 1(取得锁,阻塞)、线程 2(期待,傻傻的等);Lock 锁就不肯定会期待下
去; - Synchronized 可重入锁,不能够中断的,非偏心;Lock,可重入锁,能够 判断锁,非偏心(能够
本人设置); - Synchronized 适宜锁大量的代码同步问题,Lock 适宜锁大量的同步代码!
生产者消费者问题
传统 wait 和 notify 带来的虚伪唤醒问题
参考虚伪唤醒,也就是说在 while 中,被唤醒的时候是从 wait 的中央开始,这样就会再做一次判断,而 if 就间接走上面的逻辑,不会二次判断。参考案例
汇合类线程平安
线程安全类有两种计划,一种是应用 Collections.synchronizedxxx()
办法,一种是应用比方 CopyOnWriteList,这两种适宜不同的场景,具体能够参考这两个
1.List 汇合
CopyOnWriteArrayList 实现原理
2.set 汇合
阻塞队列
形式 | 抛出异样 | 有返回值,不抛出异样 | 阻塞期待 | 超时期待 |
---|---|---|---|---|
增加 | add | offer() | put() | offer(,,) |
移除 | remove | poll() | tack() | poll(,,) |
查看队首元素 | element | peak() | – | – |
四组 api 中,其中第三组中,阻塞的时候 task,poll,remove 是都能够取出数据而后再 put 的。
SynchronousQueue(狂的演示代码不能阐明问题,因为每次都是线程期待)如果始终 put 的话,的确队列中只能存在一个值,只有被取出才能够再放。参考
线程池
- 1、2 示意外围线程数
- 当候客区满了之后就是启动 3、4、5 的最大线程数
- 此时如果还是满的就是触发回绝策略
- 当线程完结,3、4、5 闲暇工夫达到设置的值后就会主动回收
JMM
小狂笔记中的 store 和 write 写反了。
单例模式
懒汉式中的 dubble check 还要 volitaile,避免在两次检测后进行 new 的时候呈现指令重排,导致对象还没有实例化实现。一般的单例模式能够通过反射被破解。
原子援用
应用包装类可能会呈现问题,失常的业务逻辑应该是不同的 java 类。因为在进行 compare 的时候应用的是 ==
来判断的。