线程VS线程池
为什么要使用线程池?我们知道,在Java创建并运行一个线程很简单,只需要实现run()方法并在合适时间点上调用start()方法即可。但是无法在表面看到的是,Java的Thread类调用native方法创建并运行一个线程需要多大的开销。如果在程序中需要有大量的线程执行,对于每一个线程都调用native去创建并运行,势必会造成很大的资源消耗,更多的计算资源集中在创建、开始、销毁线程的工作上,而这时线程池的出现可以解决这方面的问题。所谓线程池就是事先在一个池里创建多个线程并执行它们,即使是空任务它们也可以在循环里跑。一旦外部有线程的执行需求,可以将该需求丢进池里,让其中的正在执行的线程去调用执行,而不是为该需求重新创建线程,节省系统开销。
使用线程池需要注意
1.线程池的大小:该大小决定着同一时间可以执行的任务数。如果太小的话,工作线程可能无法适应任务需求的速度,造成后进的线程需要等待较长时间;太大的话,会浪费内存和计算资源,因为工作线程在无任务的情况下依然会占用CPU时间,接受调度;
2.关闭线程池:使用完线程池需要执行关闭,不然执行完所有任务之后依然会占用CPU时间和内存;
代码
基于以上的初等认识,可以实现自己的线程池,当然需要改进和补充的地方还有很多。
import java.util.LinkedList;
import java.util.List;
public class MyThreadPool {
private int poolSize; // 核心池大小
private MyConcreteThread[] concreteThread; // 工作线程
private List<Runnable> threadList = new LinkedList<Runnable>(); // 任务队列
private static MyThreadPool threadPool = null; // 单例模式
private MyThreadPool(){
this(5); // 默认大小
}
private MyThreadPool(int poolSize){
this.poolSize = poolSize;
concreteThread = new MyConcreteThread[poolSize];
for(MyConcreteThread oneThread:concreteThread){
oneThread = new MyConcreteThread();
oneThread.start(); // 启动线程
}
}
public static MyThreadPool getInstance(){
if(threadPool == null){
synchronized (MyThreadPool.class) {
if(threadPool == null)
threadPool = new MyThreadPool();
}
}
return threadPool;
}
public static MyThreadPool getInstance(int poolSize){
if(threadPool == null){
synchronized (threadPool) {
if(threadPool == null)
threadPool = new MyThreadPool(poolSize);
}
}
return threadPool;
}
// 添加单个线程
public void execute(Runnable r){
synchronized (threadList) {
threadList.add(r);
threadList.notify();
}
}
// 批量添加线程
public void execute(Runnable[] rs){
synchronized (threadList) {
for(Runnable r:rs)
threadList.add(r);
threadList.notify();
}
}
// 获取线程池大小
public int getPoolSize(){
return poolSize;
}
// 工作线程,内部类
class MyConcreteThread extends Thread{
@Override
public void run(){
while(!isInterrupted()){
Runnable r = null;
synchronized (threadList) {
while(threadList.isEmpty()){
try{
threadList.wait(); // 该线程没有工作,让出队列对象
}catch(InterruptedException e){
e.printStackTrace();
}
}
if(!threadList.isEmpty())
r = threadList.remove(0); //取出第一个
}
if(r != null){
r.run(); // 在工作线程里调用run方法,相当于执行了该线程
}
r = null;
}
}
}
}
运行测试
单个添加
public class TestThreadPool {
public static void main(String arg[]){
Runnable r1 = new Runnable() {
public void run() {
System.out.println("Thread 1 run!");
}
};
Runnable r2 = new Runnable() {
public void run() {
System.out.println("Thread 2 run!");
}
};
MyThreadPool myThreadPool = MyThreadPool.getInstance();
myThreadPool.execute(r1);
myThreadPool.execute(r2);
try{
Thread.sleep(1000);
}catch (Exception e) {
e.printStackTrace();
}
System.out.println("Main thread end!");
}
}
结果
Thread 2 run!
Thread 1 run!
Main thread end!
或
Thread 1 run!
Thread 2 run!
Main thread end!
批量添加
public class TestThreadPool {
public static void main(String arg[]){
MyThreadPool myThreadPool = MyThreadPool.getInstance();
TestThread[] tests = new TestThread[10];
for(int i=0;i<10;i++){
tests[i] = new TestThread(i);
}
myThreadPool.execute(tests);
try{
Thread.sleep(1000);
}catch (Exception e) {
e.printStackTrace();
}
System.out.println("Main thread end!");
}
}
class TestThread extends Thread{
private int number;
public TestThread(int number){
this.number = number;
}
@Override
public void run(){
System.out.println("Thread "+ number + " run!");
}
}
运行
Thread 0 run!
Thread 1 run!
Thread 2 run!
Thread 3 run!
Thread 4 run!
Thread 5 run!
Thread 6 run!
Thread 7 run!
Thread 8 run!
Thread 9 run!
Main thread end!
jdk线程池
jdk已经为我们封装好了线程池,应用时可以放心使用,以下简单叙述下关于线程池的类。
Executor接口
线程池类都实现了该接口,该接口提供一个方法execute(),该方法接受一个Runnable类型的参数,表面上是执行该任务,实际上是将其提交到线程池中。
ExecutorService接口
继承Executor,在Executor的基础上多提供了几个接口。
ThreadPoolExecutor类
用户自定义配置的线程池类,实例出来即可以用。
Executor类
实现了ExecutorService。对于用户而言,有时候并不需要自己去配置线程池,该类则事先为我们创建好一个优秀的线程池,现在只需要去获取即可。
该类提供了几种配置的线程池,分别使用以下方法返回:
newCacheThreadPool():在有任务时才创建新线程,空闲线程被保留60秒;
newFixedThreadPool(int nThreads):线程池中包含固定数目的线程,空闲线程被一直保留。参数是设置线程的数目;
newSingleThreadExecutor():线程池中只有一个工作线程,该线程会依次执行任务;
newScheduledThreadPool(int corePoolSize):线程池能按时间计划执行任务,允许用户设定计划执行任务的时间。参数corePoolSize设定工作线程的最小数目,如果任务较多,线程池可以动态增加工作线程;
newSingleThreadScheduledExecutor():线程池只有一个工作线程,并能够按时间计划来执行任务。