线程的生命周期

ThreadLocal

Thead源码

1
2
3
4
5
6
7
8
9
10
public class Thread implements Runnable {
//......
//与此线程有关的ThreadLocal值。由ThreadLocal类维护
ThreadLocal.ThreadLocalMap threadLocals = null;

//与此线程有关的InheritableThreadLocal值。由InheritableThreadLocal类维护
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
//......
}

ThreadLocalMap是TreadLocal的内部类

ThreadLocal内存泄漏

ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,而 value 是强引用。所以,如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。这样一来,ThreadLocalMap 中就会出现 key 为 null 的 Entry。假如我们不做任何措施的话,value 永远无法被 GC 回收,这个时候就可能会产生内存泄露。ThreadLocalMap 实现中已经考虑了这种情况,在调用 set()、get()、remove() 方法的时候,会清理掉 key 为 null 的记录。使用完 ThreadLocal方法后 最好手动调用remove()方法

1
2
3
4
5
6
7
8
9
10
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;

Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}

volatile

保证了变量和可见性和顺序性,无法保证原子性(如自增操作)
底层利用内存屏障来实现,Unsafe提供了三个内存屏障的类:

1
2
3
public native void loadFence();
public native void storeFence();
public native void fullFence();

synchronized

  1. 修饰实例方法 (锁当前对象实例)
  2. 修饰静态方法 (锁当前类)
  3. 修饰代码块 (锁指定对象/类)

synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法。不过两者的本质都是对对象监视器 monitor 的获取。

AQS

核心内容state和CLH(虚拟的双向队列)
自定义同步器实现时主要实现以下几种方法:

  1. isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
  2. tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
  3. tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
  4. tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
  5. tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false

Semaphore

限制一定数量的线程执行,内部定义Sync实现AQS,底层使用AQS的state

1
2
3
4
Semaphore sem = new Semaphore(2);
//子线程
sem.acquire();
sem.release();

CountDownLatch

表示一个线程需要等一批线程执行后才能执行,内部定义Sync实现AQS,底层使用AQS的state,state为一批线程的数量

1
2
3
4
5
CountDownLatch latch = new CountDownLatch(10);
//子线程
latch.countDown();
//主线程
latch.await();

缺点:

  1. 只能使用一次

CyclicBarrier

表示所有子线程都调用wait后,先执行cyclicBarrier的回调函数,再继续子线程wait后的方法
底层依赖ReentrandLock和Condition,利用ReentrantLock的Condition来阻塞和通知线程;

1
2
3
4
5
CyclicBarrier cyclicBarrier = new CyclicBarrier(10,()->{
System.out.println("所有人都准备好了裁判开始了");
});
//子线程
cyclicBarrier.await();

优点:

  1. 可以循环使用

synchronized 和 volatile 有什么区别?

synchronized 和 ReentrantLock 有什么区别?

  1. 两者都是可重入锁
  2. synchronized 依赖于 JVM 而 ReentrantLock 依赖于 API
  3. ReentrantLock能力更多:等待可中断 ,可实现公平锁,可实现选择性通知(锁可以绑定多个条件)

线程池

Executors工具类(不推荐)

  1. FixedThreadPool,允许请求的队列长度为 Integer.MAX_VALUE
  2. SingleThreadExecutor,允许请求的队列长度为 Integer.MAX_VALUE
  3. CachedThreadPool, 允许创建的线程数量为 Integer.MAX_VALUE
  4. ScheduledThreadPool, 允许创建的线程数量为 Integer.MAX_VALUE

ThreadPoolExecutor

核心参数

  1. corePoolSize:核心线程数
  2. maximumPoolSize:最大线程数,队列中存放的任务达到队列容量的时候才会由新增线程到此数值
  3. workQueue:等待队列
  4. keepAliveTime:存活时间,线程池中的线程数量大于 corePoolSize时,超出的线程的存活时间
  5. unit:存活时间的单位
  6. threadFactory:可以自定义实现,指定线程组和线程名称前缀
  7. handler:饱和策略

workQueue

handle

  1. ThreadPoolExecutor.AbortPolicy:抛出异常直接拒绝(默认)
  2. ThreadPoolExecutor.CallerRunsPolicy:调用执行自己的线程运行任务
  3. ThreadPoolExecutor.DiscardPolicy:不处理,直接丢掉
  4. ThreadPoolExecutor.DiscardOldestPolicy:丢弃最早的未处理的任务请求

线程数数量设置

  1. CPU 密集型任务(N+1)
  2. IO 密集型任务(2N)