线程池研发学习笔记

article/2025/9/10 8:42:39

线程池研发

在这里插入图片描述

线程池

线程池使用

ThreadPoolExecutor部分源码

public ThreadPoolExecutor(int corePoolSize, //核心线程数量int maximumPoolSize,//     最大线程数long keepAliveTime, //       最大空闲时间TimeUnit unit,         //        时间单位BlockingQueue<Runnable> workQueue,   //   任务队列ThreadFactory threadFactory,    // 线程工厂RejectedExecutionHandler handler  //  饱和处理机制) 
{ ... }
我们可以通过下面的场景理解ThreadPoolExecutor中的各个参数;
a客户(任务)去银行(线程池)办理业务,但银行刚开始营业,窗口服务员还未就位(相当于线程池中初始线程数量为0),
于是经理(线程池管理者)就安排1号工作人员(创建1号线程执行任务)接待a客户(创建线程);
在a客户业务还没办完时,b客户(任务)又来了,于是经理(线程池管理者)就安排2号工作人员(创建2号线程执行任务)接待b客户(又创建了一个新的线程);假设该银行总共就2个窗口(核心线程数量是2);
紧接着在a,b客户都没有结束的情况下c客户来了,于是经理(线程池管理者)就安排c客户先坐到银行大厅的座位上(空位相当于是任务队列)等候,
并告知他: 如果1、2号工作人员空出,c就可以前去办理业务;
此时d客户又到了银行,(工作人员都在忙,大厅座位也满了)于是经理赶紧安排临时工(新创建的线程)在大堂站着,手持pad设备给d客户办理业务;
假如前面的业务都没有结束的时候e客户又来了,此时正式工作人员都上了,临时工也上了,座位也满了(临时工加正式员工的总数量就是最大线程数),
于是经理只能按《超出银行最大接待能力处理办法》(饱和处理机制)拒接接待e客户;
最后,进来办业务的人少了,大厅的临时工空闲时间也超过了1个小时(最大空闲时间),经理就会让这部分空闲的员工人下班.(销毁线程)
但是为了保证银行银行正常工作(有一个allowCoreThreadTimeout变量控制是否允许销毁核心线程,默认false),即使正式工闲着,也不得提前下班,所以1、2号工作人员继续待着(池内保持核心线程数量);

线程池工作流程总结示意图

image-20201211135639232

自定义线程池-参数设计分析

通过观察Java中的内置线程池参数讲解和线程池工作流程总结,我们不难发现,要设计一个好的线程池,就必须合理的设置线程池的4个参数;那到底该如何合理的设计4个参数的值呢?我们一起往下看.
4个参数的设计:
1:核心线程数(corePoolSize)核心线程数的设计需要依据任务的处理时间和每秒产生的任务数量来确定,例如:执行一个任务需要0.1秒,系统百分之80的时间每秒都会产生100个任务,那么要想在1秒内处理完这100个任务,就需要10个线程,此时我们就可以设计核心线程数为10;当然实际情况不可能这么平均,所以我们一般按照8020原则设计即可,既按照百分之80的情况设计核心线程数,剩下的百分之20可以利用最大线程数处理;
2:任务队列长度(workQueue)任务队列长度一般设计为:核心线程数/单个任务执行时间*2即可;例如上面的场景中,核心线程数设计为10,单个任务执行时间为0.1秒,则队列长度可以设计为200;
3:最大线程数(maximumPoolSize)最大线程数的设计除了需要参照核心线程数的条件外,还需要参照系统每秒产生的最大任务数决定:例如:上述环境中,如果系统每秒最大产生的任务是1000个,那么,最大线程数=(最大任务数-任务队列长度)*单个任务执行时间;既: 最大线程数=(1000-200)*0.1=80个;
4:最大空闲时间(keepAliveTime)这个参数的设计完全参考系统运行环境和硬件压力设定,没有固定的参考值,用户可以根据经验和系统产生任务的时间间隔合理设置一个值即可;
提示:上面4个参数的设置只是一般的设计原则,并不是固定的,用户也可以根据实际情况灵活调整!

自定义线程池

  • java5之前,java没有提供线程池,需要我们自建线程池

  • java内置线程池原理剖析

    • 剖析ThreadPoolExecutor类的构造方法
  • 自定义线程池

    • 属性

      • 1:核心线程数量

      • 2:最大线程数量

      • 3:线程最大空闲时间

      • 需要介绍如何合理配置线程池的大小

        • 1:从自身硬件环境分析
        • 2:从自身项目应用访问环境分析
    • 功能

      • 1:执行Runnable任务
      • 2:执行Callable任务
      • 3:销毁线程
    • 实现步骤

      • 1:自定义的线程池需要包含上面的属性和功能
      • 2:可以提供多种形式的构造方法,用于初始化线程池
      • 3:可以利用队列结构的集合,保存一些线程,以用于完成即将执行的任务
      • 4:执行任务和销毁线程,实际上都是在操作队列集合中线程完成需求;

例子

MyTask

package com.liuhm.demo01;
/*需求:自定义线程池练习,这是任务类,需要实现Runnable;包含任务编号,每一个任务执行时间设计为0.2秒*/
public class MyTask implements Runnable{private int id;//由于run方法是重写接口中的方法,因此id这个属性初始化可以利用构造方法完成public MyTask(int id) {this.id = id;}@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println("线程:"+name+" 即将执行任务:"+id);try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程:"+name+" 完成了任务:"+id);}@Overridepublic String toString() {return "MyTask{" +"id=" + id +'}';}
}

MyWork

package com.liuhm.demo01;import java.util.List;/*需求:编写一个线程类,需要继承Thread类,设计一个属性,用于保存线程的名字;设计一个集合,用于保存所有的任务;*/
public class MyWorker extends Thread{private String name;//保存线程的名字private List<Runnable> tasks;//利用构造方法,给成员变量赋值public MyWorker(String name, List<Runnable> tasks) {super(name);this.tasks = tasks;}@Overridepublic void run() {//判断集合中是否有任务,只要有,就一直执行任务while (tasks.size()>0){Runnable r = tasks.remove(0);r.run();}}
}

MyThreadPool

package com.liuhm.demo01;import java.util.Collections;
import java.util.LinkedList;
import java.util.List;/*这是自定义的线程池类;成员变量:1:任务队列   集合  需要控制线程安全问题2:当前线程数量3:核心线程数量4:最大线程数量5:任务队列的长度成员方法1:提交任务;将任务添加到集合中,需要判断是否超出了任务总长度2:执行任务;判断当前线程的数量,决定创建核心线程还是非核心线程*/
public class MyThreadPool {// 1:任务队列   集合  需要控制线程安全问题private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<>());//2:当前线程数量private int num;//3:核心线程数量private int corePoolSize;//4:最大线程数量private int maxSize;//5:任务队列的长度private int workSize;public MyThreadPool(int corePoolSize, int maxSize, int workSize) {this.corePoolSize = corePoolSize;this.maxSize = maxSize;this.workSize = workSize;}//1:提交任务;public void submit(Runnable r){//判断当前集合中任务的数量,是否超出了最大任务数量if(tasks.size()>=workSize){System.out.println("任务:"+r+"被丢弃了...");}else {tasks.add(r);//执行任务execTask(r);}}//2:执行任务;private void execTask(Runnable r) {//判断当前线程池中的线程总数量,是否超出了核心数,if(num < corePoolSize){new MyWorker("核心线程:"+num,tasks).start();num++;}else if(num < maxSize){new MyWorker("非核心线程:"+num,tasks).start();num++;}else {System.out.println("任务:"+r+" 被缓存了...");}}}

MyTest

package com.liuhm.demo01;
/*测试类:1: 创建线程池类对象;2: 提交多个任务*/
public class MyTest {public static void main(String[] args) {//1:创建线程池类对象;MyThreadPool pool = new MyThreadPool(2,4,20);//2: 提交多个任务for (int i = 0; i <30 ; i++) {//3:创建任务对象,并提交给线程池MyTask my = new MyTask(i);pool.submit(my);}}
}

java内置线程池

  • Executors

    • 概念,作用介绍

      • 工厂类主要用于创建线程池
    • 静态方法

      • newCachedThreadPool()
      • newFixedThreadPool(int nThreads)
      • newSingleThreadExecutor()
      • newScheduledThreadPool(int corePoolSize)
      • newWorkStealingPool(int parallelism)
      • newWorkStealingPool()
  • ExecutorService

    • 概念介绍

    • 获取方式

      • 不能使用构造方法,需要使用工厂类的静态方法
    • 成员方法

      • submit(Runnable task)
      • submit(Runnable task, T result)
      • shutdown()
      • shutdownNow()
ExecutorService接口是java内置的线程池接口,通过学习接口中的方法,可以快速的掌握java内置线程池的基本使用
常用方法:void shutdown()   启动一次顺序关闭,执行以前提交的任务,但不接受新任务。 List<Runnable> shutdownNow() 停止所有正在执行的任务,暂停处理正在等待的任务,并返回等待执行的任务列表。 
<T> Future<T> submit(Callable<T> task)  执行带返回值的任务,返回一个Future对象。 Future<?> submit(Runnable task)  执行 Runnable 任务,并返回一个表示该任务的 Future。 
<T> Future<T> submit(Runnable task, T result)  执行 Runnable 任务,并返回一个表示该任务的 Future。 思考:
既然ExecutorService是一个接口,接口是无法直接创建对象的,那么我们该如何获取ExecutorService的对象呢?
获取ExecutorService可以利用JDK中的Executors 类中的静态方法,常用获取方式如下:
static ExecutorService newCachedThreadPool() 创建一个默认的线程池对象,里面的线程可重用,且在第一次使用时才创建 
static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) 线程池中的所有线程都使用ThreadFactory来创建,这样的线程无需手动启动,自动执行; 
static ExecutorService newFixedThreadPool(int nThreads)   创建一个可重用固定线程数的线程池 
static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) 创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建。 
static ExecutorService newSingleThreadExecutor() 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 
static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) 创建一个使用单个 worker 线程的 Executor,且线程池中的所有线程都使用ThreadFactory来创建。 
  • ScheduledExecutorService

    • 概念介绍

      • ExecutorService的子接口
    • 获取方式

      • 不能使用构造方法,需要使用工厂类的静态方法
    • 成员方法

      • schedule(Callable callable, long delay, TimeUnit unit)
      • schedule(Runnable command, long delay, TimeUnit unit)
      • scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
      • scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
