Примеры Java - Взаимоблокировка и решения
Примеры Java
Взаимоблокировка — это ситуация, когда несколько потоков одновременно блокируются, и один или все они ожидают освобождения ресурса. Поскольку потоки заблокированы бессрочно, программа не может нормально завершиться.
Четыре необходимых условия взаимоблокировки в Java:
- 1. Взаимное исключение: когда ресурс используется (занят) одним потоком, другие потоки не могут его использовать.
- 2. Отсутствие вытеснения: запрашивающий ресурс не может принудительно забрать ресурс у владельца ресурса; ресурс может быть освобождён только добровольно владельцем ресурса.
- 3. Удержание и запрос: запрашивающий ресурс продолжает удерживать уже имеющиеся у него ресурсы, запрашивая другие ресурсы.
- 4. Циклическое ожидание: существует очередь ожидания: P1 занимает ресурс P2, P2 занимает ресурс P3, а P3 занимает ресурс P1. Это образует цикл ожидания.
Когда выполняются все четыре вышеуказанных условия, возникает взаимоблокировка. Конечно, в ситуации взаимоблокировки нарушение любого из вышеперечисленных условий может устранить взаимоблокировку. Следующий Java-код имитирует возникновение взаимоблокировки.
Способы решения взаимоблокировки: один — использовать synchronized, другой — использовать явную реализацию блокировки Lock.
Однако если блокировки используются неправильно и требуется одновременно заблокировать несколько объектов, может возникнуть взаимоблокировка, как показано ниже:
Файл LockTest.java
import java.util.Date;
public class LockTest {
public static String obj1 = "obj1";
public static String obj2 = "obj2";
public static void main(String[] args) {
LockA la = new LockA();
new Thread(la).start();
LockB lb = new LockB();
new Thread(lb).start();
}
}
class LockA implements Runnable{
public void run() {
try {
System.out.println(new Date().toString() + "LockA начинает выполнение");
while(true){
synchronized (LockTest.obj1) {
System.out.println(new Date().toString() + "LockA захватывает блокировку obj1");
Thread.sleep(3000);
synchronized (LockTest.obj2) {
System.out.println(new Date().toString() + "LockA захватывает блокировку obj2");
Thread.sleep(60 * 1000);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class LockB implements Runnable{
public void run() {
try {
System.out.println(new Date().toString() + "LockB начинает выполнение");
while(true){
synchronized (LockTest.obj2) {
System.out.println(new Date().toString() + "LockB захватывает блокировку obj2");
Thread.sleep(3000);
synchronized (LockTest.obj1) {
System.out.println(new Date().toString() + "LockB захватывает блокировку obj1");
Thread.sleep(60 * 1000);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Приведённый выше код выдаёт следующий вывод:
Tue May 05 10:51:06 CST 2015 LockB 开始执行
Tue May 05 10:51:06 CST 2015 LockA 开始执行
Tue May 05 10:51:06 CST 2015 LockB 锁住 obj2
Tue May 05 10:51:06 CST 2015 LockA 锁住 obj1
В этот момент возникает взаимоблокировка.
Для решения этой задачи мы не используем явные блокировки; мы используем семафоры для управления.
Семафор может управлять тем, сколько потоков могут получить доступ к ресурсу. Здесь мы указываем, что только один поток может получить к нему доступ, таким образом достигая чего-то похожего на блокировку. Семафор также может задавать тайм-аут для его получения, и мы можем выполнять дополнительную обработку на основе этого тайм-аута.
В случаях, когда получение не удалось, обычный подход — повторять попытки многократно, либо задать количество попыток, либо немедленно выйти.
Давайте посмотрим на следующий код:
Файл UnLockTest.java
import java.util.Date;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class UnLockTest {
public static String obj1 = "obj1";
public static final Semaphore a1 = new Semaphore(1);
public static String obj2 = "obj2";
public static final Semaphore a2 = new Semaphore(1);
public static void main(String[] args) {
LockAa la = new LockAa();
new Thread(la).start();
LockBb lb = new LockBb();
new Thread(lb).start();
}
}
class LockAa implements Runnable {
public void run() {
try {
System.out.println(new Date().toString() + "LockA начинает выполнение");
while (true) {
if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) {
System.out.println(new Date().toString() + "LockA блокирует obj1");
if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) {
System.out.println(new Date().toString() + "LockA захватывает блокировку obj2");
Thread.sleep(60 * 1000);
}else{
System.out.println(new Date().toString() + "LockA не может заблокировать obj2");
}
}else{
System.out.println(new Date().toString() + "LockA не может заблокировать obj1");
}
UnLockTest.a1.release();
UnLockTest.a2.release();
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class LockBb implements Runnable {
public void run() {
try {
System.out.println(new Date().toString() + "LockB начинает выполнение");
while (true) {
if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) {
System.out.println(new Date().toString() + "LockB блокирует obj2");
if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) {
System.out.println(new Date().toString() + "LockB захватывает блокировку obj1");
Thread.sleep(60 * 1000);
}else{
System.out.println(new Date().toString() + "LockB не может заблокировать obj1");
}
}else{
System.out.println(new Date().toString() + "LockB не может заблокировать obj2");
}
UnLockTest.a1.release();
UnLockTest.a2.release();
Thread.sleep(10 * 1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Вывод приведенного выше примера кода:
Tue May 05 10:59:13 CST 2015 LockA 开始执行
Tue May 05 10:59:13 CST 2015 LockB 开始执行
Tue May 05 10:59:13 CST 2015 LockB 锁住 obj2
Tue May 05 10:59:13 CST 2015 LockA 锁住 obj1
Tue May 05 10:59:14 CST 2015LockB 锁 obj1 失败
Tue May 05 10:59:14 CST 2015LockA 锁 obj2 失败
Tue May 05 10:59:15 CST 2015 LockA 锁住 obj1
Tue May 05 10:59:15 CST 2015 LockA 锁住 obj2
Примеры Java
Другие расширения