您的位置:首页 > 科技 > 能源 > 找图纸的网站_重庆新闻经典论坛_南宁优化推广服务_建站平台哪个好

找图纸的网站_重庆新闻经典论坛_南宁优化推广服务_建站平台哪个好

2024/11/20 13:30:51 来源:https://blog.csdn.net/Helen_1997_1997/article/details/129843128  浏览:    关键词:找图纸的网站_重庆新闻经典论坛_南宁优化推广服务_建站平台哪个好
找图纸的网站_重庆新闻经典论坛_南宁优化推广服务_建站平台哪个好

一、线程池介绍

        线程池(Thread Pool)是一种基于多线程处理的服务器架构,它预先创建并维护一组线程,用于处理异步任务或并发请求。线程池的设计目的是减少创建和销毁线程的开销,提高系统的响应速度和吞吐量。

(一)线程池的主要核心原理

  1. 创建一个池子,池子中是空的。
  2. 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子。下次再次提交任务时,不需要创建新的线程,直接复用已有的线程即可。
  3. 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待。

二、线程池代码实现

实现步骤:

  1. 创建线程池
  2. 提交任务
  3. 所有的任务全部执行完毕,关闭线程池 

Excutors:线程池的工具类通过调用方法返回不同类型的线程池对象。

public static ExecutorService  newCachedThreadPool();              创建一个没有上限的线程池
public static ExecutorService  newFixedThreadPool(int nThreads);        创建有上限的线程池

(一)创建只有一个线程的线程池(了解)

