JUC
CountDownLatch原理
CountDownLatch允许count个线程阻塞在一个地方,直至所有线程的任务都执行完毕。
CountDownLatch是共享锁的一种实现,它默认构造AQS的state为count。
当线程使用countDown方法时,其实使用了tryReleaseShared方法以CAS的操作来减少state,
直至state为0就代表所有的线程都调用了countDown方法。当调用await方法的时候,如果state不为0,
就代表仍然有线程没有调用countDown方法,那么就把已经调用过countDown的线程都放入阻塞队列Park,
并自旋CAS判断state == 0,
直至最后一个线程调用了countDown,使得state == 0,
于是阻塞的线程便判断成功,全部往下执行。
Semaphore
Semaphore允许一次性最多(不是同时)permits个线程执行任务。
Semaphore与CountDownLatch一样,也是共享锁的一种实现。
它默认构造AQS的state为permits。
当执行任务的线程数量超出permits,那么多余的线程将会被放入阻塞队列Park,并自旋判断state是否大于0,
只有当state大于0的时候,阻塞的线程才能继续执行,此时先前执行任务的线程继续执行release方法,
release方法使得state的变量会加1,那么自旋的线程便会判断成功。
如此,每次只有固定的线程能自旋成功,便限制了执行任务线程的数量。所以这也是我为什么说它可能不是permits个线程同时执行,
因为只要state>0,线程就有机会执行.
CycliBarrier
CycliBarrier的功能与CountDownLatch相似,但是CountDownLatch的实现是基于AQS的,
而CycliBarrier是基于ReentrantLock(ReentrantLock也属于AQS同步器)和Condition的.
CountDownLatch虽然可以令线程阻塞,但是CountDownLatch只能await一次就不能使用了,
而CycliBarrier有Generation代的概念,一个代,就代表CycliBarrier的一个循环,
这也是CycliBarrier支持重复await的原因。
ReentrantReadWriteLock如何区分读写锁的?
Sync既有写锁,又有读锁,因此一个state不够用,
所以使用state的高16为表示读锁,低位16表示写锁.
ReentrantReadWriteLock部分源码:
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
/** Returns the number of shared holds represented in count. */
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
/** Returns the number of exclusive holds represented in count. */
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
线程池的好处
http连接池,数据库连接池,线程池等都是利用了池化技术。
如果一个资源需要多次使用并且很昂贵,那么使用new创建的对象或资源,可能会带来较大的消耗。
池化技术的好处在于
- 方便资源的管理,无需显示的使用new创建。
- 降低了资源的消耗,在池子里的资源可以重复利用
- 提供了任务的响应速度,任务可以很快的被分配资源进行处理。
线程池构造参数
new ThreadPoolExecutor
(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize
线程池的核心线程数(常驻线程数),也就是线程池的最小线程数,这部分线程不会被回收.
maximumPoolSize
线程池最大线程数,线程池中允许同时执行的最大线程数量
keepAliveTime
当线程池中的线程数量超过corePoolSize,但此时没有任务执行,
那么空闲的线程会保持keepAliveTime才会被回收,corePoolSize的线程不会被回收。
unit
keepAliveTime的时间单位
workQueue
当线程池中的线程达到了corePoolSize的线程数量,
并仍然有新任务,那么新任务就会被放入workQueue。
threadFactory
创建工作线程的工厂,也就是如何创建线程的,一般采用默认的
handler
拒绝策略,当线程池中的工作线程达到了最大数量,
并且阻塞队列也已经满了,那么拒绝策略会决定如何处理新的任务。
ThreadPoolExecutor 提供了四种策略:
1.AbortPolicy(是线程池的默认拒绝策略):
如果使用此拒绝策略,那么将对新的任务抛出RejectedExecutionException异常,来拒绝任务。
2.DiscardPolicy:
如果使用此策略,那么会拒绝执行新的任务,但不会抛出异常。
3.DiscardOldestPolicy:
如果使用此策略,那么不会拒绝新的任务,
但会抛弃阻塞队列中等待最久的那个线程。
4.CallerRunsPolicy:
如果使用此策略,不会拒绝新的任务,但会让调用者执行线程。
也就是说哪个线程发出的任务,哪个线程执行。
阿里巴巴开发者手册不建议开发者使用Executors创建线程池
newFixedThreadPool和newSIngleThreadPoolExecutor都是创建固定线程的线程池,
尽管它们的线程数是固定的,但是它们的阻塞队列的长度却是Integer.MAX_VALUE的,所以,
队列的任务很可能过多,导致OOM。
newCacheThreadPool和newScheduledThreadPool创建出来的线程池的线程数量却是Integer.MAX_VALUE的,
如果任务数量过多,也很可能发生OOM.