当前位置: 首页> 文旅> 美景 > 传奇网址大全_建筑网官方网站查询_焦作网站seo_百度知道客服电话

传奇网址大全_建筑网官方网站查询_焦作网站seo_百度知道客服电话

时间:2025/7/12 5:06:41来源:https://blog.csdn.net/2301_79748665/article/details/145052518 浏览次数:0次
传奇网址大全_建筑网官方网站查询_焦作网站seo_百度知道客服电话

目录

  • 线程池
    • 一: 提交任务
      • 1:submit
      • 2:invokeAll
      • 3:invokeAny
    • 二:停止任务
      • 1:shotdown
  • 2:shutdownNow
    • 三:工作线程
      • 1:定义
      • 2:饥饿现象
        • 饥饿的发生
        • 解决
      • 3:线程池大小
        • cpu密集型
        • IO密集型
    • 四:任务调度线程池
      • ScheduledThreadPool
          • scheduleAtFixedRate
        • 定时任务
      • 正确处理异常

线程池

一: 提交任务

1:submit

  • 带返回结果的提交任务,底层是使用了保护性暂停的设计模式;
public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService pool = Executors.newFixedThreadPool(2);Future<String> future = pool.submit(() -> {log.debug("{}", "running");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}return "ok";});log.debug("{}",future.get());
}

以上是测试代码,代码的返回结果是:

17:38:42.888 [pool-1-thread-1] DEBUG com.hbu.excutor.testSubmit - running
17:38:43.907 [main] DEBUG com.hbu.excutor.testSubmit - ok

2:invokeAll

  • 这个也是提交之后会有返回值,但是这里提交的任务是一个集合,等待线程将集合中所有的任务执行完毕之后,才会返回结果,是future类型的;
// 提交 tasks 中所有任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)throws InterruptedException;

3:invokeAny

  • 这个提交也是提交一个任务集合,但是只会返回一个结果,集合中哪个任务先执行完就返回哪个任务的结果;