private static void demo1() {// 创建只有一个线程的线程池ExecutorService executorService = Executors.newSingleThreadExecutor();// 不能并发   假设有10个任务,只有一个执行,其他9个都在等待for (int i = 0; i < 10; i++) {executorService.submit(()-> System.out.println(Thread.currentThread().getName() + "执行了"));}// 异步任务执行完之后关闭线程池
//        executorService.shutdown();// 立即关闭线程池executorService.shutdownNow();}

运行结果:

(二)指定线程池中线程的数量(了解)

private static void demo2() {// 指定线程池中线程的数量ExecutorService executorService = Executors.newFixedThreadPool(10);for (int i = 0; i < 20; i++) {executorService.submit(() -> System.out.println(Thread.currentThread().getName() + "执行了"));}}

运行结果:无论提交多少个新任务,只会创建指定线程数

(三)创造一个可以伸缩的线程池对象

private static void demo3() {ExecutorService executorService = Executors.newCachedThreadPool();for (int i = 0; i < 10; i++) {executorService.submit(() -> System.out.println(Thread.currentThread().getName() + "执行了"));}
}

运行结果:

(四)创建一个可以延迟的线程池对象

private static void demo4() {// 创建一个可以延迟的线程池对象ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);long start = System.currentTimeMillis();scheduledExecutorService.schedule(() -> {long end = System.currentTimeMillis();System.out.println(end-start); // 5018System.out.println("hello Thread");}, 5, TimeUnit.SECONDS);
}

运行结果:

三、自定义线程池——ThreadPoolExecutor

(一)自定义线程池的七个参数

参数参数解释参数范围
int corePoolSize核心线程数量不能小于0
int maximumPoolSize最大线程数量不能小于等于0,最大数量>=核心线程数
long keepAliveTime在指定的时间内回收线程不能小于0
TimeUnit unit时间单位用TimeUnit指定
BlockingQueue<Runnable> workQueue任务队列

不能为null

最大线程数中排满了,多余的队列就会在任务队列中排队等待

实现类:

ArrayBlockingQueue          有界队列

LinkedBlockingQueue        无界队列

PriorityBlockingQueue       优先队列

ThreadFactory threadFactory创建线程工厂

不能为null

传入Executors.defaultThreadFactory()

RejectedExecutionHandler handler任务的拒绝策略

不能为null

当线程池和任务队列都满了的时候的拒绝策略,该策略在主线程执行

默认传入:

new ThreadPoolExecutor.AbortPolicy()

1.自定义线程池任务拒绝策略

设置核心线程数为3,最大线程数为6,队列长度为3:

        提交3个任务:此时线程池中就会创建3个线程来处理这3个任务;

        提交5个任务:此时线程池中会创建3个线程来处理3个任务,剩余的2个任务就会在任务队列中排队等待,等有了空余的线程,后面2个任务才会被执行;

        提交8个任务:此时线程池中会创建3个线程来处理3个任务,后面3个任务就会在队列中排队等待,线程池创建临时线程处理剩下2个任务;

        因此,创建临时线程的条件:核心线程都在运行,任务队列中已经排满了,才会创建临时线程处理后面的任务。

        任务在执行的时候,并不一定会按照提交的顺序来执行,先提交的任务不一定先执行。

        提交10个任务:此时提交的任务数量已经超过了最大线程数+队列长度。处理方案:线程池中会创建3个线程来处理3个任务,有3个任务在队列中等待,线程池中会创建3个临时线程处理3个任务,剩下1个任务就会触发任务的拒绝策略。

2.代码实现线程池  

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 100, 5,TimeUnit.SECONDS, new ArrayBlockingQueue<>(10),Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
// 执行
threadPoolExecutor.execute(() -> System.out.println(Thread.currentThread().getName() + "执行了"));

(二)封装线程池

线程池在整个项目中只有一个,所以可以将线程池与单例模式相结合。

public class ThreadPoolUtil {private ThreadPoolUtil() {}private static final ThreadPoolUtil THREAD_POOL_UTIL = new ThreadPoolUtil();private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR;private static final ScheduledThreadPoolExecutor SCHEDULED_THREAD_POOL_EXECUTOR;static {THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(10, 100, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10),Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());SCHEDULED_THREAD_POOL_EXECUTOR = new ScheduledThreadPoolExecutor(5, Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());}public static ThreadPoolUtil getInstance() {return THREAD_POOL_UTIL;}// 异步执行public void execute(Runnable runnable) {THREAD_POOL_EXECUTOR.execute(runnable);}// 延迟执行public void delay(Runnable runnable, long time, TimeUnit timeUnit) {SCHEDULED_THREAD_POOL_EXECUTOR.schedule(runnable, time, timeUnit);}// 传入Callable接口的实现类public <V> void task(Callable<V> callable) {THREAD_POOL_EXECUTOR.submit(callable);}// 停止线程public void release() {THREAD_POOL_EXECUTOR.shutdown();}public void releaseNow() {THREAD_POOL_EXECUTOR.shutdownNow();}public void shutdown() {SCHEDULED_THREAD_POOL_EXECUTOR.shutdown();}public void shutdownNow() {SCHEDULED_THREAD_POOL_EXECUTOR.shutdownNow();}
}

测试封装后的线程池:

public static void main(String[] args) {// 测试封装后的线程池ThreadPoolUtil3.getInstance().execute(() -> System.out.println(Thread.currentThread().getName() + " hello Thread"));
}// pool-1-thread-1 hello Thread

(三)最大并行数

使用代码可以获取当前电脑的最大并行数:

public class MyThreadPoolDemo2 {public static void main(String[] args) {int count = Runtime.getRuntime().availableProcessors();System.out.println(count); // 6}
}

线程池的大小:

计算比较多,读取本地文件和连接数据库的操作比较少,使用:

        CPU密集型运算:最大并行数+1

读取本地文件和连接数据库的操作比较多,使用:

        I/O密集型运算:最大并行数 * 期望CPU利用率 * (总时间(CPU计算时间+等待时间) / CPU计算时间)

四、ThreadLocal

(一)ThreadLocal基本介绍

        每一个线程有一个自己的ThreadLocal,ThreadLocal本质上是线程的一个映射。

        ThreadLocal 是 Java 中的一个类,它提供了线程局部变量。每个使用该变量的线程都有其自己的独立初始化变量副本,因此每个线程可以访问到自己的线程局部变量,而不会影响到其他线程中的变量。这在多线程编程中非常有用,因为它可以避免共享资源的同步问题。

        ThreadLocal的作用:在线程之间传递数据,即从线程的上游到线程的下游;解决并发安全的问题。

出现线程安全隐患的条件:

  • 共享资源
  • 写操作
  • 多线程(通过加锁,可以破坏多线程,局部变成单线程)

(二)使用ThreadLocal传递数据 

public class TestDemo3 {private static ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void main(String[] args) {String str = "hello e";threadLocal.set(str);a();}private static void a() {b();}private static void b() {c();}private static void c() {d();}private static void d() {e();}private static void e() {System.out.println(threadLocal.get());}
}

(三)解决线程安全隐患——破坏共享资源 

        定义一个打印机类,再定义两个线程,调用打印机,实现论文打印的功能,每个线程打印3行论文,并且一个线程打印完,另一个线程才能打印。

public class TestDemo4 {public static void main(String[] args) {Printer printer = new Printer();new Thread(new GFS(printer)).start();new Thread(new BFM(printer)).start();}
}class Printer {public void print(String str) {System.out.println("打印机在打印" + str);}
}class GFS implements Runnable {private Printer printer;public GFS(Printer printer) {this.printer = printer;}@Overridepublic void run() {printer.print(printer + "高富帅");printer.print(printer + "高富帅");printer.print(printer + "高富帅");}
}class BFM implements Runnable {private Printer printer;public BFM(Printer printer) {this.printer = printer;}@Overridepublic void run() {printer.print(printer + "白富美");printer.print(printer + "白富美");printer.print(printer + "白富美");}
}

运行结果:打印输出是乱序的


使用synchronized锁,使其连续打印:

class GFS implements Runnable {private Printer printer;public GFS(Printer printer) {this.printer = printer;}@Overridepublic void run() {synchronized (printer) {printer.print(printer + "高富帅");printer.print(printer + "高富帅");printer.print(printer + "高富帅");}}
}class BFM implements Runnable {private Printer printer;public BFM(Printer printer) {this.printer = printer;}@Overridepublic void run() {synchronized (printer) {printer.print(printer + "白富美");printer.print(printer + "白富美");printer.print(printer + "白富美");}}
}

运行结果:

 


加锁是破坏多线程,使其每次只执行一个线程,除了这种方法,我们也可以共享资源:

通过重写ThreadLocal的initialValue方法,使其返回值为指定的值,源码默认为null:

protected T initialValue() {return null;
}
public class TestDemo4 {public static ThreadLocal<Printer> threadLocal = new ThreadLocal<>() {// 匿名内部类,重写initialValue方法@Overrideprotected Printer initialValue() {return new Printer();}};// lambda表达式
//    static ThreadLocal<Printer> threadLocal = ThreadLocal.withInitial(() -> new Printer());public static void main(String[] args) {new Thread(new GFS()).start();new Thread(new BFM()).start();}
}class Printer {public void print(String str) {System.out.println("打印机在打印" + str);}
}class GFS implements Runnable {@Overridepublic void run() {Printer printer = TestDemo4.threadLocal.get();printer.print(printer + "高富帅");printer.print(printer + "高富帅");printer.print(printer + "高富帅");}
}class BFM implements Runnable {@Overridepublic void run() {Printer printer = TestDemo4.threadLocal.get();printer.print(printer + "白富美");printer.print(printer + "白富美");printer.print(printer + "白富美");}
}

运行结果:打印虽然是乱序的,但是资源是一个线程一份的,地址值有2个:

 

ThreadLocal与synchronized锁:

当需要共享资源的时候,例如卖票,就使用synchronzied锁;

当一个线程一份资源的时候,就使用ThreadLocal。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com