当需要执行一些原子性操作的代码时,某个代码块或者方法就需要用到线程锁,但是在代码块或者方法中调用其他原子性方法时,就会出现当前代码块或者方法没有执行完毕,无法释放当前持有的锁,被调用的原子性方法又需要此锁,导致线程被阻塞。
例:
public class Test {
Lock locka = new Lock();
ReentrantLock lock = new ReentrantLock();
public void a() throws InterruptedException {
locka.lock();
System.out.println("aaaa");
b();
locka.unlock();
}
public void b() throws InterruptedException {
locka.lock();
System.out.println("执行方法b");
locka.unlock();
}
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
test.a();
}
}
方法a加了锁,在执行方法时调用方法b,方法b必须持有锁才能执行,但是方法a还没执行完,无法释放持有的锁,导致方法b无法获得锁,导致出现了思索现象。
解决方法:使用可重入锁
方法1:synchronized
public class Test {
Lock locka = new Lock();
ReentrantLock lock = new ReentrantLock();
public synchronized void a() throws InterruptedException {
System.out.println("aaaa");
b();
}
public void b() throws InterruptedException {
System.out.println("执行方法b");
}
public static void main(String[] args) throws InterruptedException {
Test test = new Test();
test.a();
}
}
方法2:可重入锁ReentrantLock
public class Test {
Lock locka = new Lock();
ReentrantLock lock = new ReentrantLock();
public synchronized void a() throws InterruptedException {
lock.lock();
System.out.println("aaaa");
b();
lock.unlock();
}
public void b() throws InterruptedException {
lock.lock();
System.out.println("执行方法b");
lock.unlock();
}
需要注意的是,ReenTrantLock上几次锁,就不要释放几次锁,即lock()方法和unlock()方法是成对出现的,不然会出现资源消耗异常
Thread.join():等待线程,谁调用就等待谁,等待者为当前线程。等待期间
Object.wait();线程挂起,释放对象锁,等待被唤醒,持有资源。
Object.notify():唤醒之前持有相同对象锁的线程,被唤醒的线程继续竞争对象锁,唤醒者必须释放锁,被唤醒者才有机会持有锁。
笔记:
1.若不同线程在不同同步代码块中持有相同对象的锁,那么该线程对变量的改变对于持有相同锁的线程来说是透明可见的。