ScheduledExecutorService是ExecutorService的子接口,具备了延迟运行或定期执行任务的能力,
常用获取方式如下:
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)创建一个可重用固定线程数的线程池且允许延迟运行或定期执行任务;
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建,且允许延迟运行或定期执行任务; 
static ScheduledExecutorService newSingleThreadScheduledExecutor() 创建一个单线程执行程序,它允许在给定延迟后运行命令或者定期地执行。 
static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) 创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。 
ScheduledExecutorService常用方法如下:
<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) 延迟时间单位是unit,数量是delay的时间后执行callable。 ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) 延迟时间单位是unit,数量是delay的时间后执行command。  ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) 延迟时间单位是unit,数量是initialDelay的时间后,每间隔period时间重复执行一次command。 ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) 创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。 
newCachedThreadPool
例子
package com.liuhm.demo02;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;/*练习Executors获取ExecutorService,然后调用方法,提交任务;*/
public class MyTest01 {public static void main(String[] args) {//test1();test2();}//练习newCachedThreadPool方法private static void test1() {//1:使用工厂类获取线程池对象ExecutorService es = Executors.newCachedThreadPool();//2:提交任务;for (int i = 1; i <=10 ; i++) {es.submit(new MyRunnable(i));}}private static void test2() {//1:使用工厂类获取线程池对象ExecutorService es = Executors.newCachedThreadPool(new ThreadFactory() {int n=1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义的线程名称"+n++);}});//2:提交任务;for (int i = 1; i <=10 ; i++) {es.submit(new MyRunnable(i));}}
}
/*任务类,包含一个任务编号,在任务中,打印出是哪一个线程正在执行任务*/
class MyRunnable implements Runnable{private  int id;public MyRunnable(int id) {this.id = id;}@Overridepublic void run() {//获取线程的名称,打印一句话String name = Thread.currentThread().getName();System.out.println(name+"执行了任务..."+id);}
}
newFixedThreadPool
例子
package com.liuhm.demo02;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;/*练习Executors获取ExecutorService,然后调用方法,提交任务;*/
public class MyTest02 {public static void main(String[] args) {//test1();test2();}//练习方法newFixedThreadPoolprivate static void test1() {//1:使用工厂类获取线程池对象ExecutorService es = Executors.newFixedThreadPool(3);//2:提交任务;for (int i = 1; i <=10 ; i++) {es.submit(new MyRunnable2(i));}}private static void test2() {//1:使用工厂类获取线程池对象ExecutorService es = Executors.newFixedThreadPool(3,new ThreadFactory() {int n=1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义的线程名称"+n++);}});//2:提交任务;for (int i = 1; i <=10 ; i++) {es.submit(new MyRunnable2(i));}}
}/*任务类,包含一个任务编号,在任务中,打印出是哪一个线程正在执行任务*/
class MyRunnable2 implements Runnable{private  int id;public MyRunnable2(int id) {this.id = id;}@Overridepublic void run() {//获取线程的名称,打印一句话String name = Thread.currentThread().getName();System.out.println(name+"执行了任务..."+id);}
}
newSingleThreadExecutor
例子
package com.liuhm.demo02;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;/*练习Executors获取ExecutorService,然后调用方法,提交任务;*/
public class MyTest03 {public static void main(String[] args) {//test1();test2();}//练习方法newFixedThreadPoolprivate static void test1() {//1:使用工厂类获取线程池对象ExecutorService es = Executors.newSingleThreadExecutor();//2:提交任务;for (int i = 1; i <=10 ; i++) {es.submit(new MyRunnable3(i));}}private static void test2() {//1:使用工厂类获取线程池对象ExecutorService es = Executors.newSingleThreadExecutor(new ThreadFactory() {int n=1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义的线程名称"+n++);}});//2:提交任务;for (int i = 1; i <=10 ; i++) {es.submit(new MyRunnable3(i));}}
}/*任务类,包含一个任务编号,在任务中,打印出是哪一个线程正在执行任务*/
class MyRunnable3 implements Runnable{private  int id;public MyRunnable3(int id) {this.id = id;}@Overridepublic void run() {//获取线程的名称,打印一句话String name = Thread.currentThread().getName();System.out.println(name+"执行了任务..."+id);}
}
newSingleThreadExecutor
例子
package com.liuhm.demo02;import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;/*练习Executors获取ExecutorService,测试关闭线程池的方法;*/
public class MyTest04 {public static void main(String[] args) {//test1();test2();}//练习方法newFixedThreadPoolprivate static void test1() {//1:使用工厂类获取线程池对象ExecutorService es = Executors.newSingleThreadExecutor();//2:提交任务;for (int i = 1; i <=10 ; i++) {es.submit(new MyRunnable4(i));}//3:关闭线程池,仅仅是不再接受新的任务,以前的任务还会继续执行es.shutdown();//es.submit(new MyRunnable4(888));//不能再提交新的任务了}private static void test2() {//1:使用工厂类获取线程池对象ExecutorService es = Executors.newSingleThreadExecutor(new ThreadFactory() {int n=1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义的线程名称"+n++);}});//2:提交任务;for (int i = 1; i <=10 ; i++) {es.submit(new MyRunnable4(i));}//3:立刻关闭线程池,如果线程池中还有缓存的任务,没有执行,则取消执行,并返回这些任务List<Runnable> list = es.shutdownNow();System.out.println(list);}
}/*任务类,包含一个任务编号,在任务中,打印出是哪一个线程正在执行任务*/
class MyRunnable4 implements Runnable{private  int id;public MyRunnable4(int id) {this.id = id;}@Overridepublic void run() {//获取线程的名称,打印一句话String name = Thread.currentThread().getName();System.out.println(name+"执行了任务..."+id);}@Overridepublic String toString() {return "MyRunnable4{" +"id=" + id +'}';}
}
newScheduledThreadPool
例子
package com.liuhm.demo03;import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;/*测试ScheduleExecutorService接口中延迟执行任务和重复执行任务的功能*/
public class ScheduleExecutorServiceDemo01 {public static void main(String[] args) {//1:获取一个具备延迟执行任务的线程池对象ScheduledExecutorService es = Executors.newScheduledThreadPool(3);//2:创建多个任务对象,提交任务,每个任务延迟2秒执行for (int i=1;i<=10;i++){es.schedule(new MyRunnable(i),2, TimeUnit.SECONDS);}System.out.println("over");}
}
class MyRunnable implements Runnable{private int id;public MyRunnable(int id) {this.id = id;}@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(name+"执行了任务:"+id);}
}
package com.liuhm.demo03;import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;/*测试ScheduleExecutorService接口中延迟执行任务和重复执行任务的功能*/
public class ScheduleExecutorServiceDemo02 {public static void main(String[] args) {//1:获取一个具备延迟执行任务的线程池对象ScheduledExecutorService es = Executors.newScheduledThreadPool(3, new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义线程名:"+n++);}});//2:创建多个任务对象,提交任务,每个任务延迟2秒执行es.scheduleAtFixedRate(new MyRunnable2(1),1,2,TimeUnit.SECONDS);System.out.println("over");}
}class MyRunnable2 implements Runnable{private int id;public MyRunnable2(int id) {this.id = id;}@Overridepublic void run() {String name = Thread.currentThread().getName();try {Thread.sleep(1500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name+"执行了任务:"+id);}
}
package com.liuhm.demo03;import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;/*测试ScheduleExecutorService接口中延迟执行任务和重复执行任务的功能*/
public class ScheduleExecutorServiceDemo03 {public static void main(String[] args) {//1:获取一个具备延迟执行任务的线程池对象ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor( new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义线程名:"+n++);}});//2:创建多个任务对象,提交任务,每个任务延迟2秒执行es.scheduleWithFixedDelay(new MyRunnable3(1),1,2,TimeUnit.SECONDS);System.out.println("over");}
}class MyRunnable3 implements Runnable{private int id;public MyRunnable3(int id) {this.id = id;}@Overridepublic void run() {String name = Thread.currentThread().getName();try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name+"执行了任务:"+id);}
}

异步计算结果(Future)

  • 概念介绍

  • 常用方法

    • cancel(boolean mayInterruptIfRunning)
    • get()
    • get(long timeout, TimeUnit unit)
    • isCancelled()
    • isDone()
我们刚刚在学习java内置线程池使用时,没有考虑线程计算的结果,但开发中,我们有时需要利用线程进行一些计算,然后获取这些计算的结果,而java中的Future接口就是专门用于描述异步计算结果的,我们可以通过Future 对象获取线程计算的结果;
Future 的常用方法如下:
boolean cancel(boolean mayInterruptIfRunning) 试图取消对此任务的执行。 V get() 如有必要,等待计算完成,然后获取其结果。 V get(long timeout, TimeUnit unit) 如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。 boolean isCancelled() 如果在任务正常完成前将其取消,则返回 true。 boolean isDone() 如果任务已完成,则返回 true。 
package com.liuhm.demo04;import java.util.concurrent.*;/*练习异步计算结果*/
public class FutureDemo {public static void main(String[] args) throws Exception {//1:获取线程池对象ExecutorService es = Executors.newCachedThreadPool();//2:创建Callable类型的任务对象Future<Integer> f = es.submit(new MyCall(1, 1));//3:判断任务是否已经完成//test1(f);boolean b = f.cancel(true);//System.out.println("取消任务执行的结果:"+b);//Integer v = f.get(1, TimeUnit.SECONDS);//由于等待时间过短,任务来不及执行完成,会报异常//System.out.println("任务执行的结果是:"+v);}//正常测试流程private static void test1(Future<Integer> f) throws InterruptedException, ExecutionException {boolean done = f.isDone();System.out.println("第一次判断任务是否完成:"+done);boolean cancelled = f.isCancelled();System.out.println("第一次判断任务是否取消:"+cancelled);Integer v = f.get();//一直等待任务的执行,直到完成为止System.out.println("任务执行的结果是:"+v);boolean done2 = f.isDone();System.out.println("第二次判断任务是否完成:"+done2);boolean cancelled2 = f.isCancelled();System.out.println("第二次判断任务是否取消:"+cancelled2);}
}
class MyCall implements Callable<Integer>{private int a;private int b;//通过构造方法传递两个参数public MyCall(int a, int b) {this.a = a;this.b = b;}@Overridepublic Integer call() throws Exception {String name = Thread.currentThread().getName();System.out.println(name+"准备开始计算...");Thread.sleep(2000);System.out.println(name+"计算完成...");return a+b;}
}

线程池综合案例

使用线程池模拟秒杀商品的场景

  • 需求:假如有20人同时秒杀,但是商品却只有10件,请使用线程池模拟这个场景,前10人秒杀成功,后10人秒杀失败;

  • 要求:

    • 1:使用线程池创建线程
    • 2:解决线程安全问题
  • 思路提示:

    • 1:既然商品总数量是10个,那么我们可以在创建线程池的时候初始化线程数是10个及以下,设计线程池最大数量为10个;
    • 2:当某个线程执行完任务之后,可以让其他秒杀的人继续使用该线程参与秒杀;
    • 3:使用synchronized控制线程安全,防止出现错误数据;
package com.liuhm.demo05;
/*任务类:包含了商品数量,客户名称,送手机的行为;*/
public class MyTask implements Runnable {//设计一个变量,用于表示商品的数量private static int id = 10;//表示客户名称的变量private String userName;public MyTask(String userName) {this.userName = userName;}@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(userName+"正在使用"+name+"参与秒杀任务...");try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}synchronized (MyTask.class){if(id>0){System.out.println(userName+"使用"+name+"秒杀:"+id-- +"号商品成功啦!");}else {System.out.println(userName+"使用"+name+"秒杀失败啦!");}}}
}
package com.liuhm.demo05;import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*主程序类,测试任务类*/
public class MyTest {public static void main(String[] args) {//1:创建一个线程池对象ThreadPoolExecutor pool = new ThreadPoolExecutor(3,5,1, TimeUnit.MINUTES,new LinkedBlockingQueue<>(15));//2:循环创建任务对象for (int i = 1; i <=20 ; i++) {MyTask myTask = new MyTask("客户"+i);pool.submit(myTask);}//3:关闭线程池pool.shutdown();}
}

学员练习

需求:

  • 设计一个程序,使用两个线程模拟在两个地点同时从一个账号中取钱,假如卡中一共有1000元,每个线程取800元,要求演示结果一个线程取款成功,剩余200元,另一个线程取款失败,余额不足;

要求:

  • 1:使用线程池创建线程
  • 2:解决线程安全问题

思路提示:

  • 1:线程池可以利用Executors工厂类的静态方法,创建线程池对象;
  • 2:解决线程安全问题可以使用synchronized方法控制取钱的操作
  • 3:在取款前,先判断余额是否足够,且保证余额判断和取钱行为的原子性;
package com.liuhm.demo06;public class MyTask implements Runnable {//用户姓名private String userName;//取款金额private double money;//总金额private static double total = 1000;public MyTask(String userName, double money) {this.userName = userName;this.money = money;}@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(userName+"正在准备使用"+name+"取款:"+money+"元");try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}synchronized (MyTask.class){if(total-money>0){System.out.println(userName+"使用"+name+"取款:"+money+"元成功,余额:"+(total-money));total-=money;}else {System.out.println(userName+"使用"+name+"取款:"+money+"元失败,余额:"+total);}}}
}
package com.liuhm.demo06;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;public class MyTest {public static void main(String[] args) {//1:创建线程池对象ExecutorService pool = Executors.newFixedThreadPool(2, new ThreadFactory() {int id = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "ATM" + id++);}});//2:创建两个任务并提交for (int i = 1; i <=2 ; i++) {MyTask myTask = new MyTask("客户" + i, 800);pool.submit(myTask);}//3:关闭线程池pool.shutdown();}
}

线程池总结

线程池的使用步骤可以归纳总结为五步

  • 1:利用Executors工厂类的静态方法,创建线程池对象;
  • 2:编写Runnable或Callable实现类的实例对象;
  • 3:利用ExecutorService的submit方法或ScheduledExecutorService的schedule方法提交并执行线程任务
  • 4:如果有执行结果,则处理异步执行结果(Future)
  • 5:调用shutdown()方法,关闭线程池

http://chatgpt.dhexx.cn/article/C53PXrus.shtml

相关文章

线程池是什么?线程池(ThreadPoolExecutor)使用详解

点一点&#xff0c;了解更多https://www.csdn.net/ 本篇文章将详细讲解什么是线程池&#xff0c;线程池的参数介绍&#xff0c;线程池的工作流程&#xff0c;使用Executors创建常见的线程池~~~ 目录 点一点&#xff0c;了解更多 文章目录 一、线程池的概念 1.1线程池的目的…

写给小白看的线程池,还有10道面试题

如何搞定20k的面试小抄 为什么要用线程池呢&#xff1f; 下面是一段创建线程并运行的代码: for (int i 0; i < 100; i) {new Thread(() -> {System.out.println("run thread->" Thread.currentThread().getName());userService.updateUser(....);}).start…

线程池详解(通俗易懂超级好)

目标 【理解】线程池基本概念 【理解】线程池工作原理 【掌握】自定义线程池 【应用】java内置线程池 【应用】使用java内置线程池完成综合案例 线程池 线程池基础线程池使用线程池综合案例学员练习线程池总结 概念介绍 什么是线程池为什么使用线程池线程池有哪些优势 什么…

Java 多线程:彻底搞懂线程池

熟悉 Java 多线程编程的同学都知道&#xff0c;当我们线程创建过多时&#xff0c;容易引发内存溢出&#xff0c;因此我们就有必要使用线程池的技术了。 目录 1 线程池的优势 2 线程池的使用 3 线程池的工作原理 4 线程池的参数 4.1 任务队列&#xff08;workQueue&#x…

GridView概述

一、使用GridView以表格形式显示多张图片 GridView用于在界面上按行、列分布的方式来显示多个组件 二、使用GridView 1、java代码 import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.AdapterV…

Master-Detail GridView

梦幻版Master-Detail GridView(黄忠成) 2007-12-26 09:34 前面的Master-Detail GridView控件應用&#xff0c;相信你已在市面上的書、或網路上見過&#xff0c;但此節中的GridView控件應用包你沒看過&#xff0c;但一定想過&#xff01;請見圖4-8-63。 圖4-8-63 圖 4-8-64 你一…

GridView DataGrid

ASP.NET 2.0提供了功能强大的数据绑定控件GridView、在使用中&#xff0c;一些属性和方法经常会与ASP.NET 1.1中的DataGrid混淆(VS2005中依然可以使用DataGrid&#xff0c;手动添加到工具箱或HTML状态输入代码)&#xff0c;下面我们分别用GridView和DataGrid实现其数据绑定、编…

GridView详讲

GridView是ASP.NET界面开发中的一个重要的控件&#xff0c;对GridView使用的熟练程度直接影响软件开发的进度及功能的实现。(车延禄) GridView的主要新特性&#xff1a; 1.与DataSource控件结合实现了显示与数据操作的分离&#xff0c;大大减化了代码的编写量; 2.实现"双向…

GridView详述

GridView无代码分页排序GridView选中&#xff0c;编辑&#xff0c;取消&#xff0c;删除GridView正反双向排序GridView和下拉菜单DropDownList结合GridView和CheckBox结合鼠标移到GridView某一行时改变该行的背景色方法一鼠标移到GridView某一行时改变该行的背景色方法二GridVi…

GridView、ListView、Adapter、Map、HashMap

1.ListView自定义适配器adapter 注&#xff1a;Android适配器是数据和视图之间的桥梁&#xff0c;以便于数据在View上显示。适配器就像显示器&#xff0c;把复杂的东西按人可以接受的方式来展现。 &#xff08;1&#xff09;首先将适配器的View视图表现出来&#xff0c;使用L…

GridViewPager

GridViewPager ViewPager结合GridView&#xff0c;轻松实现类似表情面板的控件。可自由定制Item布局&#xff0c;提供充足的自定义参数等。也处理了条目点击事件和条目长按事件。效果如下&#xff1a; Demo下载地址&#xff1a;GridViewPager &#xff0c;或者扫描以下二维码…

libevent 编译

1.下载源码 github:https://github.com/libevent/libevent 官网&#xff1a;http://libevent.org/ 2.CMake 编译 在libevent源码目录建立文件夹&#xff1a;BuildVs2010_x64 2.打开CMake 3.BuildVs2010_x64 下此时生成了vs2010的解决方案。然后编译生成就ok NOTE&#x…

13、《Libevent中文帮助文档》学习笔记13:Linux下集成、运行libevent

Linux下编译libevent的指导可以参考《4、《Libevent中文帮助文档》学习笔记4&#xff1a;Linux下编译libevent》&#xff0c;完成编译、安装&#xff0c;生成so库后&#xff0c;其他程序即可依赖libevent的so库&#xff0c;使用libevent的功能。 由于没有通过prefix指定安装路…

libevent 编译与安装 (WIN10 visual studio2019, ubuntu,centos)

文章目录 一、准备安装包二、编译与安装编译zlib编译openssl编译libevent 三、libevent集成zlib测试程序修改编译&#xff08;可选&#xff09;四、测试程序五、linux(ubuntu)测试安装依赖环境&#xff0c;依次编译zlib,openssl,libeventwindows与linux共享文件夹&#xff08;使…

Libevent 学习一:Libevent 源码编译

文章目录 Libevent 学习一&#xff1a;Libevent 源码编译Libevent Windows 编译Windows 编译环境安装 Visual Studio Community 2015安装 zlib安装 OpenSSL安装 Libeventcmake 安装 LibeventLibevent 测试程序 Libevent Linux编译CentOS 7 安装 LibeventLibevent 测试程序 Libe…

libevent mysql_libevent安装总结

1.先用&#xff1a;ls -al /usr/lib | grep libevent 查看是否已安装&#xff1b;如果已安装且版本低于1.3&#xff0c;则先通过&#xff1a;rpm -e libevent —nodeps进行卸载。 2.下载libevent安装包&#xff1a;libevent-2.0.18-stable.tar.gz。 wget https://github.com/do…

在window用vcpkg安装libevent

参考readme https://github.com/microsoft/vcpkg/blob/master/README_zh_CN.md 使用的PackageManager方式安装&#xff0c; Package Managers 下载 vcpkg 依赖管理包 git clone https://github.com/Microsoft/vcpkg.git cd vcpkg ./bootstrap-vcpkg.bat ./vcpkg integrate…

libevent实践01:准备源码、搭建项目、编译脚本和入门例子

编译源码 libevent是一个Reactor事件库。 我的理解&#xff0c;就是封装了select、epoll、poll的函数库。有使用select&#xff0c;poll&#xff0c;epoll的需求就可以使用的。 官网地址&#xff1a;https://libevent.org/ 下载源码&#xff1a; https://github.com/libev…

libevent(1)windows下安装libevent

Socket通信库libevent成熟、稳定、性能高&#xff0c;在unix和windows下都能使用&#xff0c;在证券交易领域也有不少成功的应用&#xff0c;已经用事实证明是非常棒的socket通信库。对我们目前交易系统的unix重构来说&#xff0c;是比较合适的选择 –– 坑少、在证券交易项目中…

Libevent库的学习

目录 Libevent 概述 Libevent 使用模型 使用Libevent的基本流程&#xff1a; libevent 的核心&#xff0c;event 事件 1. 创建一个事件event 2. 释放event_free 3. 注册event 4. 信号事件 5. 销毁event_base Libevent 结构图 使用libevent库去实现tcp服务器 Libev…