线程池的使用

参考:

Java四种线程池的使用

Java多线程总结之线程安全队列Queue

Android 线程池来管理线程

java&android线程池-Executor框架之ThreadPoolExcutor&ScheduledThreadPoolExecutor浅析(多线程编程之三)

并行和并发区别

1、并行是指两者同时执行一件事,比如赛跑,两个人都在不停的往前跑;

2、并发是指资源有限的情况下,两者交替轮流使用资源,比如一段路(单核CPU资源)同时只能过一个人,A走一段后,让给B,B用完继续给A ,交替使用,目的是提高效率

new Thread的弊端如下:

a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。

相比new Thread,Java提供的四种线程池的好处在于:

a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。

线程池的作用:

线程池作用就是限制系统中执行线程的数量。

根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排 队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池 中有等待的工作线程,就可以开始运行了;否则进入等待队列。

为什么要用线程池:

1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

ThreadPoolExecutor相关类的关系:


public interface Executor {}
    //真正的线程池接口。
    public interface ExecutorService extends Executor {}
        //能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。
        public interface ScheduledExecutorService extends ExecutorService {}
        
        public abstract class AbstractExecutorService implements ExecutorService {}
            //ExecutorService的默认实现。
            public class ThreadPoolExecutor extends AbstractExecutorService {}
                //继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。
                public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {

            private static class DelegatedExecutorService extends AbstractExecutorService {
                private static class FinalizableDelegatedExecutorService extends DelegatedExecutorService {

在这里插入图片描述

ThreadPoolExecutor的构造方法:

    public ThreadPoolExecutor(
                int corePoolSize,//核心线程数,能够同时执行的任务数量;
                int maximumPoolSize,//最大线程数,达到最大任务数新任务将会被阻塞;
                long keepAliveTime,//线程没有任务执行时最多保持多久时间会终止,然后线程池的数目维持在corePoolSize 大小,在corePore<*<maxPoolSize情况下有用
                TimeUnit unit,//时间单位;
                BlockingQueue<Runnable> workQueue,//阻塞队列用来存储等待执行的任务,当前对线程的需求超过了corePoolSize大小才会放在这里
                ThreadFactory threadFactory,//创建线程的工厂,使用系统默认的类,主要用来创建线程,比如可以指定线程的名字;
                RejectedExecutionHandler handler) {//当任务数超过maximumPoolSize时,对任务的处理策略,默认策略是拒绝添加;
        
        if (corePoolSize < 0 ||
                maximumPoolSize <= 0 ||
                maximumPoolSize < corePoolSize ||
                keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
  1. 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
  2. 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
  1. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
  2. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。
  3. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务;
  4. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常,告诉调用者“我不能再接受任务了”。
  1. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
  2. 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到corePoolSize 的大小。

这样的过程说明,并不是先加入任务就一定会先执行。
假设队列大小为 10,corePoolSize 为 3,maximumPoolSize 为 6,那么当加入 20 个任务时,执行的顺序就是这样的:首先执行任务 1、2、3,然后任务 4~13 被放入队列。这时候队列满了,任务 14、15、16 会被马上执行,而任务 17~20 则会抛出异常。最终顺序是:1、2、3、14、15、16、4、5、6、7、8、9、10、11、12、13。

处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,
如果三者都满了,使用handler处理被拒绝的任务。

一、currentSize < corePoolSize
空闲线程
新线程(优先)

二、corePoolSize < currentSize < maximumPoolSize
workQueue未满时,添加进来的任务会被安排到workQueue中等待执行。 
workQueue已满时,会立即开启一个非核心线程来执行任务。 
	空闲线程(优先):超过一定的时间(keepAliveTime)时会被回收
	新线程

三、currentSize > maximumPoolSize
调用handler默认抛出RejectExecutionExpection异常。

FixedThreadPool

是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。
但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(nThreads, nThreads,
    //线程池中均为核心线程,最多线程数nThreads
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>(),
                                  //单向链表实现的阻塞队列,先进先出的顺序,可无限排队
                                  threadFactory);
}

CachedThreadPool

特点就是在线程池空闲时,即线程池中没有可运行任务时,它会释放工作线程,从而释放工作线程所占用的资源。
但是,但当出现新任务时,又要创建一新的工作线程,又要一定的系统开销。
并且,在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。

public static ExecutorService newCachedThreadPool() {
       return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
       //线程池中均为非核心线程,而且可无限创建线程
                                     60L, TimeUnit.SECONDS,
                                     new SynchronousQueue<Runnable>());
                                     //如果现有线程无法接收任务,将会创建新线程来执行.
   }

参考:Java线程池ThreadPoolExecutor详解

四种线程池:

在这里插入图片描述

//Java通过Executors提供四种线程池,分别为:

//newCachedThreadPool创建一个可缓存线程池,如果超过存活时间不活动,其会自动被终止。最大线程数不限
new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>())
                                 
//newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>())
                                 
//newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。最大线程数不限
super(corePoolSize, Integer.MAX_VALUE , DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue())
         
//newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())

队列


//    队列:先进先出
    public interface BlockingQueue<E> extends Queue<E> {
    public interface TransferQueue<E> extends BlockingQueue<E> {
    public abstract class AbstractQueue<E> extends AbstractCollection<E> implements Queue<E> {
          /*阻塞队列,线程安全*/
          public class SynchronousQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, Serializable {
              //一个不存储元素的阻塞队列。
          }
          public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, Serializable {
              //一个由数组结构组成的有界阻塞队列。
          }
          public class PriorityBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, Serializable {
              //一个支持优先级排序的无界阻塞队列。
          }
          public class LinkedBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, Serializable {
              //一个由链表结构组成的有界阻塞队列。
          }
          public class LinkedTransferQueue<E> extends AbstractQueue<E> implements TransferQueue<E>, Serializable {
              //一个由链表结构组成的无界阻塞队列。
          }
          public class LinkedBlockingDeque<E> extends AbstractQueue<E> implements BlockingDeque<E>, Serializable {
              //一个由链表结构组成的双向阻塞队列
          }
          public class DelayQueue<E extends Delayed> extends AbstractQueue<E> implements BlockingQueue<E> {
              //一个使用优先级队列实现的无界阻塞队列
          }

          /*非阻塞队列*/
          public class ConcurrentLinkedQueue<E> extends AbstractQueue<E> implements Queue<E>, Serializable {
              //并发队列,基于链接节点的、无界的、线程安全。
          }

线程池提交之execute和submit区别:

向线程池提交任务有两种方式:

      Executor.execute(Runnable command);

      ExecutorService.submit(Callable<T> task);

参考:线程池提交之execute和submit区别

1.对返回值的处理不同

execute方法不关心返回值。

submit方法有返回值,Future.

2.对异常的处理不同

excute方法会抛出异常。

sumbit方法不会抛出异常。除非你调用Future.get()

线程池工具类:

/**
 * 线程池工具类
 * <p>
 * 根据CPU数量
 */
public class ThreadPoolManager {

    private ThreadPoolExecutor executor;
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;//9
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;//17
    private static final int KEEP_ALIVE = 1;

    private static ThreadPoolManager mInstance;

    public static ThreadPoolManager getInstance() {
        if (mInstance == null) {
            synchronized (ThreadPoolManager.class) {
                if (mInstance == null) {
                    mInstance = new ThreadPoolManager();
                }
            }
        }
        return mInstance;
    }

    private ThreadPoolManager() {
        executor = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAXIMUM_POOL_SIZE,
                KEEP_ALIVE,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }

    /**
     * 执行任务
     */
    public void execute(Runnable runnable) {
        if (runnable == null) return;

        executor.execute(runnable);
    }

    /**
     * 移除任务
     * <p>
     * 注意:此方法起作用有一个必要的前提,就是这个任务还没有开始执行,如果已经开始执行了,就停止不了该任务了,这个方法就不会起作用
     */
    public void remove(Runnable runnable) {
        if (runnable == null) return;

        executor.remove(runnable);
    }

    /**
     * 提交任务
     */
    public Future<?> submit(Runnable runnable) {
        if (runnable == null) return null;

        return executor.submit(runnable);
    }
}

调用:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        for (int i = 0; i < 9; i++) {
            ThreadPoolManager.getInstance().execute(new Runnable() {
                @Override
                public void run() {
                    SystemClock.sleep(5000); //模拟延时执行的时间
                }
            });
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值