线程并发,那就牵扯到内存共享的问题,在并发编程中,有三个理念:原子性、可见性、有序性。这里分享一个转载。
在ThreadPoolExecutor中,我们用到了AtomicInteger完成了列队中数量、状态的管理。为什么选择AtomicInteger呢?AtomicInteger是Number的子类,里面的值,就是一个volatile修饰的value,那么value就有了对多线程的可见性和一定的有序性了。只有这些,还是不够,那谁来保证他的原子性呢?Unsafe。Unsafe是个极其强大但也很危险的东西,很多牛逼的工具底层都基于他来实现的,他可以直接使用allocateMemory为对象分配内存,理论上可以直接使用堆的内存大小。
Unsafe为AtomicInteger提供了CAS等算法,这里不讲太多Unsafe的东西,大家有兴趣可以搜索下,文章很多。给大家分享一个。
UnSage的功能图
至此,大家都明白了为什么用AtomicInteger了吧,因为他在多线程的情况下,能保证原子性、可见性、有序性。
以ThreadPoolExecutor为例,他的workerCount的增减,就是使用了CAS。
/**
* Attempts to CAS-increment the workerCount field of ctl.
*/
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
/**
* Attempts to CAS-decrement the workerCount field of ctl.
*/
private boolean compareAndDecrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect - 1);
}
CAS(Compare And Swap)先比较在替换,是我们所知道的最小操作单元。CAS使用JNI直接完成Java的非阻塞算法,直接交换内存地址。但他不是保证肯定会成功的。
所以,ThreadPoolExecutor中的addWorker的嵌套死循环,后面的再次检查。
在回过头来看下ThreadPoolExecutor中的AtomicInteger,他使用CompareAndSet将当前workerCount和各种状态都存起来,以便多线程之间共享使用。以便池子可以按照列队进行调度。除了线程间是贡献,ThreadPoolExecutor本身也是线程安全的。
重点:如何控制开启线程
接Android并发之Executor(线程池)家族(一)是个跨度,上一篇我没有将的如何控制线程的开启,这期补上。
先来代码。
private void moreYourBody() throws RuntimeException {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4, 10, 3
, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3)
, new ThreadPoolExecutor.DiscardOldestPolicy());
for (int i = 0; i < 10; i++) {
System.out.println("dtl:" + ctl.get());