您的位置:首页 > 编程语言 > Java开发

Java之线程池

2018-03-27 10:07 246 查看
多线程的软件设计方法确实可以最大限度地发挥现代多核处理器的计算能力,提高生产系统的吞吐量和性能。但是,若不加控制和管理的随意使用多线程,对系统的性能反而会产生不利的影响。

一个简单的线程创建和回收的方法类似如下:


new Thread(new Runnable() {
@Override
public void run() {
//do sth.
}
}).start();


线程是一种轻量级的工具,但其创建和关闭依然需要花费时间,如果为每一个小的任务都创建一个线程,很有可能出现创建和销毁线程所占用的时间大于该线程真实工作所消耗时间的情况,反而会得不偿失。

其次,线程本身也是要占用内存空间的,大量的线程会抢占宝贵的内存资源,如果处理不当,可能会导致Out of Memory异常。即便没有,大量的线程回收也会给GC带来很大的压力,延长GC的停顿时间。

因此,对线程的使用必须掌握一个度,在有限的范围内,增加线程的数量可以明显提高系统的吞吐量,但一旦超出了这个范围,大量的线程只会拖垮应用系统。因此在生产环境中使用线程,必须对其加以控制和管理。

什么是线程池

为了避免系统频繁地创建和销毁线程,我们可以让创建的线程进行复用。如数据库连接池,为了避免每次数据库查询都重新建立和销毁数据库连接,我们可以使用数据库连接池维护一些数据库连接,让他们长期保持在一个激活状态。当系统需要使用数据库时,并不是创建一个新的连接,而是从连接池中获得一个可以用的连接即可。反之,当需要关闭连接时,并不真的把连接关闭,而是将这个连接归还线程池。

线程池也是类似的概念,简而言之,在使用线程池后,创建线程变成了从线程池获得空闲线程,关闭线程变成了向线程池归还线程。

JDK对线程池的支持

JDK提供了一套Executor床架,其本质就是一个线程池。核心成员均在java.util.concurrent包中.

其中ThreadPoolExecutor表示一个线程池。Executor类扮演着线程池工厂的角色,通过Execturos可以取得一个拥有特定功能的线程池。

Executor框架提供了各种类型的线程池,主要有以下工厂方法。

public static ExecutroService newFixedThreadPool(int nThreads)
public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newCachedThreadPool()
public static ScheduledExecutorService newSingleThreadScheduleExecutor()
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)


这些线程池工厂方法的具体说明如下:

①.newFixedThreadPool()方法:该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂时存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。

public class ExecutorsThreadPool {

public static void main(String[] args) {
//创建有2个线程的线程池(固定大小的线程池)
ExecutorService threadPool = Executors.newFixedThreadPool(2);
for(int i = 0; i < 3; i++) {
final int task = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
for(int j = 0; j < 3; j++) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is looping of " + j + " for task of " + task);
}
}
});
}
System.out.println("all of tasks hava committed!");
threadPool.shutdown();
}
}


输出如下

all of tasks hava committed!
pool-1-thread-2 is looping of 0 for task of 1
pool-1-thread-1 is looping of 0 for task of 0
pool-1-thread-2 is looping of 1 for task of 1
pool-1-thread-1 is looping of 1 for task of 0
pool-1-thread-2 is looping of 2 for task of 1
pool-1-thread-1 is looping of 2 for task of 0
pool-1-thread-2 is looping of 0 for task of 2
pool-1-thread-2 is looping of 1 for task of 2
pool-1-thread-2 is looping of 2 for task of 2


②.newSingletThreadExecutor()方法:该方法返回一个只有一个线程的线程池,若多余一个任务被提交到该线程池,任务会被保存到一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。

public class ExecutorsThreadPool {

public static void main(String[] args) {

//创建单一线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();

for(int i = 0; i < 3; i++) {
final int task = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
for(int j = 0; j < 3; j++) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is looping of " + j + " for task of " + task);
}
}
});
}
System.out.println("all of tasks hava committed!");
threadPool.shutdown();
}
}


输出如下

all of tasks hava committed!
pool-1-thread-1 is looping of 0 for task of 0
pool-1-thread-1 is looping of 1 for task of 0
pool-1-thread-1 is looping of 2 for task of 0
pool-1-thread-1 is looping of 0 for task of 1
pool-1-thread-1 is looping of 1 for task of 1
pool-1-thread-1 is looping of 2 for task of 1
pool-1-thread-1 is looping of 0 for task of 2
pool-1-thread-1 is looping of 1 for task of 2
pool-1-thread-1 is looping of 2 for task of 2


③.newCachedThreadPool()方法:该方法返回一个可根据实际情况调整线程数量的线程池,线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。如所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完成后,将返回线程池进行复用。

public class ExecutorsThreadPool {

public static void main(String[] args) {
//动态生成线程池, 下面就会会分配3个线程处理3个任务(缓存线程池)
ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i = 0; i < 3; i++) {
final int task = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
for(int j = 0; j < 3; j++) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is looping of " + j + " for task of " + task);
}
}
});
}
System.out.println("all of tasks hava committed!");
threadPool.shutdown();
}
}


输出如下

all of tasks hava committed!
pool-1-thread-1 is looping of 0 for task of 0
pool-1-thread-2 is looping of 0 for task of 1
pool-1-thread-3 is looping of 0 for task of 2
pool-1-thread-2 is looping of 1 for task of 1
pool-1-thread-3 is looping of 1 for task of 2
pool-1-thread-1 is looping of 1 for task of 0
pool-1-thread-1 is looping of 2 for task of 0
pool-1-thread-3 is looping of 2 for task of 2
pool-1-thread-2 is looping of 2 for task of 1


④.newSingleThreadScheduledExecutor()方法:该方法返回一个ScheduledExecutorService对象,线程池大小为1。ScheduledExecutorService接口在Executor接口之上扩展了在给定时间执行某任务的功能,如在某个固定的延时后执行,或者周期性执行某个任务。

newScheduledThreadPool()方法:该方法也返回一个ScheduledExecutorService对象,但该线程池可以指定线程数量。

public class ExecutorsScheduledThread {
public static void main(String[] args) {
//设定固定时间6秒后开始然后每隔2秒一次
Executors.newScheduledThreadPool(3).scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("run-" + (System.currentTimeMillis() / 1000)%60);
}
}, 6, 2, TimeUnit.SECONDS);
}
}


输出示例

start-1522115911
run-1522115917
run-1522115919
run-1522115921
run-1522115923
run-1522115925


--参考书籍《Java高并发程序设计》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java线程池