Примеры Java - Взаимоблокировка и решения

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); //Ожидание здесь нужно, чтобы дать B шанс захватить блокировку 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); //Ожидание здесь нужно, чтобы дать A шанс захватить блокировку 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(); //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(); //Release UnLockTest.a2.release(); Thread.sleep(10 * 1000); //Это только для демонстрации, поэтому tryAcquire использует только 1 секунду, и B должен уступить время выполнения A; иначе эти двое всегда будут в тупике. } } 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 实例Примеры Java

Другие расширения