标签:
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<Runnable> 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<Runnable>()); }
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<Runnable>()); }通过它的实例方法可以看出它的核心线程数是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<Runnable>())); }通过代码可以看出,这种线程池,就是FixedThreadPool但是实例化方法的参数是1的嘛。
public abstract class PriorityRunnable implements Runnable, Comparable<PriorityRunnable> { 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<Runnable>()); 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<Runnable> 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个线程可以理解为几乎是在同一时间开启的,当线程开始的时候会尝试申请信号量,如果申请成功就开始模拟一个耗时操作,在操作完成后,再释放一次信号量,我们来看一看这几个线程的运行情况
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context="com.lanou.chenfengyao.myapplication.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <Button android:id="@+id/btn_FIFO" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="FIFO"/> <Button android:id="@+id/btn_LIFO" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="LIFO"/> </LinearLayout> <TextView android:id="@+id/main_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" /> </LinearLayout>这个不用多说,就是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<Runnable> 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<Runnable>()); 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();来获得
标签:
原文地址:http://blog.csdn.net/cfy137000/article/details/51422316