死锁:
(1)定义:在多线程中(两个或以上线程),因为资源抢占而造成线程无限等待的问题。由于一个线程可以拥有多把锁,但是一个锁只能被一个线程拥有。
(2)死锁造成的四个因素(同时):
1.互斥条件(一个资源只能被一个线程持有);
2.请求拥有条件(一个线程持有了一个资源之后,又请求另一个资源);
3.不可剥夺条件(一个资源被一个线程拥有之后,如果这个线程不释放此资源,那么其他线程不能强制获得此资源);
4.环路等待条件(多个线程在获取资源时形成了一个环形链);
(3)如何解决死锁的问题?
从以下两个条件入手,修改以下条件任意一个:
1.请求拥有条件
2.环路等待条件:
解决死锁可以通过控制获取锁的顺序来解决死锁问题(破坏环路等待条件)。
线程等待:
sleep休眠缺陷:必须传递一个明确的结束时间。
线程的通讯机制:一个线程的动作可以让另一个线程感知到。
wait(线程休眠)/notify(线程唤醒)/nofifyAll(唤醒当前锁对应的全部线程)
1.在使用以上方法时必须要加锁;
wait为什么要加锁?
因为在进行wait操作时,会先释放锁;
为什么要释放锁?
因为wait在不传任何参数时,默认是永久等待,这样会造成一个资源一直被占用。
2.加锁对象和以上方法的加锁对象必须保持一致;
3.一组wait(休眠)/notify(唤醒)/nofifyAll(唤醒全部)必须加同一把锁;
4.notifyAll只能唤醒当前对象对应的线程;
sleep(0)和wait(0)的区别:
1.sleep是Thread的一个静态方法;而wait是Object的方法;
2.sleep(0)立即触发一次CPU资源的抢占,wait(0)代表永久的等待下去;
wait和sleep区别:
相同点:
都可以让当前线程休眠;
都必须要处理一个Interrupt的异常;
不同点:
wait来自于Object中的一个方法,而sleep来自Thread类;
传参不同,wait可以没有参数,而sleep必须有一个大于0的参数;
wait必须要加锁,sleep使用时不用加锁;
wait使用时会释放锁,sleep没有锁;
wait默认不传参的情况下,会进入WAITING状态,sleep会进入TIMEDWAITING状态;
为什么wait会放到Object中而不是Thread?
wait必须要加锁和释放锁,而锁属于对象级别,而非线程级别(一个线程线程拥有多把锁),为了灵活起见(释放哪把锁),就把wait放在Object中。
LockSupport.park()方法——新版本休眠——WAITING——不用加锁,不用处理异常
LockSupport.unpark(线程)方法——新版本唤醒休眠——可以控制线程唤醒顺序,不能和notify等混用
LockSupport.parkUntil(休眠时间)与wait区别:
相同点:
1.都可以线程休眠;
2.都可以传参不传参,并且线程状态一致;
不同点:
1.wait必须要配合synchronized一起使用(加锁),而LockSupport不用;
2.wait只能唤醒全部和随机的一个线程,而LockSupport可以唤醒指定线程;
线程池:
1.线程的创建需要开辟内存资源:本地方法栈、Java虚拟机栈、程序计数器等线程私有变量的内存,频繁的创建和销毁会造成性能问题;
2.使用线程不能很好的管理任务和友好的拒绝任务;
定义:使用池化技术来管理和使用线程的技术就叫做线程池。
线程池的创建方式总共包含七种:
方式一:创建固定个数的线程池;Executors——当对当前代码执行很了解,知道执行多少个任务
public static void main(String[] args) {
//创建固定个数的线程池
ExecutorService executorService=Executors.newFixedThreadPool(5);
//执行一个任务
/* executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程名"+Thread.currentThread().getName());
}
});*/
//执行多个任务
for (int i = 0; i < 10; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程名"+Thread.currentThread().getName());
}
});
}
}
创建了10个线程的线程池来执行两个任务,实际创建了几个线程?答案:2个
线程池执行流程:当拿到一个任务之后,会判断当前线程池里面的线程数量是否达到最大值,如果没有就创建新的线程执行任务;当任务来了之后,线程池的线程数量已经是最大值,并且没有空闲线程,那么会将任务放到任务队列中等待执行;
线程池里面的两个重要任务:
1.线程;
2.任务队列;
方式二:创建带缓存的线程池;——当有大量短期任务的时候适用,根据任务的数量来创建对应的线程数;
public static void main(String[