线程池面试相关

文章详细介绍了Java中使用线程池ExecutorService和ThreadPoolExecutor管理线程,包括固定线程数、单线程执行和缓冲线程池的创建。同时,文章提到了线程池的工作原理,展示了自定义线程池配置以及四种拒绝策略,如AbortPolicy、CallerRunsPolicy等。此外,文章还讨论了死锁的概念并给出示例代码,以及如何通过jps和jstack命令检测和解决死锁问题。
摘要由CSDN通过智能技术生成

1.线程池的优势

在这里插入图片描述

2 Callable方式实现多线程

package com.ge.healthcare.cn.apm.gateway.util;
import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;
public class MyThread implements  Runnable{
    @Override    
    public void run(){}
}
class MyThread2 implements Callable<Integer>{
    @Override    
    public Integer call() throws Exception{
        System.out.println("**********come in callable");        
        return 1024;    
    }

}
class CallableDemo{
    public static void main(String[] args) throws  InterruptedException, ExecutionException {
        FutureTask<Integer> futureTask = new FutureTask<>(new MyThread2());        
        Thread t1 = new Thread(futureTask,"aaa");        
        t1.start();        
        //获取callable返回的返回值 放在最后面.        
        System.out.println("*******result: "+futureTask.get());
    }
}

在这里插入图片描述

package com.ge.healthcare.cn.apm.gateway.util;
import java.util.concurrent.*;
public class MyThreadPoolDemo {
    public static void main(String[] args) {
        //Executors.newFixedThreadPool(int) 固定线程数 执行长期的任务,性能好很多        
        //Executors.newSingleThreadExecutor() 单一 一个任务一个任务执行的场景        
        //Executors.newCachedThreadPool() 可扩容缓冲 适用:执行很多短期异步的小程序或者负载较轻的服务
        /*        
        ThreadPoolExecutor(        
        int corePoolSize, 线程池中的常驻核心线程数
        int maximumPoolSize, 线程池能够容纳同时执行的最大线程数,此值必须大于等于1        
        long keepAliveTime,  多余空闲线程存活时间,当前线程池数量超过corePoolSize时,当空闲时间达到keepAliveTime值是,多余空闲线程会被销魂直到剩下corePoolSize个线程为止
        TimeUnit unit, keepAliveTime的单位。
        BlockingQueue<Runnable> workQueue, 任务队列,被提交但尚未被执行的任务。
        ThreadFactory threadFactory, 表示生成线程池中工作线程的线程工厂,用于创建线程一般用默认的即可
        RejectedExecutionHandler handler 拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数(maximumPoolSIze)时如何来
        )        
        */
        ExecutorService threadPool = Executors.newFixedThreadPool(5);        
        try {
            for (int i = 1; i <=10 ; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"办理业务");                    
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();                    
                    }
                });            }
        }catch (Exception e){
            e.printStackTrace();        
        }finally {
            threadPool.shutdown();        
        }


    }
}

3 线程池

在这里插入图片描述
线程池底层工作原理
在这里插入图片描述
JKD内置拒绝策略
在这里插入图片描述
JDK提供的线程池经过实践都不用,用自己写的线程池

package com.ge.healthcare.cn.apm.gateway.util;
import java.util.concurrent.*;
public class MyThreadPoolDemo2 {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(
            2,            
            5,            
            1L,            
            TimeUnit.SECONDS,            
            new LinkedBlockingQueue <Runnable>(3),            
            Executors.defaultThreadFactory(),            
            new ThreadPoolExecutor.CallerRunsPolicy());//AbortPolicy:直接抛异常 CallerRunsPolicy:回退机制 DiscardOldestPolicy:抛弃等待时间最长的 DiscardPolicy:直接抛        try {
            for (int i = 1; i <=10 ; i++) {//最多处理8个 maximumPoolSize(5)+LinkedBlockingQueue(3)=8                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"办理业务");                    
                try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();                    
                }
                }
        );            
    }
        }catch (Exception e){
            e.printStackTrace();        
}finally {
            threadPool.shutdown();        
}
    
}

    public static void threadPoolInit() {
        //Executors.newFixedThreadPool(int) 固定线程数 执行长期的任务,性能好很多        //Executors.newSingleThreadExecutor() 单一 一个任务一个任务执行的场景        //Executors.newCachedThreadPool() 可扩容缓冲 适用:执行很多短期异步的小程序或者负载较轻的服务
    }
}

最大线程数设置多少合适
1.业务是cpu密集型
在这里插入图片描述
2.业务是io密集型
在这里插入图片描述
在这里插入图片描述

3 死锁编码及分析

