文章目录
回顾:
线程:
------三种创建线程的方式
------线程的常用方法
------线程的分类
------线程通讯的方式
-------线程带来的问题
a)线程安全问题:加锁
b)死锁
造成死锁的四个条件:
1.互斥条件
2.拥有请求条件
3.不可剥夺条件
4.环路等待条件-------->按序请求锁
线程的缺点:
1.线程的创建需要开辟内存资源:本地方法栈,虚拟机栈,程序计数器等线程私有变量的内存,频繁的创建和消耗会带来一定的性能开销
2.使用线程不能很好的管理任务和友好的拒绝任务。
3.线程资源必须通过线程池提供,不允许在应用中自行显示创建线程,线程池的好处是减少在创建和销毁线程上所损耗的时间以及系统资源的开销,解决资源不足的问题。不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
线程池:
定义:使用池化技术来管理和使用线程的技术,就叫线程池。
线程池里面包含的重要内容(2个):
1.线程
2.任务队列
线程池的优点:
1.避免频繁创建和销毁带来的性能开销
2.可以友好地拒绝任务
3.可以有更多的功能:执行定时任务
线程池的创建方式(7种)
第一种:创建固定个数的线程池
任务数取向无限大,建议谨慎使用
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadMain45 {
public static void main(String[] args) {
//创建固定个数的线程池
ExecutorService service= Executors.newFixedThreadPool(10);
for (int i = 0; i <10 ; i++) {
//执行内容
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程名:"+
Thread.currentThread().getName());
}
});
}
}
}
经典易错题:创建了10个线程来执行2个任务,问当前程序创建了几个线程?
答:启动两个线程
线程池的执行流程:当拿到一个任务后,会判断当前线程池里面的线程数量是否达到了最大值,如果没有达到,创建新的线程执行任务;当任务来了之后,线程线程池的线程数量已经是最大值,并且没有空闲线程,当前任务会被放到线程池的任务队列里等待执行。
自定义线程池行为(设置线程池命名规则,线程池的优先级)
方法二:创建带缓存的线程池
根据任务的数量生成对应的线程数,适用于短期大量任务
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 创建带缓存的线程池
*/
public class ThreadPoolMain47 {
public static void main(String[] args) {
ExecutorService service= Executors.newCachedThreadPool();
for (int i = 0; i <10 ; i++) {
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程名:"+
Thread.currentThread().getName());
}
});
}
}
}
使用场景:有大量的短期任务的时候
方法三:创建可以执行定时任务的线程池
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolMain48 {
public static void main(String[] args) {
//创建一个执行定时任务的线程池
ScheduledExecutorService service= Executors.newScheduledThreadPool(10);
System.out.println("执行任务之前:"+ new Date());
//执行任务
service.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:"+ new Date());
}
},1,3, TimeUnit.SECONDS);
}
}
参数1:线程池的任务
参数2:定时任务延迟多长时间开始执行
参数3:定时任务的执行频率
参数4:配合参数2和参数3使用的时间单位
schedule区别:
1.没有延迟执行的时间设置
2.定时任务只能执行一次
package thread.thread_0519;
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolMain50 {
public static void main(String[] args) {
//创建一个执行定时任务的线程池
ScheduledExecutorService service= Executors.newScheduledThreadPool(10);
System.out.println("执行任务之前:"+ new Date());
//执行任务
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:"+ new Date());
}
},1,3, TimeUnit.SECONDS);
}
}
scheduleAtFixedRate 和cheduleWithFixedDelay的区别:
scheduleAtFixedRate任务开始时间是以上次任务的开始时间做起始时间的(固定频率执行)
scheduleWithFixedDelay任务开始执行时间是以上次任务执行的结束时间作为开始时间的(时间不固定,根据任务执行时间来定)
方法四:创建单个执行定时任务的线程池
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolMain51 {
public static void main(String[] args) {
// 创建单个执行定时任务的线程池
ScheduledExecutorService service =
Executors.newSingleThreadScheduledExecutor();
// 开启定时任务
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行任务:" + new Date());
}
}, 1, 3, TimeUnit.SECONDS);
}
}
单个线程的线程池有什么意义?
答:1.无需频繁的创建和销毁线程
2.可以更好的分配,管理以及存储任务(任务队列)
方法五:单个线程的线程池
a:频繁的创建和消耗;
b:更好的分配和执行任务,并且可以将任务存放到任务队列
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolMain52 {
//创建单个线程的线程池
public static void main(String[] args) {
ExecutorService service= Executors.newSingleThreadExecutor();
for (int i = 0; i <10 ; i++) {
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程名:"+
Thread.currentThread().getName());
}
});
}
}
}
方法六(JDK 8+):根据当前工作环境(CPU核心数,任务量)创建异步线程池
Synchronization:同步
同步:按照某种规格按序执行的就叫同步
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadMain52 {
public static void main(String[] args) {
ExecutorService service= Executors.newWorkStealingPool();
for (int i = 0; i < 10; i++) {
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程名:"+Thread.currentThread().getName());
}
});
}
//等待异步线程池执行完成(根据线程池的终止状态)
while (!service.isTerminated()){
}
}
}
同步执行的流程:
1.main调用线程池
2.线程池执行完之后
3.关闭线程池,main线程也随之关闭
异步执行流程:
1.main调用异步线程池
2.异步线程池后台执行,对与main线程来说异步线程池已经执行完成,关闭main线程
线程池不允许使用Executors去创建,而是通过ThreadPoolExcutor的方式。这样的处理方式可以更加明确线程池的运行规则,规避资源耗尽的风险。
最大运行内存:10M
Excutors创建线程池的问题:
1.线程数量不可控(线程的过度切换和争取---->程序执行比较慢)
2.任务数量不可控(任务数无限大,Integer.MAX_VALUE)当任务量比较大的情况下会造成内存溢出异常(OOM)。