java并发(1)-Executors
关于java创建多线程常用的两种方式,我就不多说了,无非就是实现Runnable和继承Thread。那么我们现在来说说其他的方法。
Executors是什么
Executors,我们称之为执行器,用于管理Thread对象,简化并发过程,其在客户端和任务执行之间提供了一个间接层。由中介执行任务。无需管理线程的生命周期。
我们先是实现一个Runnable的类,其功能非常简单,就是在一个计数器,而且对于每一个创建的对象来说,其id都不一样,方便我们识别。
class MyCountDownRunnale implements Runnable {
private int countDown = 10;
private static int taskCount = 0;
private final int id = taskCount ++;
public MyCountDownRunnale(){}
public MyCountDownRunnale(int countDown) {
this.countDown = countDown;
}
@Override
public void run() {
while (countDown -- > 0) {
System.out.print(toString());
Thread.yield();//线程都让出使用权,加入竞争。
}
}
@Override
public String toString() {
return "$" + id + "<" + Thread.currentThread().getId() + ">(" + (countDown > 0 ? countDown : "Zero") + "), ";
}
}
CachedThreadPool方式
如下代码:
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i ++) {
exec.execute(new MyCountDownRunnale());
}
exec.shutdown();//防止新线程提交
执行结果:
$1<12>(9), $3<14>(9), $1<12>(8), $2<13>(9), $0<11>(9),
$2<13>(8), $1<12>(7), $3<14>(8), $4<15>(9), $3<14>(7),
$1<12>(6), $2<13>(7), $0<11>(8), $2<13>(6), $1<12>(5),
$3<14>(6), $4<15>(8), $3<14>(5), $1<12>(4), $2<13>(5),
$3<14>(4), $2<13>(4), $0<11>(7), $2<13>(3), $3<14>(3),
$1<12>(3), $4<15>(7), $1<12>(2), $3<14>(2), $2<13>(2),
$0<11>(6), $2<13>(1), $3<14>(1), $2<13>(Zero), $1<12>(1),
$4<15>(6), $1<12>(Zero), $3<14>(Zero), $0<11>(5), $4<15>(5),
$0<11>(4), $4<15>(4), $0<11>(3), $4<15>(3), $0<11>(2),
$4<15>(2), $0<11>(1), $4<15>(1), $0<11>(Zero), $4<15>(Zero),
FixedThreadPool方式
如下代码:
ExecutorService exec =Executors.newFixedThreadPool(4);
for (int i = 0; i < 5; i ++) {
exec.execute(new MyCountDownRunnale());
}
exec.shutdown();//防止新线程提交
执行结果:
$0<11>(9), $3<14>(9), $2<13>(9), $1<12>(9), $2<13>(8),
$3<14>(8), $0<11>(8), $3<14>(7), $0<11>(7), $2<13>(7),
$1<12>(8), $2<13>(6), $0<11>(6), $3<14>(6), $0<11>(5),
$2<13>(5), $1<12>(7), $2<13>(4), $0<11>(4), $3<14>(5),
$0<11>(3), $2<13>(3), $1<12>(6), $2<13>(2), $3<14>(4),
$2<13>(1), $0<11>(2), $1<12>(5), $0<11>(1), $2<13>(Zero),
$3<14>(3), $0<11>(Zero), $1<12>(4), $1<12>(3), $3<14>(2),
$1<12>(2), $4<13>(9), $1<12>(1), $1<12>(Zero), $3<14>(1),
$4<13>(8), $3<14>(Zero), $4<13>(7), $4<13>(6), $4<13>(5),
$4<13>(4), $4<13>(3), $4<13>(2), $4<13>(1), $4<13>(Zero),
SingleThreadExecutor方式
如下代码:
ExecutorService exec = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i ++) {
exec.execute(new MyCountDownRunnale());
}
exec.shutdown();//防止新线程提交
执行结果:
$0<11>(9), $0<11>(8), $0<11>(7), $0<11>(6), $0<11>(5),
$0<11>(4), $0<11>(3), $0<11>(2), $0<11>(1), $0<11>(Zero),
$1<11>(9), $1<11>(8), $1<11>(7), $1<11>(6), $1<11>(5),
$1<11>(4), $1<11>(3), $1<11>(2), $1<11>(1), $1<11>(Zero),
$2<11>(9), $2<11>(8), $2<11>(7), $2<11>(6), $2<11>(5),
$2<11>(4), $2<11>(3), $2<11>(2), $2<11>(1), $2<11>(Zero),
$3<11>(9), $3<11>(8), $3<11>(7), $3<11>(6), $3<11>(5),
$3<11>(4), $3<11>(3), $3<11>(2), $3<11>(1), $3<11>(Zero),
$4<11>(9), $4<11>(8), $4<11>(7), $4<11>(6), $4<11>(5),
$4<11>(4), $4<11>(3), $4<11>(2), $4<11>(1), $4<11>(Zero),
三者的区别
看了三个不同的执行结果,我想大家都能知道1和2更3的区别了吧,
1、第三种方式如其名,单线程跑的,他会把提交的任务,按照顺序执行,而且还是在同一个线程里执行的。
2、那第一种和第二种的区别是什么呢?java培训技术构造方法的参数不一样,第二种方式中的参数代表的意思是,一次性预先执行代价昂贵的线程分配,之后就可直接使用了,不过不能滥用,因为数量有限。而第一种就是,执行过程中通常创建所需的数量的线程,然后回收旧线程的时候,停止创建线程。
3、他们两个除了这个区别之外,还有一个重要的特性,就是FixedThreadPool保证了“没有两个线程”会被并发调用。
4、如果一个线程实例请求加入FixedThreadPool时,如果该实例不存在,且没有达到线程池数目上线,则会创建一个实例,否则,会先加入等待序列,当FixedThreadPool中有一个线程停止并移出线程池后,线程实例才能加入线程池。
5、CachedThreadPool中线程实例默认超时时间为60s,超过这个时间,线程实例停止并被移出CachedThreadPool,适用于生存期短、异步的线程任务。FixedThreadPool没有超时机制,适用于稳定且并发线程任务。关于这点,可以看源码中的代码Executors.java:
如下,第三个参数就是超时的时间设置。0就是不超时。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}