死锁代码

package com.ge.healthcare.cn.apm.gateway.util;
import java.util.concurrent.TimeUnit;
class HoldLockThread implements  Runnable{
    private String lockA;    
    private String lockB;
    public HoldLockThread(String lockA, String lockB) {
        this.lockA = lockA;        
        this.lockB = lockB;    
    }

    @Override    public void run() {
        synchronized (lockA){
            System.out.println(Thread.currentThread().getName()+"自己持有"+ lockA +"尝试获得"+ lockB);            
            try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+"自己持有"+ lockB +"尝试获得"+ lockA);            
            }
        }
    }
}
/** 
* 死锁是指两个或两个以上的进程在执行过程中, 
* 因为争夺资源而造成的一种互相等待的现象, 
* 若无外力干涉他们都将无法推进下去。 
*/public class DeadLockDemo {
    public static void main(String[] args) {
        String lockA = "lockA";        
        String lockB = "lockB";        
        new Thread(new HoldLockThread(lockA,lockB),"ThreadAAA").start();        
        new Thread(new HoldLockThread(lockB,lockA),"ThreadBBB").start();
    }
}

解决
1.jps命令定位进程号
2.jstack找到死锁查看
jsp -l
jstack {进程号}

4 ThreadLocal案例

package com.ge.healthcare.cn.apm.report.utils;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//资源类
class MyData{
    ThreadLocal<Integer> threadLocalField = ThreadLocal.withInitial(() ->0);
    public void add(){
        threadLocalField.set(1+threadLocalField.get());
    }
}

