文章目录
1、线程安全问题产生的原因
- 多线程共享同一资源
- cpu对线程的调度,使得每个线程在就绪、阻塞、运行之间随意切换
比如两个线程同时处于运行状态对同一资源进行操作
2、解决线程安全问题
- 同步代码块(Synchronized)
- 同步方法
1、Synchronized代码块
Synchronized(同步监视器){
//需要被的同步代码
}
同步代码:操作共享数据的代码。(对数据进行增、删、改)
同步监视器:又称为锁,任何一个类的对象都可以充当锁。(多个线程共用一把锁,即一个对象)
2、Synchronized同步方法
public synchronized void run() {
//需要被同步的方法
}
在非静态同步方法中,同步监视器是 this
在静态同步方法中,同步监视器是该类的class对象
(反射)
所以可以同时调用实例同步方法和静态同步方法,因为它们的锁不是同一个。
3、显式同步锁Lock
Lock是接口,ReentrantLock是它的实现类,比较常用
- 创建ReentrantLock实例
- 调用lock(),为当前对象上锁
- 最后调用unlock(),为当前对象解锁
class Task implements Runnable{
private ReentrantLock lock = new ReentrantLock();
private int ticket = 100;
@Override
public void run() {
try{
while (ticket > 0) {
lock.lock();
System.out.println(ticket--);
}
}finally {
lock.unlock();
}
}
}
4、Synchronized与Lock的区别
- Synchronized是隐式锁,进入作用域自动上锁,结束自动释放锁。Lock是显式锁,必须手动上锁与解锁
- Lock只有代码锁,Synchronized有代码锁与方法锁
- Lock有更好的扩展性,并且jvm可以花费较少的时间来调度线程,性能更好
5、总结
- 同步方法与代码块,使得线程安全得以解决,但是降低了程序运行速度。当运行到同步代码块或同步方法时,只有一个线程可以操作代码,其他线程等待,相当于单线程
- 使用Synchronized时,注意使用的范围,不能过大也不能过小
- 一个对象拥有一个锁,所以无法同时调用两个静态同步方法或非静态同步方法
- 解决多线程的安全问题,只要使多线程共享一个锁
3、死锁
1、什么是死锁
不同线程分别占用了对方需要的同步资源不放弃,都等待对方放弃自己所需同步资源,这样就形成了死锁。
从生命周期的角度来说,就是多个线程都进入了阻塞状态,且无法退出阻塞,导致程序无法继续运行。
例子
public static void main(String[] args){
Object obj1 = new Object();
Object obj2 = new Object();
Thread thread = new Thread(){
@Override
public void run() {
synchronized (obj1){
System.out.println(Thread.currentThread().getName()+"获得obj1");
synchronized (obj2){
System.out.println(Thread.currentThread().getName()+"获得obj2");
}
}
}
};
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj2){
System.out.println(Thread.currentThread().getName()+"获得obj2");
synchronized (obj1){
System.out.println(Thread.currentThread().getName()+"获得obj1");
}
}
}
});
thread.start();
thread1.start();
}
2、如何避免死锁
- 尽量避免嵌套同步
- 尽量减少同步资源的定义
- 采取专门的算法
4、线程通信
1、线程通信方法
- wait():一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器
- notify():一旦执行此方法,就会唤醒被wait()的一个线程,有多个线程就唤醒优先级高的
- notifyAll():一旦执行此方法,就会唤醒被wait()的所有线程
PS:
- 三个方法必须使用在同步代码块或同步方法中(Synchronized)
- 这三个方法的调用者必须是同步方法或同步代码块中的同步监视器;否则会出现
IllegalMonitorStateException
异常 - 这三个方法定义在Object对象中,因为任何对象都可以充当同步监视器