线程池(实现线程的第四种方式)

线程池(实现线程的第四种方式)

1. 线程是什么

线程池其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作。无需反复创建线程而消耗过多的资源

2.使用线程池的优点

**推荐使用线程池来创建线程 **

**1.降低资源消耗:**通过重复利用已创建的线程降低线程创建和销毁带来的消耗

**2.提高响应速度:**当任务到达时,任务可以不需要等待线程创建就能立即执行

**3.提高线程的可管理性: **使用线程池可以统一进行线程分配,调度和监控。

JDK 1.5 之后内置了线程池

两个接口与两个类

3.两大接口

3.1 ExecutorService 普通线程池

提交任务:

void execute(Runnable )
<T> Futer<T> submit (Callable<T> tack || Runnable);

3.2 ScheduledExcutorService :定时线程池

ScheduledFuture<?> scheduleAtFixedRate(Runnbale command,long initiableDelay,long period,TimeUnit unit);

4. 线程池的工作原理

4.1 线程池工作步骤在这里插入图片描述

1)线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程。

2)线程池判断工作队列是否已满。如果工作没满,则将新提交的任务存储在这个工作队列中。反之进入下一流程。

3)线程池判断池中线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。

4.1.1 类

Executor框架最核心的类是ThreadPoolExecutor,它是线程池的工厂类,用来生产线程池

ThreadPoolExecutor: ExecutorService的子类

ThreadPoolExecutor(int corePoolSize,//基本线程池大小
                   int maximumPoolSize,//最大线程池大小
                   long keepAliveTime,//线程活动保持时间
                   TimeUnit unit,//线程活动保持时间单位
                   BlockingQueue<Runnable> workQueue,//阻塞队列
                   RejectExecutionHandler handler)//饱和策略

实例实现:创建一个线程池

ThreadPoolExecutor threadpoolexecutor = new ThreadPoolExecutor(3,5,2000,TimeUnit.MIlLISECONDS,new LinkBlockingDeque<Runnable>())
4.1.2 参数详解
  1. corePoolSize: 线程池基本大小

2) maximumPoolSize: 线程池最大数量:线程池允许创建的最大线程数,若使用无界的任务队列这个参数无意义

3)keepAliveTime:线程活动保持时间:线程池的工作线程空闲后,保持存活的时间。当任务多,且单个任务耗时短时。可以调大这个参数,提高线程的利用率。

4)TimeUnit线程活动保持时间的单位 ,可选单位:天(DAYS)、小时(HOURS)、分钟(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一秒)、纳秒(NANOSECONDS)

5)RejectedExecutionHandler:饱和策略 当队列和线程都满了,说明线程处于饱和状态,有以下四种策略:

a: AbortPoilcy,直接抛出异常

b: CallerRunPoilcy:只用调用者所在线程来运行任务。

c:DiscardOldestPoilcy:丢弃队列中最近的一个任务,并执行当前任务

d: DiscardPoilcy:不处理,丢弃掉

6)runnableTaskQueue:任务队列 :用于保存等待执行任务的阻塞队列,一个元素的插入操作,必须要等待同时有一个元素的删除操作,否则插入操作就一直阻塞(反之亦然),简而言之,就是插入和删除操作必须配对执行,常见四个阻塞队列:

1.ArrayBlockingQueue:基于数组的有界阻塞队列

2.LinkedBlockingQueue:基于链表的无界阻塞队列

3.内置线程池FixedThreadPoolSingleThreadPool都采取次队列,synchronousQueue:一个不存元素的无界阻塞队列

4.PriorityBlockingQueue:基于优先级的阻塞队列(优先级队列底层是二叉树);

当阻塞队列为无界的链表时,FututerTask:可以保证多线程场景下,任务只会被一个线程执行一次。其他线程不再执行此任务。

Future接口中的get()方法会阻塞当前线程,直到取到Callable的返回值。

4.1.3 方法

1)execute():用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功

  1. submit():用于提交需要返回值额度任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成。重载方法get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完
4.1.4 使用线程池提交任务

(1) 使用 execute()方法

package ThreadPoolTest;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Name:创建线程池,并提交任务使用executor()
 * @Author:ZYJ
 * @Date:2019-06-12-21:18
 * @Description:
 */
public class ThreadPoolDemo {
    public static void main(String[] args) {
        RunnableThread runnableThread = new RunnableThread();
        //创建线程池
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,5,2000,
                TimeUnit.MILLISECONDS,  new LinkedBlockingDeque<Runnable>());
        for(int i=0;i<5;i++){
            //使用execute()向线程提交任务
            threadPoolExecutor.execute(runnableThread);
        }
    }
}
//实现类实现Runnable接口,覆写run()接口
class RunnableThread implements  Runnable{
    @Override
    public void run() {
        for(int i=0;i<50;i++){
            System.out.println(Thread.currentThread().getName()+","+i);
        }
    }
}

(2) 使用submit()方法

package ThreadPoolDemo;
import javax.security.auth.callback.Callback;
import java.util.concurrent.*;

/**
 * @Name: 创建线程池 并使用submit()向线程提交任务
 * @Author:ZYJ
 * @Date:2019-06-17-19:48
 * @Description:
 */
