多个线程并行时容易引发并发问题
当多条线程处理同一份资源时,就容易引发线程安全问题,就是并发问题,
1. 同步块
使用synchronized (){}包裹一段代码,这个处理被称为同步代码块,
格式
//obj是一个对象,
synchronized( Obj){
......
//这里就是同步代码块
}
Obj叫做监视器 ,线程开始执行这里时,必须先获得同步代监视器的锁定,一个线程执行完成之后,就会释放对监视器的锁定,这时其他的线程才可以访问这段代码,这样就可以保证只有一个线程访问共享资源,就不会出现错误了,相当于一个人进去,锁了门,其他人排队等候,保证了安全,不会多个线程同时处理。但是要注意,选作监视器的类,在不同的线程访问时,必须时同一个监视器,不然,就是失去了锁的意义
2. 同步方法
用sychronized修饰方法,方法就变成了一个同步方法,对于一个非static方法,监视器就是this ,就是调用刚发的对象,同步方法可以方便的实现线程安全的类,
一个线程安全的类:
该类的对象可以被多个线程安全访问
调用该类的任何方法,都可以得到一个正确的结果
每个线程调用该对象的任何方法,该对象依然保持合理的状态
加了锁的方法一次只允许一个线程执行
3、释放同步监视器的锁定
下面情况会释放同步监视器的锁定
- 同步代码块中的语句执行结束,或遇到break,return 等结束类其中的运行
- 出现了error或抛出Exception或方法异常结束
- 执行了监视器的wait()的方法,当前线程暂停,释放监视器
下面情况不会释放
- 调用了sleep(0方法,yield()方法,线程会暂停但不会释放监视器的锁定。
- 其他线程使用了suspend()将线程挂起,县城不会释放监视器
死锁
我们要尽量避免死锁
假如有两个线程各自都拿到了一个对方需要的锁,同时又各自需要对方的锁,两方因为无法得到需要的锁,都无法先释放自己拿到的锁,此时线程就在等待,不结束也不抛异常,只是阻塞状态。
//用来创建监视器
class A{
}
public class Demo01 {
public static void main(String[] args) {
//两个监视器
A a=new A();
A b=new A();
//这个线程先使用a锁,后使用b锁,之后再释放b,在释放a
new Thread(
()->{
synchronized (a){
try {
//停一下,放大锁住的概率
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
准备拿b锁
synchronized (b){
}
}
}
).start();
//这个线程先使用b锁,后使用a锁,之后再释放a,在释放b
new Thread(
()->{
synchronized (b){
try {
Thread.sleep(1000);//放大概率
} catch (InterruptedException e) {
e.printStackTrace();
}
准备拿a锁
synchronized (a){
}
}
}
).start();
}
}
这时,线程1,先拿到a,准备拿b,线程2,先拿到b准备拿a,但是,要拿的却都被对方拿了,都在等对方放手,自己拿到,再结束线程,但都拿不到,所以就僵住了,都阻塞了。
使用多线程时,要注意处理并发时适当枷锁,但也要注意出现死锁。