new Thread(new Runnable() {
@Override
public void run() {
//耗时代码
}
}).start();
这种方式的线程随处可见,但是这种方式的写法是存在一定问题的,我们知道,在操作系统中,线程是操作系统调度的最小单元,同时线程又不能无限制的产生,并且线程的创建和销毁都会有资源的开销,同时当线程频繁的创建或者销毁的时候,还会让GC频繁的运行,造成程序的卡顿,例如当我们需要网络请求的时候,一定是讲网络请求的代码放到子线程中去运行的,同时如果是ListView中图片的画,采用传统的new Thread的形式,会在ListView滑动的时候,一下开数十个子线程,程序就会卡顿起来;或者当我们进行下载的时候,通常会指定下载的优先级,优先级高的优先下载,优先级低的会暂停排队,这种需求传统的Thread也是做不到的。那么这就需要用到线程池了。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
我们来看看这几个参数的意义:(以下的所说的任务可以理解为实现Runnable接口的对象)
threadPoolExecutor.allowCoreThreadTimeOut(true);
值得注意的是,并不是说只有核心线程才能去执行任务,而是核心线程是最稳定的线程,在默认状态下,它们不会销毁,这样在新的任务需要执行的时候,就会很节省时间,所以核心线程数只需要保证大于0就可以了。
Thread newThread(Runnable r);
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
使用执行线程池的线程本身来运行该任务,此策略提供简单的反馈控制机制,能减缓新任务的提交速度。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException();
}
这种策略直接抛出异常,丢弃任务
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
这种策略也是丢弃任务,不同的是,它并不会抛出异常
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 1; i <= 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("线程:" + threadName + ",任务:" + index);
try {
//模拟耗时
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
我们创建了一个最大线程和核心线程都是3的一个线程池,然后向其中循环添加10个任务,每一个人只打印当前线程的名字,来看一下效果
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
通过它的实例方法可以看出它的核心线程数是0也就是说该线程池并没有核心线程,而它的最大线程数是int类型的的上限,那么我们可以理解为该线程池的最大线程数是没有上限的,也就是说可以无限的创建线程。那么当新任务向线程池中提交的时候,如果有空闲线程,就会把任务放到空闲线程中去,如果没有空闲线程,就会开启一个新的线程来执行此任务,而它的队列SynchronousQueue是一个特殊的队列,在多数情况下,我们可以把它简单的理解为一个无法插入的队列,我们会在之后详细说明的。
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 1; i <= 10; i++) {
final int index = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("线程:" + threadName + ",任务:" + index);
try {
long time = index * 500;
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
我们这次是在每一次添加的时候都会停止1s的时间,来看看CachedThreadPool的运行情况,并且,每一个任务所执行的时间也不一样,效果如下
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
可以看出它的核心线程数是固定的,而最大线程数也是int类型的上限,理解为没有限制,它与之前的线程池相区别的就是它的任务队列,DelayedWorkQueue能让任务周期性的执行,也就是说该线程池可以周期性的执行任务。
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
//延迟2秒后执行该任务
System.out.println("任务开始");
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("任务:" + threadName );
}
}, 3, TimeUnit.SECONDS);
我们首先模拟延迟执行任务的代码,在我们提交任务之前先打印一次任务开始,在提交任务的时候,去设置延迟时间为3s,运行一下看看效果
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
//延迟2秒后执行该任务
System.out.println("任务开始");
//延迟4秒后,每隔1秒执行一次该任务
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("执行任务");
}
}, 4, 1, TimeUnit.SECONDS);
我们设置了在提交任务时,需要延迟4s才会第一次执行,同时在任务执行完毕后每隔1s又会重复的执行一次该任务,看一下效果
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
通过代码可以看出,这种线程池,就是FixedThreadPool但是实例化方法的参数是1的嘛。
public abstract class PriorityRunnable implements Runnable, Comparable {
private int priority;
public PriorityRunnable(int priority) {
if (priority < 0) {
throw new IllegalArgumentException();
}
this.priority = priority;
}
@Override
public int compareTo(PriorityRunnable another) {
return another.getPriority() - priority;
}
public int getPriority() {
return priority;
}
}
我们的抽象类最基本的需要实现Runnable接口才能被提交到线程池中,同时我们需要再实现Compareable接口,来告诉队列到底谁大谁小,这里我们自己写了一个int类型的变量代表每一个任务的优先级,然后复写compareTo方法来写我们的比较条件,这里要注意的是,当我们自己去写的时候,不一定非要指定一个int类型的变量,也可以是其他的例如String等的,只要实现了Comparable接口就可以了。接下来我们就可以使用这个任务了,代码如下
ExecutorService priorityThreadPool = new ThreadPoolExecutor(3, 3, 0L, TimeUnit.MILLISECONDS, new PriorityBlockingQueue());
for (int i = 1; i <= 10; i++) {
final int priority = i;
priorityThreadPool.execute(new PriorityRunnable(priority) {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("线程:" + threadName + ",正在执行优先级为:" + priority + "的任务");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
我们创建了一个核心线程为3的使用优先级队列的线程池,向线程池中添加10个优先级不同的任务,来看看效果
public class MyThreadPool extends ThreadPoolExecutor {
public MyThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
//在执行任务之前
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
//在执行任务之后
}
@Override
protected void terminated() {
super.terminated();
//线程池关闭
}
}
类似这样,就可以在线程池执行任务之前和之后,都很方便的加上我们的功能
//只有一个位置的信号量,相当于停车场只有一个位置
final Semaphore semp = new Semaphore(2);
for(int i = 0;i<4;i++){
new Thread(new Runnable() {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
try {
//申请信号量
semp.acquire();
System.out.println(threadName+"申请到了信号量");
Thread.sleep(2000);
//释放信号量
semp.release();
System.out.println(threadName+"释放了了信号量");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
首先我们定义了一个只有2个位置的信号量,然后循环开启了4个线程,而这4个线程可以理解为几乎是在同一时间开启的,当线程开始的时候会尝试申请信号量,如果申请成功就开始模拟一个耗时操作,在操作完成后,再释放一次信号量,我们来看一看这几个线程的运行情况
这个不用多说,就是2个Button来控制线程池的,一个TextView用来显示当前的线程的
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button FIFOBtn, LIFOBtn;
private TextView mainTv;
private MyThreadPool myThreadPool;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FIFOBtn = (Button) findViewById(R.id.btn_FIFO);
LIFOBtn = (Button) findViewById(R.id.btn_LIFO);
mainTv = (TextView) findViewById(R.id.main_text);
FIFOBtn.setOnClickListener(this);
LIFOBtn.setOnClickListener(this);
handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
//设置给TextView;
mainTv.setText("任务:"+msg.what);
return false;
}
});
myThreadPool = new MyThreadPool(1);
for (int i = 0; i < 100; i++) {
final int index = i;
myThreadPool.execute(new Runnable() {
@Override
public void run() {
handler.sendEmptyMessage(index);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_FIFO:
myThreadPool.setWay(MyThreadPool.OutWay.FIFO);
break;
case R.id.btn_LIFO:
myThreadPool.setWay(MyThreadPool.OutWay.LIFO);
break;
}
}
}
我直接在onCreate方法里创建了我自定义的一个线程池,然后向里面添加了100个任务,而这个线程池的核心线程和最大线程我都设置成了1个,每一个任务也就是把自己任务号通过handler发送给主线程,然后显示到TextView上,在按钮的监听里,调用自定义线程池的setWay方法,把FIFO还是LIFO的信息设置上,那么关键的代码就在我们自定义的线程池里了,我们来看一下:
package com.lanou.chenfengyao.myapplication;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* Created by ChenFengYao on 16/5/17.
*/
public class MyThreadPool extends ThreadPoolExecutor {
private volatile Semaphore semaphore;
private List runnableList;
private LoopThread loopThread;
private boolean flag;
//两种策略,先进先出和先进后出
enum OutWay {
FIFO, LIFO
}
private OutWay outWay;
public MyThreadPool(int corePoolSize) {
super(corePoolSize, corePoolSize, 0l, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
semaphore = new Semaphore(corePoolSize);
runnableList = new LinkedList<>();
flag = true;
outWay = OutWay.FIFO;//默认是先进先出
loopThread = new LoopThread();
loopThread.start();
}
//提交任务的方法
@Override
public synchronized void execute(Runnable command) {
//所有来的任务是提交到我们自己的任务队列中
runnableList.add(command);
if (runnableList.size() < 2) {
//如果这是队列中的第一个任务,那么就去唤醒轮询线程
synchronized (loopThread) {
loopThread.notify();
}
}
}
//设置是FIFO/LIFO
public void setWay(OutWay outWay) {
this.outWay = outWay;
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
//任务完成释放信号量
semaphore.release();
}
@Override
protected void terminated() {
super.terminated();
flag = false;//轮询线程关闭
}
class LoopThread extends Thread {
@Override
public void run() {
super.run();
while (flag) {
if (runnableList.size() == 0) {
try {
//如果没有任务,轮询线程就等待
synchronized (this) {
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
try {
//请求信号量
semaphore.acquire();
int index = runnableList.size();
switch (outWay) {
case FIFO:
//先进先出
index = 0;
break;
case LIFO:
//先进后出
index = runnableList.size() - 1;
break;
}
//调用父类的添加方法,将任务添加到线程池中
MyThreadPool.super.execute(runnableList.get(index));
runnableList.remove(index);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
可以看到,这个自定义的线程池的核心线程和最大线程都是一样的,通过构造方法传进来的,这里是1,同时,我们将我们信号量的数值也设置为核心线程数,并且我们在内部有一个List用来存放我们提交的任务,我将线程池父类的execute方法复写了,这里不再向线程池内提交,而是存放到我们自己的RunnableList里。
Runtime.getRuntime().availableProcessors();
来获得