在java中,如果开启多个线程同时读写一个对象,会导致数据不正常。为了保证数据的安全,我们会使用锁来控制线程的访问。
synchronized
synchronized,互斥锁,当一个线程持有锁时,另一个线程无法持有,只有当方法执行完,锁被释放时,其他线程才有机会获取锁。
synchronized的使用有两种方式:一种是用在方法上,一种是用在代码块上。
public synchronized void method ();
public void method () {
synchronized(obj){
}
}
当其用在方法上时,线程调用该方法会获取其实例的锁,效果等同于在代码块上锁住了自己的实例。
synchronized(this){
}
所以除非保证该方法的实例唯一,或者为static修饰(static修饰时,对象锁为改类的锁),否则无法保证互斥。也就是说,只能互斥同一个对象锁的线程。
wait
wait,使线程释放当前持有的对象锁,并进入等待状态,后续代码不再执行,其他线程可争夺该对象锁。
notify
notify,唤醒其他因调用其对象锁进入wait状态的线程。执行完后续代码之后释放对象锁。若有多个调用其对象进入wait状态的线程,则根据系统的实现,唤醒其中一个。
notifyAll
notifyAll,唤醒所有其他因调用其对象锁进入wait状态的线程。执行完后续代码之后释放对象锁。根据系统实现决定先唤醒哪一个。
注意
只有线程本身持有该对象锁,才能执行wait/notify/notifyAll方法,比如:
synchronized(obj){
obj.wait();
}
不然会报异常:IllegalMonitorStateException。
死锁
有两个线程,分别持有两个对象锁,并且互相申请对方的对象锁时,会发生死锁。
比如:
synchronized(a){
try{
Thread.sleep(1000);
}catch(Exception e){
}
synchronized(b){
}
}
synchronized(b){
try{
Thread.sleep(1000);
}catch(Exception e){
}
synchronized(a){
}
}
两个线程分别持有对象锁a、b,之后互相申请对方的对象锁b、a,这个时候因为双方都无法释放自己的对象锁,同时对方无法获取对象锁,就会进入锁死BLOCKED状态。
线程可以持有多个对象锁,并且当其调用wait释放其中一个对象锁时,其扔持有其他对象锁。比如:
synchronized(a){
synchronized(b){
b.wait();
}
}
当线程调用b的对象锁进入等待状态,并释放了对象锁b,这个时候依然保持着持有对象锁a,当此线程被唤醒并释放对象锁a之前,其他线程依然无法获取对象锁a。
网友评论