重量级锁与轻量级锁
轻量级锁一般使用的是cas原理,进行自旋来获取锁,如果一直有多个线程发生竞争的话,会发生CPU过高的情况。
重量级锁一般使用Synchronized或者ReentrantLock,ReentrantLock底层原理是AQS。Synchronized有锁升级的过程,ReentrantLock可操作性更强,比如选择公平锁或者非公平锁(饿死现象),以及手动释放等。
CountDownLatch:创建时的参数为计数器的值
await()方法:阻塞等待执行,aqs
countDown()方法:对计数器中的值进行递减,减到0唤醒所有因调用await方法而被阻塞的线程。
相比于使用join方法来实现线程间同步,CountDownLatch更具有灵活性和方便性。并且调用子程序的join()方法后,该线程会一直被阻塞直到子程序运行完毕,而CountDownLatch是使用计数器来允许子线程运行完毕或者运行中递减计数,而没必要一定等待线程结束,另外,使用线程池来管理线程时,一般都是直接添加到Runable到线程池,这时候没有办法再调用线程的join方法。
**CyclicBarrier :**回环屏障,也是基于aqs
CyclicBarrir构造器中parties
await()方法:当前线程会被阻塞,直到parties个线程都调用了await方法,或者被中断。当count为0时,重新设为parties。
CycleBarrier与ountDownLatch 的不同在于,前者是可以复用 的,并且前者特别适合分段任务有序执行的场景。然后分析了 CycleBarrier 其通独占锁 ReentrantLock 实现计数器原子性更新,并使用条件变量队列来实现线程同步。
**Semaphore:**信号量,构造器入参为信号量计数,以及是否公平策略,默认非公平。如果为公平锁,底层为LockSupport
acqire()方法:非公平,先尝试能否获取到信号量,非公平则是先看aqs阻塞队列中有没有其他线程,没有在尝试获取,如果没有获取到,则当前线程会被放入 AQS 的阻塞队列。
acqire(int i)方法:
release()方法:该方法的作用 Semaphore 信号量值增加 ,如果当前有线程因为调用aquire 方法被阻塞 被放入了 AQS 的阻塞队列,则会根据公平策略选择一个信号量个数能被满足的线程进行激活, 激活的线程会尝试获取刚增加的信号量。
release(int i)方法:
Semaphor 完全可以达到CountDownLatch 的效果,但是 Semaphore 的计数器是不可以自动重置的, 不过通过变相地改变 aquire 方法的参数还是可以实现 CycleBarrier 的功能的 。 Semaphore 也是使用 AQS 现的,并且获取信号量时有公平策略和非公平策略之分。
线程池中Future与get方法可能出现的问题
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 1,
TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), new ThreadPoolExecutor.AbortPolicy());
Future> submit1 = threadPoolExecutor.submit(() -> {
try {
TimeUnit.SECONDS.sleep(5);
System.out.println("8777");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Future> submit2 = threadPoolExecutor.submit(() -> System.out.println("8989"));
Future> submit3 = null;
try {
submit3=threadPoolExecutor.submit(() -> System.out.println("9999"));
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(submit1.get());
System.out.println(submit2.get());
System.out.println(submit3!=null?submit3.get():null);
threadPoolExecutor.shutdown();
在线程池中使用 FutureTask 时, 当拒绝策略为 DiscardPolicy和DiscardOldestPolicy 时,在被拒绝的任务FutureTask 对象上调用 get() 方法会导致调用线程一直阻塞,所以在日常开发中尽量使用带超时参数的 get 方法以避免线程一直阻塞。原因是这两个拒绝策略返回的是New状态的Future对象。