public class ThreadPoolTest {
    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,
                5,2000, TimeUnit.MILLISECONDS,
                  new LinkedBlockingDeque<Runnable>());
        CallableThread callableThread = new CallableThread();
        for(int i=0;i<5;i++){
            //实例化 Future对象接收提交的返回值
            Future<String> future = threadPoolExecutor.submit(callableThread);
            try {
                //调用Future类的get方法得到返回值
                String str = future.get();
                System.out.println(str);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
}
//实现类实现CallableThread接口 覆写call类
class CallableThread implements Callable<String> {
    @Override
    public String call() throws Exception {
        for(int i=0;i<50;i++){
            System.out.println(Thread.currentThread().getName()+","+i);
        }
        return Thread.currentThread().getName()+"任务执行完毕!";
    }
}


4.2 关闭线程池

4.2.1 方法 :

1)shutdown():首先将线程池的状态设置为STOP,然后尝试停止所有正在执行或暂停任务的线程,并返回等待执行任务的列表

2)shutdownNow();只是将状态设置为SHUTDOWN状态,然后中断所有没有正在执行的任务的线程

threadPoolExecutor.shutdown();
4.2.2 原理

遍历线程池中的工作线程,逐个调用interrupt()方法来中断线程,所以无法响应中断的任务永远无法停止。

线程池中的线程被包装为Worker工作线程,具备可重复执行任务的能力

4.3 :合理配置线程池

配置核心池以及最大线程池数量:System.out.println(Runtime.getRuntime().availableProcessors());查看处理器数量。

CPU密集型任务(大数运算,) :NCPU+1;

I/O密集型任务:2*NCPU;

5. Executor框架

在java中使用线程来异步执行任务,java线程的创建与销毁需要一定的开销,如果我们为每一个任务来创建一个新的线程来执行,这些线程的创建与销毁将消耗大量的计算资源。同时为每一个任务创建一个新新线程来执行,这种策略可能会使处于高负荷状态的应用最终崩溃。

Java的线程既是工作单元,也是执行机制。从JDK1.5开始,把工作单元与执行机制分离开。工作单元包括Runnable和 Callable,而执行机制由Executor框架提供

5.1 Executor框架的两级调度模型

java线程被一对一映射为本地操作系统线程。java线程启动时,会创建一个本地操作系统线程。当该java线程终止时,这个操作系统线程会被回收。操作系统会调度所有线程并将他们分配给可用的CPU。

在上层,java多线程程序通常应用分为若干个任务,然后使用用户级的调度器(Executor框架)将这些任务映射为固定数量的线程;

在底层,操作系统内核将这些线程映射到硬件处理器上。

5.1 .四大内置线程池

Executors-线程池的工具(实现)类 他的核心类是ThreadPoolExecutor,通过工具类Executors可以创建以下类型的线程池

5.1.1固定大小线程池

运用场景:实用于负载较重的服务器(配置较低),来满足资源分配的要求。可重用

**注意:**线程池若不关闭,会一直开启。使用完的线程会自动把线程归还给线程池,可以继续使用

public static ExecutorService newFixedThreadPool(int nThreads)//静态方法,通过类名调用

该方法的返回接口的实现类对象,使用ExecutorService接口接收
核心线程池大小与最大线程池大小相同,不允许有空闲线程出现,

public static ExecutorService newFixedThreadThreadPool(int nThread){
    return new ThreadPoolExecutor(n Thread,nThread,TimeUnit.MILLISECONDS,
                                 new LinkedBlockingQueue<Runnable>());
    //使用无界队列
}

使用无界队列的影响

1)线程池中的线程数量不超过 corePoolSize(基本线程池大小)

  1. 这时最大线程池是一个无效参数

3)线程保持活动时间(keepAliveTime)是一个无效参数

4)运行中的FixedThreadThreadPool(未执行方法shutdown()或shutdownNow()不会拒绝任务)

使用步骤:

1,使用线程池的实现类Executors里提供的静态方法newFixedThreadPool()生产一个特定线程数量的线程池。

2,创建一个类实现Runnable接口,覆写run()方法,设置线程任务。

3,调用ExecutorService中的方法submit()方法传递线程任务,开启线程、执行main()方法

4,调用Executor中的方法shutdown()销毁线程池,不建议使用

实例 : FixedThreadPool的使用

package FixedThreadPoolTest;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Name: FixedThreadPool的使用
 * @Author:ZYJ
 * @Date:2019-06-18-19:38
 * @Description:
 */
public class FixedThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        RunnableImpl runnable = new RunnableImpl();

        for(int i=0;i<5;i++){
            executorService.submit(runnable);
        }
        executorService.shutdown();
    }
}
class RunnableImpl implements Runnable{
    @Override
    public void run() {
       for(int j=0;j<10;j++){
           System.out.println(Thread.currentThread().getName()+"执行任务");
       }
    }
}
5.1.2 单线程池-只有一个线程

运用场景:当多线程场景下,需要让人物串行执行时采用

public static ExecutorService newSingleThreadExecutor()
5.1.3 缓存线程池 (无大小限制的线程池):

应用场景:适用于负载较轻的服务器,或执行很多短期的异步任务。

public static ExecutorService newCachedThreadPool()

当任务提交速度大于线程执行速度,会不断创建新线程,有可能无限创建线程将内存写满。

当线程的执行速度大于任务提交速度,只会创建若干个有限线程。

5.1.4 定时调度池
public static ExecutorService newScheduledThreadPool()
executorService.schedule(Runnable command,long delay,TimeUnit unit);

延迟delay个时间单元后创建nThreads个线程执行command任务。

scheduleAtFixedRate(Runnbale command,long initialDelay,long period,TimeUnit unit)s

延迟delay个单元后每隔period单元时间就执行一次command任务

5.2 扩展问题

JDK1.7 任务分工Fork/Join 单机版的MapReduce

JDK1.8 StampedLock 性能优于ReentrantReadWriteLock

JUC:并发工具类

CountDownLatch 闭锁

CyclicBarrier 循环栅栏

Semophore 信号量

Exchanger 交换器

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值