线程、线程池三大方法、七大参数、四种策略
线程和进程
进程: 一个程序,是执行程序的一次执行过程。
一个进程往往包含若干个线程,线程是cpu调度和执行的单位。
Java默认有2个线程:main、GC
并发:(多线程操作同一个资源)
- CPU 一核,模拟出来多条线程,快速交替
并行:(多个人一起行走)
- CPU 多核,多个线程可以同时执行;线程池
public class Test1 {
public static void main(String[] args) {
//获取cpu的核数
//cpu 密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
线程有几种状态
public enum State {
// 新生
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待
WAITING,
// 超时等待
TIMED_WAITING,
// 终止
TERMINATED;
}
wait/sleep 区别
1、来自不同的类
wait => Object
sleep => Thread
2、关于锁的释放
wait 会释放锁,sleep 睡觉了,抱着锁睡觉,不会释放!
3、使用的范围是不同的
wait:必须在同步代码块中
sleep:可以在任何地方睡
Callable接口
1、可以有返回值
2、可以抛出异常
3、方法不同,run() / call()
线程启动只能使用
new Thread().start();
而Thread类的构造方法只接受Runnable接口;而
FutureTask<V>
是Runnable的实现类,并且有构造方法参数为Callable接口。所以可以通过类FutureTask来作为Thread的参数启动线程。
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread thread = new MyThread();
FutureTask futureTask = new FutureTask(thread); // 适配类
new Thread(futureTask, "A").start();
new Thread(futureTask, "B").start();
// 结果只打印一个 call()
// 结果会被缓存,效率高
Integer o = (Integer) futureTask.get();
//这个get 方法可能会产生阻塞!把他放到 最后
// 或者使用异步通信来处理!
System.out.println(o);
}
}
class MyThread implements Callable<Integer> {
@Override
public Integer call() {
System.out.println("call()");
return 1024;
}
}
1、有缓存
2、结果可能需要等待,会阻塞!
线程池
线程池:三大方法、七大参数、4种拒绝策略
池化技术
程序的运行,本质:占用系统的资源!优化资源的技术 ===> 池化技术
线程池、连接池、内存池、对象池。。。。创建、销毁。十分浪费资源
池化技术:事先准备好一些资源,有人要用,就来池子里拿,用完之后还过来。
线程池的好处:
1、降低资源的浪费
2、提高响应的速度
3、方便管理。
线程复用、可以控制最大并发数、管理线程
线程池:三大方法
// Executors 工具类、3大方法
public class ExecutorsDemo {
public static void main(String[] args) {
// ExecutorService threadPool = Executors.newSingleThreadExecutor();// 单个线程
// ExecutorService threadPool = Executors.newFixedThreadPool(5);// 创建一个固定的线程池的大小
ExecutorService threadPool = Executors.newCachedThreadPool();// 可伸缩的,遇强则强,遇弱则弱
try {
for (int i = 1; i <= 100; i++) {
// 使用了线程池之后,使用线程池类创建线程
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "---OK");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
三大方法的线程数
ExecutorService threadPool = Executors.newSingleThreadExecutor();// 单个线程
ExecutorService threadPool = Executors.newFixedThreadPool(5);// 创建一个固定的线程池的大小
ExecutorService threadPool = Executors.newCachedThreadPool();// 可伸缩的,遇强则强,遇弱则弱
三大方法源码分析
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
七大参数
通过三大方法的源码可发现:本质都是通过ThreadPoolExecutor
// 本质ThreadPoolExecutor()
public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
int maximumPoolSize, // 最大核心线程池大小
long keepAliveTime, // 超时没有人调用就会释放
TimeUnit unit, // 超时单位
BlockingQueue<Runnable> workQueue, // 阻塞队列
ThreadFactory threadFactory, //线程工厂:创建线程的,一般不用动
RejectedExecutionHandler handler) { //拒绝策略
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
举例,银行共有5个窗口(最大线程数),有常开的两个窗口处理业务(核心线程数);候客区(阻塞队列)有三个位置;当两个常开窗口(核心线程)以及候客区(阻塞队列)的位置都有人后,再来客户会开启另外三个窗口;当5个窗口(最大线程)和候客区(阻塞队列)都有人后,再来客户会启动拒绝策略。当窗口(线程)超过设定时间(超时时间)没有业务办理时(未调用)会关闭窗口(释放线程)。
// Executors 工具类、3大方法
/**
* new ThreadPoolExecutor.AbortPolicy() // 银行满了,还有人进来,不处理这个人的,抛出异 常
* new ThreadPoolExecutor.CallerRunsPolicy() // 哪来的去哪里!
* new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!
* new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试去和最早的竞争,也不会 抛出异常!
*/
public class ExecutorsDemo01 {
public static void main(String[] args) {
// 自定义线程池!工作ThreadPoolExecutor
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试去和 最早的竞争,也不会抛出异常!
try {
// 最大承载:Deque + max
// 超过 RejectedExecutionException
for (int i = 1; i <= 9; i++) {
// 使用了线程池之后,使用线程池来创建线程
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
四种策略
new ThreadPoolExecutor.AbortPolicy() // 银行满了,还有人进来,不处理这个人的,抛出异常
new ThreadPoolExecutor.CallerRunsPolicy() // 哪来的去哪里!
new ThreadPoolExecutor.DiscardPolicy() //队列满了,丢掉任务,不会抛出异常!
new ThreadPoolExecutor.DiscardOldestPolicy() //队列满了,尝试去和最早的竞争,也不会抛出异常!
小结和拓展
问题:池的线程最大值如何去设置!
了解:IO密集型,CPU密集型(调优)
最大线程到底该如何定义
1、CPU 密集型,几核的计算机就设置为几,可以保存CPU的效率最高!
自动获取计算机CPU处理器个数
Runtime.getRuntime().availableProcessors()
2、IO 密集型 —> 判断程序中十分耗IO的线程,根据IO线程数来设定。
比如:15个大型IO线程任务,可以最大线程数为30