首页 > 其他分享 >并发学习记录15:工作线程

并发学习记录15:工作线程

时间:2022-10-04 11:44:54浏览次数:61  
标签:15 log 并发 线程 str debug new public

定义

就是让有限的工作线程来轮流异步处理无限多的任务。典型实现就是线程池,线程个数是有限的,但是任务是源源不断需要被处理的

假设一个饭店有服务员线程,需要轮流处理客户的点餐任务,如果每一个客户都配一名专属的服务员,那么成本就会非常高。

饥饿

固定大小的线程池会有饥饿现象,假设这是背景:
两个工人线程是一个线程池中的两个线程。要做的工作就是为客人点餐和到后厨做菜,这是两个阶段的工作。
为客人点餐:帮客人点餐,然后等待菜做好,接着上菜
后厨做菜:接收到订单然后做菜
当只有一个客户的时候,工人A为客人点了餐,然后等工人B帮客人做好菜,然后就可以上菜完成一次对客户的服务
但是当两个工人时,就出现了一点问题。

所以不同类型的任务需要放在不同的线程池中运行,以此来避免饥饿的情况。

代码演示:

import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

@Slf4j(topic = "ch.ThreadPoolTest05")
public class ThreadPoolTest05 {
    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) {
        method3();
    }

    public static void method1() {
        ExecutorService workerPool = Executors.newFixedThreadPool(2);
        workerPool.execute(new Runnable() {
            @Override
            public void run() {
                log.debug("处理点餐");
                Future<String> returnResult = workerPool.submit(new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        String str;
                        log.debug("做菜:{}", (str = cooking()));
                        return str;
                    }
                });
                try {
                    log.debug("上菜:{}", returnResult.get());
                } catch (InterruptedException | ExecutionException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    //由于两个线程都在做点餐任务,且都在等后厨做菜的结果,所以会产生饥饿现象
    public static void method2() {
        ExecutorService workerPool = Executors.newFixedThreadPool(2);
        workerPool.execute(new Runnable() {
            @Override
            public void run() {
                log.debug("1处理点餐");
                Future<String> returnResult = workerPool.submit(new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        String str;
                        log.debug("1做菜:{}", (str = cooking()));
                        return str;
                    }
                });
                try {
                    log.debug("1上菜:{}", returnResult.get());
                } catch (InterruptedException | ExecutionException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        workerPool.execute(new Runnable() {
            @Override
            public void run() {
                log.debug("2处理点餐");
                Future<String> returnResult = workerPool.submit(new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        String str;
                        log.debug("2做菜:{}", (str = cooking()));
                        return str;
                    }
                });
                try {
                    log.debug("2上菜:{}", returnResult.get());
                } catch (InterruptedException | ExecutionException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }

    //不同的任务设置不同的线程池,就可以避免饥饿现象
    public static void method3() {
        ExecutorService waiterPool = Executors.newFixedThreadPool(1);
        ExecutorService cookerPool = Executors.newFixedThreadPool(1);
        waiterPool.execute(new Runnable() {
            @Override
            public void run() {
                log.debug("开始点菜");
                Future<String> result = cookerPool.submit(new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        String str;
                        log.debug("开始做菜{}", (str = cooking()));
                        return str;
                    }
                });
                try {
                    log.debug("上菜{}",result.get());
                } catch (InterruptedException | ExecutionException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        waiterPool.execute(new Runnable() {
            @Override
            public void run() {
                log.debug("开始点菜");
                Future<String> result = cookerPool.submit(new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        String str;
                        log.debug("开始做菜{}", (str = cooking()));
                        return str;
                    }
                });
                try {
                    log.debug("上菜{}",result.get());
                } catch (InterruptedException | ExecutionException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }
}

创建多少线程合适呢

如果线程数目过少,可能会导致程序不能充分的利用系统资源,容易导致饥饿
如果线程数目过大,可能会导致线程上下文切换频繁,占用了更多的内存

对于cpu密集型运算

通常采用cpu核数+1能够实现最优的cpu利用率,+1是用来保证当线程由于页缺失故障或者其他原因导致暂停时,额外的线程能顶上去,保证cpu时钟周期不被浪费

对于IO密集型运算

cpu不总是处于繁忙状态,当执行业务计算时,会使用cpu资源,但执行IO操作和远程RPC调用时,cpu就闲下来了,所以可以利用多线程去提cpu的利用率

一个线程数的经验公式

线程数 = 核数 * 期望cpu利用率 * 总时间(cpu计算时间 + 等待时间) / cpu计算时间
两个例子:
4核cpu,假设计算时间是 50% ,其他等待时间是 50%,期望cpu利用率是 100%,套用公式需要的线程数目便是:
4 * 100% * 100% / 50% = 8

4核cpu,假设计算时间是10%,其他等待时间是 90%,期望cpu利用率是 100%,套用公式
4 * 100% * 100% / 10% = 40

标签:15,log,并发,线程,str,debug,new,public
From: https://www.cnblogs.com/wbstudy/p/16753509.html

相关文章