// 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消
<T> T invokeAny(Collection<? extends Callable<T>> tasks)throws InterruptedException, ExecutionException;// 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消,带超时时间
<T> T invokeAny(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;

二:停止任务

1:shotdown

  • 将线程池的状态设置为shotdown;

    • 即:阻塞队列不会接受新的任务。
    • 不会终止正在执行的任务;
    • 并且会将阻塞队列中的任务执行完
    
    public void shutdown() {final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {checkShutdownAccess();// 修改线程池状态advanceRunState(SHUTDOWN);// 仅会打断空闲线程interruptIdleWorkers();onShutdown(); // 扩展点 ScheduledThreadPoolExecutor} finally {mainLock.unlock();}// 尝试终结(没有运行的线程可以立刻终结,如果还有运行的线程也不会等)tryTerminate();
    }
    

    注意:主线程执行了shutdown之后,不会阻塞等待队列中的任务执行完,而是会继续执行主线程后面的代码;

    我们可以使用

    // 调用 shutdown 后,由于调用线程并不会等待所有任务运行结束,因此如果它想在线程池 TERMINATED 后做些事情,可以利用此方法等待
    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
    

    2:shutdownNow

    • 将线程池状态转成stop

    • 即:终止当前正在执行的任务

    • 将阻塞队列中的任务返回;(返回一个runnable数组)

    • 不会执行新的任务

public List<Runnable> shutdownNow() {List<Runnable> tasks;final ReentrantLock mainLock = this.mainLock;mainLock.lock();try {checkShutdownAccess();// 修改线程池状态advanceRunState(STOP);// 打断所有线程interruptWorkers();// 获取队列中剩余任务tasks = drainQueue();} finally {mainLock.unlock();}// 尝试终结tryTerminate();return tasks; 
}

三:工作线程

1:定义

  • 使用有限的工作线程去异步执行无限的任务,这种行为被称为分工模式;利用了享元模式的思想;

让有限的工作线程(Worker Thread)来轮流异步处理无限多的任务。也可以将其归类为分工模式,它的典型实现

就是线程池,也体现了经典设计模式中的享元模式。

例如,海底捞的服务员(线程),轮流处理每位客人的点餐(任务),如果为每位客人都配一名专属的服务员,那么成本就太高了(对比另一种多线程设计模式:Thread-Per-Message)

注意,不同任务类型应该使用不同的线程池,这样能够避免饥饿,并能提升效率

例如,如果一个餐馆的工人既要招呼客人(任务类型A),又要到后厨做菜(任务类型B)显然效率不咋地,分成

服务员(线程池A)与厨师(线程池B)更为合理,当然你能想到更细致的分工

2:饥饿现象

  • 饥饿现象就是当存在两阶段的任务,而线程池只有一个,两阶段的任务具有依赖关系,如果线程池被其中一个任务给占用完了,那么就会造成类似于死锁的现象,称为饥饿;

固定大小线程池会有饥饿现象

  • 两个工人是同一个线程池中的两个线程

  • 他们要做的事情是:为客人点餐和到后厨做菜,这是两个阶段的工作

  • 客人点餐:必须先点完餐,等菜做好,上菜,在此期间处理点餐的工人必须等待

  • 后厨做菜:没啥说的,做就是了

  • 比如工人A 处理了点餐任务,接下来它要等着 工人B 把菜做好,然后上菜,他俩也配合的蛮好

  • 但现在同时来了两个客人,这个时候工人A 和工人B 都去处理点餐了,这时没人做饭了,饥饿

饥饿的发生
public class TestStarvation {static final List<String> MENU = Arrays.asList("地三鲜", "宫保鸡丁", "辣子鸡丁", "烤鸡翅");static Random RANDOM = new Random();static String cooking() {return MENU.get(RANDOM.nextInt(MENU.size()));}public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(2);executorService.execute(() -> {log.debug("处理点餐...");Future<String> f = executorService.submit(() -> {log.debug("做菜");return cooking();});try {log.debug("上菜: {}", f.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}});/*executorService.execute(() -> {log.debug("处理点餐...");Future<String> f = executorService.submit(() -> {log.debug("做菜");return cooking();});try {log.debug("上菜: {}", f.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}});*/}
}

如果只是提交一个上述任务,先从线程池中获取一个线程执行点餐操作,然后在执行的任务中再提交一个任务这个时候又会从线程池中获取一个线程,而线程池中只有2个线程,于是,线程就背用完了。然后如果再提交上面这个任务,那么因为提交时就会取出一个线程,提交两个任务就会将两个线程全部取用,而做菜需要线程,就会等待,线程永远阻塞等待,这样就会造成饥饿

解决

不同的任务使用不同的线程池就能避免饥饿;

public class TestStarvation {static final List<String> MENU = Arrays.asList("地三鲜", "宫保鸡丁", "辣子鸡丁", "烤鸡翅");static Random RANDOM = new Random();static String cooking() {return MENU.get(RANDOM.nextInt(MENU.size()));}public static void main(String[] args) {ExecutorService waiterPool = Executors.newFixedThreadPool(1);ExecutorService cookPool = Executors.newFixedThreadPool(1);waiterPool.execute(() -> {log.debug("处理点餐...");Future<String> f = cookPool.submit(() -> {log.debug("做菜");return cooking();});try {log.debug("上菜: {}", f.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}});waiterPool.execute(() -> {log.debug("处理点餐...");Future<String> f = cookPool.submit(() -> {log.debug("做菜");return cooking();});try {log.debug("上菜: {}", f.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}});}
}public class TestStarvation {static final List<String> MENU = Arrays.asList("地三鲜", "宫保鸡丁", "辣子鸡丁", "烤鸡翅");static Random RANDOM = new Random();static String cooking() {return MENU.get(RANDOM.nextInt(MENU.size()));}public static void main(String[] args) {ExecutorService waiterPool = Executors.newFixedThreadPool(1);ExecutorService cookPool = Executors.newFixedThreadPool(1);waiterPool.execute(() -> {log.debug("处理点餐...");Future<String> f = cookPool.submit(() -> {log.debug("做菜");return cooking();});try {log.debug("上菜: {}", f.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}});waiterPool.execute(() -> {log.debug("处理点餐...");Future<String> f = cookPool.submit(() -> {log.debug("做菜");return cooking();});try {log.debug("上菜: {}", f.get());} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}});}
}

3:线程池大小

线程池创建多少比较合适:

  • 如果线程池大小过小,容易造成饥饿现象
  • 如果线程池大小过大,则会造成线程上下文切换频繁,占用内存
  • 分情况考虑线程池的大小情况:
cpu密集型

在cpu计算密集时,我们将线程池的大小设为核的个数+1;+1是保证操作系统在页缺失和因为异常导致暂停时,额外的线程可以顶上去;

IO密集型

在IO密集型计算时,比如在rpc远程调用,数据库调用数据的时候,cpu就会空闲下来。

这时我们创建线程池的大小公式应该为 核的个数*期望的cpu利用率*总时间(cpu计算时间+其他的等待时间)/cpu计算时间

例如:核的个数是4,期望的cpu利用率是100,cpu计算时间是50,其他时间是50。所以线程池的个数是8个;

四:任务调度线程池

在任务调度线程池出现之前,任务调度是由timer实现的,但是因为timer是一个线程来实现任务调度的,所以任务是串行执行的,需要等待前面的任务执行完才能执行下面的任务。如果前面的任务出现了异常那么后面的任务就不会执行;

public static void main(String[] args) {Timer timer = new Timer();TimerTask task1 = new TimerTask() {@Overridepublic void run() {log.debug("task 1");sleep(2);}};TimerTask task2 = new TimerTask() {@Overridepublic void run() {log.debug("task 2");}};log.debug("start...");// 使用 timer 添加两个任务,希望它们都在 1s 后执行// 但由于 timer 内只有一个线程来顺序执行队列中的任务,因此『任务1』的延时,影响了『任务2』的执行// 甚至如果task1出异常停止后,task2都不会执行timer.schedule(task1, 1000);timer.schedule(task2, 1000);
}

ScheduledThreadPool

使用ScheduledThreadPool就能解决timer的缺点,任务是并行执行的,并且前面任务出现异常不会影响后面的任务;

public static void main(String[] args) throws InterruptedException {ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);log.debug("{}",System.currentTimeMillis());pool.schedule(() -> {log.debug("task1");int i=1/0;log.debug("sss");}, 1, TimeUnit.SECONDS);Thread.sleep(1000);log.debug("{}",System.currentTimeMillis());pool.schedule(() -> {log.debug("task2");}, 1, TimeUnit.SECONDS);
}
scheduleAtFixedRate
  • 定时执行任务
ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
log.debug("start...");pool.scheduleAtFixedRate(() -> {log.debug("running...");sleep(2);
}, 1, 1, TimeUnit.SECONDS);

设置的任务间隔时间和任务执行时间,取其中较大的作为最终的任务执行间隔时间;

执行结果:

21:44:30.311 c.TestTimer [main] - start... 
21:44:31.360 c.TestTimer [pool-1-thread-1] - running... 
21:44:33.361 c.TestTimer [pool-1-thread-1] - running... 
21:44:35.362 c.TestTimer [pool-1-thread-1] - running... 
21:44:37.362 c.TestTimer [pool-1-thread-1] - running...
定时任务
  • 实现每周四18:00执行任务:
pool.scheduleAtFixedRate(() -> {log.debug("running...");sleep(2);
}, 1, 1, TimeUnit.SECONDS);

这里要填写参数:

initialDelay:初始时间距离现在的时间间隔;我们要获取当前时间与这周四或者下周四的间隔时间;

period:是间隔的周期;

initialDelay

 LocalDateTime now=LocalDateTime.now();LocalDateTime initialDelay = now.withHour(18).withMinute(0).withSecond(0).withNano(0).with(DayOfWeek.THURSDAY);if (now.compareTo(initialDelay)>0){initialDelay = initialDelay.plusWeeks(1);}System.out.println(initialDelay);System.out.println(now);long millis = Duration.between(now, initialDelay).toMillis();

period:

long period =1000*60*60*24*7;

正确处理异常

我们在线程池的任务中不会直接抛出异常;

我们可以主动采用两种方法:

1:主动使用try-catch代码块捕捉异常:

 pool.schedule(() -> {log.debug("task1");try {int i=1/0;} catch (Exception e) {log.error("{}",e.getMessage());}log.debug("sss");}, 1, TimeUnit.SECONDS);

2:使用submit接收一个callable对象,返回的future,我们get他的返回值;

Future<String> submit = pool.submit(() -> {log.debug("task1");int i = 1 / 0;return "ok";
});
log.error("{}",submit.get());
关键字:传奇网址大全_建筑网官方网站查询_焦作网站seo_百度知道客服电话

版权声明:

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

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

责任编辑: