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();
}
}