public class ThreadLocalDemoV2 {
    public static void main(String[] args) {
        MyData myData = new MyData();
        //模拟一个银行有3个办理业务的受理窗口
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        try {
            //10个线程,池子里面有3个受理线程,负责处理业务
            for (int i = 0; i < 10; i++) {
                int finalI=i;
                threadPool.submit(() ->{
                    try {
                        Integer beforeInt = myData.threadLocalField.get();
                        myData.add();
                        Integer afertInt = myData.threadLocalField.get();
                        System.out.println(Thread.currentThread().getName()+"\t"+"工作城口\t"+"受理第: "+finalI +"个顾客业务"+"\t beforeInt: "+beforeInt+"\t afterInt:"+afertInt);
                    } finally {
                        //【强制】 必须回收自定义的ThreadLocal变量,尤其在线程池场景下,现成经常会被复用,如果不清理
                        //自定义的ThreadLocal变量,可能会影响后续业务逻辑和造成内存泄漏等问题。
                        //尽量在代码中使用try-finally块进行回收
                        myData.threadLocalField.remove();
                    }
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
}

5 线程间的数据传递和父子线程共享

ThreadLocal,InheritableThreadLocal,TransmittableThreadLocal三个有什么不同

package com.ge.healthcare.cn.apm.report.utils;


import com.alibaba.ttl.TransmittableThreadLocal;
import com.alibaba.ttl.threadpool.TtlExecutors;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadLocalDemoV3 {
    public static void main(String[] args) {
//        m1();
//        m2();
//        m3(); //InheritableThreadLocal 解决父线程给子线程传递信息
//        m4();
        m5(); //TransmittableThreadLocal
    }

    private static  void m1(){
        //ThreadLocal可以在线程中共享数据,set/get需要在同一个线程中执行才行,别人的取不到
        ThreadLocal<String> threadLocal = ThreadLocal.withInitial(()->null);

        threadLocal.set(Thread.currentThread().getName()+"-Java");
        System.out.println("major:"+threadLocal.get());
        System.out.println();

        //新建线程thread1,设置Vue,然后取出学科名看看?
        new Thread(()->{
            System.out.println("major:" +threadLocal.get());//thread1是否可以取得main线程上一步写入的值?答案:取不到 major:null major:thread1-Vue
            threadLocal.set(Thread.currentThread().getName()+"-Vue");
            System.out.println("major:"+threadLocal.get());
        },"thread1").start();
        System.out.println();

        //暂停几秒线程
        try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedException e){e.printStackTrace();}
        //
        new Thread(()->{
            System.out.println("major:"+threadLocal.get());
            threadLocal.set(Thread.currentThread().getName()+"-Flink");
            System.out.println("major:"+threadLocal.get());
        }).start();
        System.out.println();

        CompletableFuture.supplyAsync(()->{
            System.out.println("major:"+ threadLocal.get());
            threadLocal.set(Thread.currentThread().getName()+"-mysql");
            System.out.println("major:"+ threadLocal.get());
            return null;
        });
        System.out.println();
        try{TimeUnit.MILLISECONDS.sleep(500);}catch(InterruptedException e){e.printStackTrace();}

    }

    private static void m2(){
        ThreadLocal<String> threadLocal = ThreadLocal.withInitial(()->null);
        //这里是主线程,ThreadLocal中设置了值:Java
        threadLocal.set(Thread.currentThread().getName()+"-Java");
        System.out.println("major:"+threadLocal.get());

        //新建线程thread1,在子线程thread1中去ThreadLocal中拿main线程放入值,是否能拿到?
        //自己的set才能自己get,别人的取不到,分灶吃饭,自取自划
        new Thread(()->{
            System.out.println("major:"+threadLocal.get());
        },"thread1").start();
    }

    private static void m3(){
        InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();

        //这里是主线程,使用InheritableThreadLocal.set放入值:Java
        inheritableThreadLocal.set(Thread.currentThread().getName()+"-Java");
        System.out.println("major:"+inheritableThreadLocal.get());

        //新建线程thread1,在子线程thread1中去ThreadLocal中拿main线程放入值,能否拿到?答案:能
        //使用InheritableThreadLocal,子线程可以获得父线程set进去的值
        new Thread(()->{
            System.out.println("major:"+ inheritableThreadLocal.get());
        },"thread1").start();

        new Thread(()->{
            System.out.println("major:"+ inheritableThreadLocal.get());
        },"thread2").start();

        new Thread(()->{
            System.out.println("major:"+ inheritableThreadLocal.get());
        },"thread3").start();
    }

    private static void m4(){
        //InheritableThreadLocal:遇到线程池,会有问题

        InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
        //这里是主线程,使用InheritableThreadLocal.set 翻入值:Java
        inheritableThreadLocal.set(Thread.currentThread().getName()+"-Java");
        System.out.println("major:"+inheritableThreadLocal.get());

        //为了看到效果,这里创建大小为1的线程池方便看到效果,池中只有一个线程池才有效果
        ExecutorService threadPool = Executors.newFixedThreadPool(1);
        //在线程池中通过InheritableThreadLocal 拿值,看看能否拿到刚才放入的Java?答案:能
        threadPool.execute(()->{
            System.out.println("threadPool第一次获取 major:"+inheritableThreadLocal.get());
        });
        //可以先执行上面的看看----------------------------------------------------------------------------------------------------
        try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedException e){e.printStackTrace();}
        System.out.println();

        //这里又在主线程池中放入的vue
        inheritableThreadLocal.set(Thread.currentThread().getName()+"-Vue已经修改");
        System.out.println("major:"+inheritableThreadLocal.get());
        //这里又在线程池中通过InheritableThreadLocal.get方法拿值,看看能否拿到刚刚放入的vue?答案:只能获取第一次的值,没有同步修改
        threadPool.execute(()->{
            System.out.println("threadPool第二次获取major:"+inheritableThreadLocal.get());
        });
        try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedException e){e.printStackTrace();}
        threadPool.shutdown();
        //总结:new 新建可以,复用不好使。
    }

    private static void m5(){
        /*在Pom.xml中添加
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>transmittable-thread-local</artifactId>
            <version>2.14.3</version>
        </dependency>*/
        TransmittableThreadLocal<String> transmittableThreadLocal = new TransmittableThreadLocal<>();
        //为了看到效果,这里创建大小为1的线程池方便看到效果,池中只有1个线程有效。
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //这里需要用TtlExecutors.getTtlExecutorService 将原线程池包装下
        threadPool = TtlExecutors.getTtlExecutorService(threadPool);

        //这里是主线程,使用transmittableThreadLocal.set 放入职:Java
        transmittableThreadLocal.set(Thread.currentThread().getName()+"-Java");
        System.out.println("major:" + transmittableThreadLocal);

        //在线程池中通过 transmittableThreadLocal拿值,看看能否拿到刚才放入的Java?
        threadPool.execute(()->{
            System.out.println("threadPool第一次获取 major:"+transmittableThreadLocal.get());
        });
        try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedException e){e.printStackTrace();}
        System.out.println();

        //这里又往主线程放入的vue
        transmittableThreadLocal.set(Thread.currentThread().getName()+"-Vue");
        System.out.println("major:"+transmittableThreadLocal.get());

        //这里又在线程池中通过transmittableThreadLocal.get 方法拿值,看是否能拿到刚才放入的vue?
        threadPool.execute(()->{
            System.out.println("threadPool第二次获取 major:"+transmittableThreadLocal.get());
        });
        System.out.println();
        try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedException e){e.printStackTrace();}
        threadPool.shutdown();
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值