JUC

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,去执行下面的逻辑。

两者对比

  1. Synchronized 内置的Java关键字, Lock 是一个Java类
  2. Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
  3. Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
  4. Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下
    去;
  5. Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以
    自己设置);
  6. 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的话,确实队列中只能存在一个值,只有被取出才可以再放。参考

线程池

JUC_第1张图片

  • 1、2表示核心线程数
  • 当候客区满了之后就是启动3、4、5的最大线程数
  • 此时如果还是满的就是触发拒绝策略
  • 当线程结束,3、4、5空闲时间到达设置的值后就会自动回收

JMM

小狂笔记中的store 和 write写反了。

单例模式

懒汉式中的dubble check 还要volitaile,防止在两次检测后进行new 的时候出现指令重排,导致对象还没有实例化完成。普通的单例模式可以通过反射被破解。

原子引用

使用包装类可能会出现问题,正常的业务逻辑应该是不同的java类。因为在进行compare的时候使用的是==来判断的。

你可能感兴趣的:(程序员,多线程)