Je voudrais expliquer les impasses de filetage aux débutants. J'ai vu de nombreux exemples de blocages dans le passé, certains utilisant du code et d'autres des illustrations (comme la célèbre 4 voitures ). Il existe également des problèmes classiques, facilement bloqués, comme The Dining Philosophers , mais ils sont peut-être trop complexes pour qu'un vrai débutant puisse les comprendre parfaitement.
Je cherche l'exemple de code le plus simple pour illustrer ce que sont les blocages. L'exemple devrait:
Que recommandez-vous?
Peut-être une simple situation bancaire.
class Account {
double balance;
void withdraw(double amount){
balance -= amount;
}
void deposit(double amount){
balance += amount;
}
void transfer(Account from, Account to, double amount){
sync(from);
sync(to);
from.withdraw(amount);
to.deposit(amount);
release(to);
release(from);
}
}
De toute évidence, s'il y a deux threads qui tentent d'exécuter le transfert ( a, b ) et le transfert ( b, a ) en même temps, un blocage se produira alors qu'ils tenteront d'acquérir le les ressources dans l'ordre inverse.
Ce code est également idéal pour rechercher des solutions à l'impasse. J'espère que cela t'aides!
Laissez la nature expliquer l'impasse,
Impasse: Grenouille vs Serpent
"J'adorerais les avoir vus se séparer, mais je suis épuisé", a déclaré le photographe. "La grenouille essayait tout le temps deRetirer le serpent, mais le serpentNe le lâcherait pas".
Voici un exemple de code de le département informatique d’une université de Taiwan montrant un exemple simple en Java avec verrouillage des ressources. C'est très "réel" pertinent pour moi. Code ci-dessous:
/**
* Adapted from The Java Tutorial
* Second Edition by Campione, M. and
* Walrath, K.Addison-Wesley 1998
*/
/**
* This is a demonstration of how NOT to write multi-threaded programs.
* It is a program that purposely causes deadlock between two threads that
* are both trying to acquire locks for the same two resources.
* To avoid this sort of deadlock when locking multiple resources, all threads
* should always acquire their locks in the same order.
**/
public class Deadlock {
public static void main(String[] args){
//These are the two resource objects
//we'll try to get locks for
final Object resource1 = "resource1";
final Object resource2 = "resource2";
//Here's the first thread.
//It tries to lock resource1 then resource2
Thread t1 = new Thread() {
public void run() {
//Lock resource 1
synchronized(resource1){
System.out.println("Thread 1: locked resource 1");
//Pause for a bit, simulating some file I/O or
//something. Basically, we just want to give the
//other thread a chance to run. Threads and deadlock
//are asynchronous things, but we're trying to force
//deadlock to happen here...
try{
Thread.sleep(50);
} catch (InterruptedException e) {}
//Now wait 'till we can get a lock on resource 2
synchronized(resource2){
System.out.println("Thread 1: locked resource 2");
}
}
}
};
//Here's the second thread.
//It tries to lock resource2 then resource1
Thread t2 = new Thread(){
public void run(){
//This thread locks resource 2 right away
synchronized(resource2){
System.out.println("Thread 2: locked resource 2");
//Then it pauses, for the same reason as the first
//thread does
try{
Thread.sleep(50);
} catch (InterruptedException e){}
//Then it tries to lock resource1.
//But wait! Thread 1 locked resource1, and
//won't release it till it gets a lock on resource2.
//This thread holds the lock on resource2, and won't
//release it till it gets resource1.
//We're at an impasse. Neither thread can run,
//and the program freezes up.
synchronized(resource1){
System.out.println("Thread 2: locked resource 1");
}
}
}
};
//Start the two threads.
//If all goes as planned, deadlock will occur,
//and the program will never exit.
t1.start();
t2.start();
}
}
Un exemple de blocage simple que j'ai rencontré.
public class SimpleDeadLock {
public static Object l1 = new Object();
public static Object l2 = new Object();
private int index;
public static void main(String[] a) {
Thread t1 = new Thread1();
Thread t2 = new Thread2();
t1.start();
t2.start();
}
private static class Thread1 extends Thread {
public void run() {
synchronized (l1) {
System.out.println("Thread 1: Holding lock 1...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 1: Waiting for lock 2...");
synchronized (l2) {
System.out.println("Thread 2: Holding lock 1 & 2...");
}
}
}
}
private static class Thread2 extends Thread {
public void run() {
synchronized (l2) {
System.out.println("Thread 2: Holding lock 2...");
try { Thread.sleep(10); }
catch (InterruptedException e) {}
System.out.println("Thread 2: Waiting for lock 1...");
synchronized (l1) {
System.out.println("Thread 2: Holding lock 2 & 1...");
}
}
}
}
}
Si method1 () et method2 () seront appelés par deux ou plusieurs threads, il y a de fortes chances pour que le blocage se produise car si le thread 1 acquiert le verrou sur l'objet String lors de l'exécution de method1 () et que le thread 2 acquiert le verrouillage sur l'objet Integer lors de l'exécution de la méthode2 () les deux attendront mutuellement pour libérer le verrou sur Integer et String pour continuer, ce qui ne se produira jamais.
public void method1() {
synchronized (String.class) {
System.out.println("Acquired lock on String.class object");
synchronized (Integer.class) {
System.out.println("Acquired lock on Integer.class object");
}
}
}
public void method2() {
synchronized (Integer.class) {
System.out.println("Acquired lock on Integer.class object");
synchronized (String.class) {
System.out.println("Acquired lock on String.class object");
}
}
}
Voici un exemple simple en C++ 11.
#include <mutex> // mutex
#include <iostream> // cout
#include <cstdio> // getchar
#include <thread> // this_thread, yield
#include <future> // async
#include <chrono> // seconds
using namespace std;
mutex _m1;
mutex _m2;
// Deadlock will occur because func12 and func21 acquires the two locks in reverse order
void func12()
{
unique_lock<mutex> l1(_m1);
this_thread::yield(); // hint to reschedule
this_thread::sleep_for( chrono::seconds(1) );
unique_lock<mutex> l2(_m2 );
}
void func21()
{
unique_lock<mutex> l2(_m2);
this_thread::yield(); // hint to reschedule
this_thread::sleep_for( chrono::seconds(1) );
unique_lock<mutex> l1(_m1);
}
int main( int argc, char* argv[] )
{
async(func12);
func21();
cout << "All done!"; // this won't be executed because of deadlock
getchar();
}
S'il vous plaît voir ma réponse à cette question . En bout de ligne, chaque fois que deux threads doivent acquérir deux ressources différentes et le faire dans des ordres différents, vous pouvez obtenir des blocages.
Un exemple auquel je peux penser est le scénario Table, Lampe de poche et Piles. Imaginez une lampe de poche et une paire de piles placées sur une table. Si vous marchiez vers cette table et attrapiez les piles pendant qu'une autre personne a la lampe de poche, vous serez forcés de vous regarder l'un l'autre avec gêne en attendant de savoir qui remettra le premier leur article sur la table. Ceci est un exemple d'impasse. Vous et la personne attendiez des ressources, mais aucun d’entre vous n’abandonnera ses ressources.
De même, dans un programme, un blocage se produit lorsque deux ou plusieurs threads (vous et l’autre personne) attendent la libération de deux verrous (lampe de poche et piles) et que les circonstances dans le programme sont telles que les verrous ne le sont jamais ( vous avez tous les deux une pièce du puzzle).
Si vous connaissez Java, voici comment vous pouvez représenter ce problème:
import Java.util.concurrent.locks.*;
public class Deadlock1 {
public static class Table {
private static Lock Flashlight = new ReentrantLock();
private static Lock Batteries = new ReentrantLock();
public static void giveFlashLightAndBatteries() {
try {
Flashlight.lock();
Batteries.lock();
System.out.println("Lights on");
} finally {
Batteries.unlock();
Flashlight.unlock();
}
}
public static void giveBatteriesAndFlashLight() {
try {
Batteries.lock();
Flashlight.lock();
System.out.println("Lights on");
} finally {
Flashlight.unlock();
Batteries.unlock();
}
}
}
public static void main(String[] args) {
// This thread represents person one
new Thread(new Runnable() {
public void run() { Table.giveFlashLightAndBatteries(); }
}).start();
// This thread represents person two
new Thread(new Runnable() {
public void run() { Table.giveBatteriesAndFlashLight(); }
}).start();
}
}
Si vous utilisez cet exemple, vous remarquerez que parfois les choses fonctionnent correctement et correctement. Mais parfois, votre programme n’imprimera rien. En effet, une personne dispose des piles tandis qu'une autre dispose de la lampe de poche, ce qui l'empêche d'allumer la lampe de poche et de provoquer un blocage.
Cet exemple est similaire à celui donné par les tutoriels Java: http://docs.Oracle.com/javase/tutorial/essential/concurrency/deadlock.html
Un autre exemple est l'exemple de la boucle:
public class Deadlock2 {
public static class Loop {
private static boolean done = false;
public static synchronized void startLoop() throws InterruptedException {
while(!done) {
Thread.sleep(1000);
System.out.println("Not done");
}
}
public static synchronized void stopLoop() {
done = true;
}
}
public static void main(String[] args) {
// This thread starts the loop
new Thread(new Runnable() {
public void run() {
try {
Loop.startLoop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
// This thread stops the loop
new Thread(new Runnable() {
public void run() {
Loop.stopLoop();
}
}).start();
}
}
Cet exemple peut soit imprimer "Non fait" maintes et maintes fois, soit il ne peut jamais imprimer "Non fait" du tout. La première survient parce que le premier thread acquiert le verrou de classe et ne le libère jamais, empêchant ainsi l'accès à 'stopLoop' par le second thread. Et le dernier se produit parce que le second thread a démarré avant le premier thread, ce qui a pour effet que la variable "done" est vraie avant que le premier thread ne soit exécuté.
Je me suis récemment rendu compte que les conflits entre couples ne sont qu'une impasse ... où l'un des processus doit s'effondrer pour le résoudre, bien sûr, il s'agit de la priorité la moins importante (garçon;)).
Voici l'analogie ...
Process1: Fille (G) Process2: Garçon (B)
Resource1: Sorry Resource2: Accepter sa propre erreur
Conditions nécessaires:
1. Exclusion mutuelle: Seul l'un des G ou B peut s'excuser ou accepter sa propre erreur à la fois.
2. Attente et attente: À un moment donné, on tient Désolé et les autres en acceptant sa propre erreur, on attend Attend en acceptant sa propre erreur pour se libérer en désolée, et l'autre en attente pour désolé de se libérer en acceptant sa propre erreur.
3. Pas de préemption: Même Dieu ne peut forcer B ou G à libérer Désolé ou accepter sa propre erreur. Et volontairement? Vous plaisantez j'espère??
4. Circulaire Wait: Encore une fois, celui qui détient des excuses attend que les autres acceptent ses propres erreurs, et un qui accepte ses propres erreurs veut que l'autre s'excuse en premier. Donc c'est circulaire.
Donc, des blocages se produisent lorsque toutes ces conditions sont en vigueur en même temps, et c'est toujours le cas dans un combat de couple;)
Source: http://www.quora.com/Saurabh- Pandey-3/Posts/Never-ending-couple-fights-a-deadlock
Encore un exemple simple d'interblocage avec deux ressources différentes et deux threads en attente l'un de l'autre pour libérer la ressource. Directement à partir de examples.oreilly.com/jenut/Deadlock.Java
public class Deadlock {
public static void main(String[] args) {
// These are the two resource objects we'll try to get locks for
final Object resource1 = "resource1";
final Object resource2 = "resource2";
// Here's the first thread. It tries to lock resource1 then resource2
Thread t1 = new Thread() {
public void run() {
// Lock resource 1
synchronized(resource1) {
System.out.println("Thread 1: locked resource 1");
// Pause for a bit, simulating some file I/O or something.
// Basically, we just want to give the other thread a chance to
// run. Threads and deadlock are asynchronous things, but we're
// trying to force deadlock to happen here...
try { Thread.sleep(50); } catch (InterruptedException e) {}
// Now wait 'till we can get a lock on resource 2
synchronized(resource2) {
System.out.println("Thread 1: locked resource 2");
}
}
}
};
// Here's the second thread. It tries to lock resource2 then resource1
Thread t2 = new Thread() {
public void run() {
// This thread locks resource 2 right away
synchronized(resource2) {
System.out.println("Thread 2: locked resource 2");
// Then it pauses, for the same reason as the first thread does
try { Thread.sleep(50); } catch (InterruptedException e) {}
// Then it tries to lock resource1. But wait! Thread 1 locked
// resource1, and won't release it 'till it gets a lock on
// resource2. This thread holds the lock on resource2, and won't
// release it 'till it gets resource1. We're at an impasse. Neither
// thread can run, and the program freezes up.
synchronized(resource1) {
System.out.println("Thread 2: locked resource 1");
}
}
}
};
// Start the two threads. If all goes as planned, deadlock will occur,
// and the program will never exit.
t1.start();
t2.start();
}
}
Je considère cependant que le problème de Dining Philosophers est l’un des exemples les plus simples en ce qui concerne les impasses, étant donné que les quatre critères d’impasse peuvent être facilement illustrés par le dessin (en particulier l’attente circulaire).
Je considère que les exemples du monde réel sont beaucoup plus déroutants pour le débutant, bien que je ne puisse pas penser à un bon scénario du monde réel en ce moment (je suis relativement inexpérimenté avec la concurrence du monde réel).
public class DeadLock {
public static void main(String[] args) throws InterruptedException {
Thread mainThread = Thread.currentThread();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
mainThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread1.start();
thread1.join();
}
}
Exemple simple tiré de https://docs.Oracle.com/javase/tutorial/essential/concurrency/deadlock.html
public class Deadlock {
public static void printMessage(String message) {
System.out.println(String.format("%s %s ", Thread.currentThread().getName(), message));
}
private static class Friend {
private String name;
public Friend(String name) {
this.name = name;
}
public void bow(Friend friend) {
printMessage("Acquiring lock on " + this.name);
synchronized(this) {
printMessage("Acquired lock on " + this.name);
printMessage(name + " bows " + friend.name);
friend.bowBack(this);
}
}
public void bowBack(Friend friend) {
printMessage("Acquiring lock on " + this.name);
synchronized (this) {
printMessage("Acquired lock on " + this.name);
printMessage(friend.name + " bows back");
}
}
}
public static void main(String[] args) throws InterruptedException {
Friend one = new Friend("one");
Friend two = new Friend("two");
new Thread(new Runnable() {
@Override
public void run() {
one.bow(two);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
two.bow(one);
}
}).start();
}
}
Sortie:
Thread-0 Acquiring lock on one
Thread-1 Acquiring lock on two
Thread-0 Acquired lock on one
Thread-1 Acquired lock on two
Thread-1 two bows one
Thread-0 one bows two
Thread-1 Acquiring lock on one
Thread-0 Acquiring lock on two
Thread Dump:
2016-03-14 12:20:09
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.74-b02 mixed mode):
"DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x00007f472400a000 nid=0x3783 waiting on condition [0x0000000000000000]
Java.lang.Thread.State: RUNNABLE
"Thread-1" #12 prio=5 os_prio=0 tid=0x00007f472420d800 nid=0x37a3 waiting for monitor entry [0x00007f46e89a5000]
Java.lang.Thread.State: BLOCKED (on object monitor)
at com.anantha.algorithms.ThreadJoin$Friend.bowBack(ThreadJoin.Java:102)
- waiting to lock <0x000000076d0583a0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$Friend.bow(ThreadJoin.Java:92)
- locked <0x000000076d0583e0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$2.run(ThreadJoin.Java:141)
at Java.lang.Thread.run(Thread.Java:745)
"Thread-0" #11 prio=5 os_prio=0 tid=0x00007f472420b800 nid=0x37a2 waiting for monitor entry [0x00007f46e8aa6000]
Java.lang.Thread.State: BLOCKED (on object monitor)
at com.anantha.algorithms.ThreadJoin$Friend.bowBack(ThreadJoin.Java:102)
- waiting to lock <0x000000076d0583e0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$Friend.bow(ThreadJoin.Java:92)
- locked <0x000000076d0583a0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$1.run(ThreadJoin.Java:134)
at Java.lang.Thread.run(Thread.Java:745)
"Monitor Ctrl-Break" #10 daemon prio=5 os_prio=0 tid=0x00007f4724211000 nid=0x37a1 runnable [0x00007f46e8def000]
Java.lang.Thread.State: RUNNABLE
at Java.net.SocketInputStream.socketRead0(Native Method)
at Java.net.SocketInputStream.socketRead(SocketInputStream.Java:116)
at Java.net.SocketInputStream.read(SocketInputStream.Java:170)
at Java.net.SocketInputStream.read(SocketInputStream.Java:141)
at Sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.Java:284)
at Sun.nio.cs.StreamDecoder.implRead(StreamDecoder.Java:326)
at Sun.nio.cs.StreamDecoder.read(StreamDecoder.Java:178)
- locked <0x000000076d20afb8> (a Java.io.InputStreamReader)
at Java.io.InputStreamReader.read(InputStreamReader.Java:184)
at Java.io.BufferedReader.fill(BufferedReader.Java:161)
at Java.io.BufferedReader.readLine(BufferedReader.Java:324)
- locked <0x000000076d20afb8> (a Java.io.InputStreamReader)
at Java.io.BufferedReader.readLine(BufferedReader.Java:389)
at com.intellij.rt.execution.application.AppMain$1.run(AppMain.Java:93)
at Java.lang.Thread.run(Thread.Java:745)
"Service Thread" #9 daemon prio=9 os_prio=0 tid=0x00007f47240c9800 nid=0x3794 runnable [0x0000000000000000]
Java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #8 daemon prio=9 os_prio=0 tid=0x00007f47240c6800 nid=0x3793 waiting on condition [0x0000000000000000]
Java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007f47240c4000 nid=0x3792 waiting on condition [0x0000000000000000]
Java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f47240c2800 nid=0x3791 waiting on condition [0x0000000000000000]
Java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f47240bf800 nid=0x3790 waiting on condition [0x0000000000000000]
Java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f47240be000 nid=0x378f waiting on condition [0x0000000000000000]
Java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f472408c000 nid=0x378e in Object.wait() [0x00007f46e98c5000]
Java.lang.Thread.State: WAITING (on object monitor)
at Java.lang.Object.wait(Native Method)
- waiting on <0x000000076cf88ee0> (a Java.lang.ref.ReferenceQueue$Lock)
at Java.lang.ref.ReferenceQueue.remove(ReferenceQueue.Java:143)
- locked <0x000000076cf88ee0> (a Java.lang.ref.ReferenceQueue$Lock)
at Java.lang.ref.ReferenceQueue.remove(ReferenceQueue.Java:164)
at Java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.Java:209)
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f4724087800 nid=0x378d in Object.wait() [0x00007f46e99c6000]
Java.lang.Thread.State: WAITING (on object monitor)
at Java.lang.Object.wait(Native Method)
- waiting on <0x000000076cf86b50> (a Java.lang.ref.Reference$Lock)
at Java.lang.Object.wait(Object.Java:502)
at Java.lang.ref.Reference.tryHandlePending(Reference.Java:191)
- locked <0x000000076cf86b50> (a Java.lang.ref.Reference$Lock)
at Java.lang.ref.Reference$ReferenceHandler.run(Reference.Java:153)
"VM Thread" os_prio=0 tid=0x00007f4724080000 nid=0x378c runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f472401f000 nid=0x3784 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f4724021000 nid=0x3785 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f4724022800 nid=0x3786 runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f4724024800 nid=0x3787 runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00007f4724026000 nid=0x3788 runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00007f4724028000 nid=0x3789 runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00007f4724029800 nid=0x378a runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x00007f472402b800 nid=0x378b runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00007f47240cc800 nid=0x3795 waiting on condition
JNI global references: 16
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007f46dc003f08 (object 0x000000076d0583a0, a com.anantha.algorithms.ThreadJoin$Friend),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007f46dc006008 (object 0x000000076d0583e0, a com.anantha.algorithms.ThreadJoin$Friend),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at com.anantha.algorithms.ThreadJoin$Friend.bowBack(ThreadJoin.Java:102)
- waiting to lock <0x000000076d0583a0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$Friend.bow(ThreadJoin.Java:92)
- locked <0x000000076d0583e0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$2.run(ThreadJoin.Java:141)
at Java.lang.Thread.run(Thread.Java:745)
"Thread-0":
at com.anantha.algorithms.ThreadJoin$Friend.bowBack(ThreadJoin.Java:102)
- waiting to lock <0x000000076d0583e0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$Friend.bow(ThreadJoin.Java:92)
- locked <0x000000076d0583a0> (a com.anantha.algorithms.ThreadJoin$Friend)
at com.anantha.algorithms.ThreadJoin$1.run(ThreadJoin.Java:134)
at Java.lang.Thread.run(Thread.Java:745)
Found 1 deadlock.
Heap
PSYoungGen total 74752K, used 9032K [0x000000076cf80000, 0x0000000772280000, 0x00000007c0000000)
eden space 64512K, 14% used [0x000000076cf80000,0x000000076d8520e8,0x0000000770e80000)
from space 10240K, 0% used [0x0000000771880000,0x0000000771880000,0x0000000772280000)
to space 10240K, 0% used [0x0000000770e80000,0x0000000770e80000,0x0000000771880000)
ParOldGen total 171008K, used 0K [0x00000006c6e00000, 0x00000006d1500000, 0x000000076cf80000)
object space 171008K, 0% used [0x00000006c6e00000,0x00000006c6e00000,0x00000006d1500000)
Metaspace used 3183K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 352K, capacity 388K, committed 512K, reserved 1048576K
Le problème producteurs-consommateurs ainsi que le problème des philosophes de la restauration est probablement aussi simple qu'il va l'être. Il y a aussi un pseudo-code qui l'illustre. Si ceux-ci sont trop complexes pour un débutant, ils feraient mieux de les saisir.
J'ai trouvé cela un peu difficile à comprendre lors de la lecture du problème des philosophes de la restauration, le blocage IMHO est en réalité lié à l'allocation de ressources. J'aimerais partager un exemple plus simple où 2 infirmières doivent se battre pour 3 équipements afin de mener à bien une tâche. Bien que ce soit écrit en Java. Une méthode simple lock () est créée pour simuler le blocage et ainsi s’appliquer à un autre langage de programmation . http://www.justexample.com/wp/example-of-deadlock/
Voici une impasse simple en Java. Nous avons besoin de deux ressources pour démontrer l'impasse. Dans l'exemple ci-dessous, une ressource est un verrou de classe (via la méthode sync) et l'autre est un entier 'i'.
public class DeadLock {
static int i;
static int k;
public static synchronized void m1(){
System.out.println(Thread.currentThread().getName()+" executing m1. Value of i="+i);
if(k>0){i++;}
while(i==0){
System.out.println(Thread.currentThread().getName()+" waiting in m1 for i to be > 0. Value of i="+i);
try { Thread.sleep(10000);} catch (InterruptedException e) { e.printStackTrace(); }
}
}
public static void main(String[] args) {
Thread t1 = new Thread("t1") {
public void run() {
m1();
}
};
Thread t2 = new Thread("t2") {
public void run() {
try { Thread.sleep(100);} catch (InterruptedException e) { e.printStackTrace(); }
k++;
m1();
}
};
t1.start();
t2.start();
}
}
Voici une impasse simple en C #.
void UpdateLabel(string text) {
lock(this) {
if(MyLabel.InvokeNeeded) {
IAsyncResult res = MyLable.BeginInvoke(delegate() {
MyLable.Text = text;
});
MyLabel.EndInvoke(res);
} else {
MyLable.Text = text;
}
}
}
Si, un jour, vous appelez cela depuis le fil de l'interface graphique et qu'un autre fil l'appelle également, vous risquez un blocage. L'autre thread arrive à EndInvoke, attend que le thread d'interface graphique exécute le délégué Tout en maintenant le verrou. Le thread de la GUI se bloque sur le même verrou en attendant que l'autre thread le libère - ce qui ne sera pas le cas car le thread de l'interface graphique ne sera jamais disponible pour exécuter le délégué que l'autre thread attend. (Bien sûr, le verrou ici n'est pas strictement nécessaire - ni EndInvoke, mais dans un scénario légèrement plus complexe, l'appelant pourrait acquérir un verrou pour d'autres raisons, ce qui entraînerait le même blocage.)
Choisissez le scénario simpliste dans lequel une impasse peut survenir lors de la présentation du concept à vos étudiants. Cela impliquerait un minimum de deux threads et un minimum de deux ressources (je pense). Le but étant de concevoir un scénario dans lequel le premier thread a un verrou sur la première ressource et attend que le verrou sur la deuxième ressource soit libéré, tandis que le deuxième thread maintient un verrou sur la deuxième ressource le verrou sur la ressource 1 doit être libéré.
Peu importe les ressources sous-jacentes; pour simplifier, vous pouvez simplement leur faire une paire de fichiers dans lesquels les deux threads peuvent écrire.
EDIT: Cela suppose qu'aucune communication inter-processus autre que les verrous ne sont détenus.
public class DeadLock {
public static void main(String[] args) {
Object resource1 = new Object();
Object resource2 = new Object();
SharedObject s = new SharedObject(resource1, resource2);
TestThread11 t1 = new TestThread11(s);
TestThread22 t2 = new TestThread22(s);
t1.start();
t2.start();
}
}
class SharedObject {
Object o1, o2;
SharedObject(Object o1, Object o2) {
this.o1 = o1;
this.o2 = o2;
}
void m1() {
synchronized(o1) {
System.out.println("locked on o1 from m1()");
synchronized(o2) {
System.out.println("locked on o2 from m1()");
}
}
}
void m2() {
synchronized(o2) {
System.out.println("locked on o2 from m2()");
synchronized(o1) {
System.out.println("locked on o1 from m2()");
}
}
}
}
class TestThread11 extends Thread {
SharedObject s;
TestThread11(SharedObject s) {
this.s = s;
}
public void run() {
s.m1();
}
}
class TestThread22 extends Thread {
SharedObject s;
TestThread22(SharedObject s) {
this.s = s;
}
public void run() {
s.m2();
}
}
Un blocage peut se produire dans le cas où un Girl1
veut flirter avec Guy2
, qui est attrapé par un autre Girl2
et que Girl2
veut flirter avec un Guy1
qui est attrapé par Girl1
. Depuis, les deux filles attendent de se jeter l'une sur l'autre, la condition s'appelle impasse.
class OuchTheGirls
{
public static void main(String[] args)
{
final String resource1 = "Guy1";
final String resource2 = "Guy2";
// Girl1 tries to lock resource1 then resource2
Thread Girl1 = new Thread(() ->
{
synchronized (resource1)
{
System.out.println("Thread 1: locked Guy1");
try { Thread.sleep(100);} catch (Exception e) {}
synchronized (resource2)
{
System.out.println("Thread 1: locked Guy2");
}
}
});
// Girl2 tries to lock Guy2 then Guy1
Thread Girl2 = new Thread(() ->
{
synchronized (resource2)
{
System.out.println("Thread 2: locked Guy2");
try { Thread.sleep(100);} catch (Exception e) {}
synchronized (resource1)
{
System.out.println("Thread 2: locked Guy1");
}
}
});
Girl1.start();
Girl2.start();
}
}
Laissez-moi vous expliquer plus clairement en utilisant un exemple ayant plus que 2 threads.
Supposons que vous avez n threads contenant chacun les verrous L1, L2, ..., Ln respectivement. Supposons maintenant que, à partir du thread 1, chaque thread tente d'acquérir le verrou de son thread voisin. Ainsi, le thread 1 est bloqué pour avoir tenté d’acquérir L2 (étant donné que L2 appartient au thread 2), le thread 2 est bloqué pour L3 et ainsi de suite. Le thread n est bloqué pour L1. C'est maintenant un blocage car aucun thread n'est capable de s'exécuter.
class ImportantWork{
synchronized void callAnother(){
}
synchronized void call(ImportantWork work) throws InterruptedException{
Thread.sleep(100);
work.callAnother();
}
}
class Task implements Runnable{
ImportantWork myWork, otherWork;
public void run(){
try {
myWork.call(otherWork);
} catch (InterruptedException e) {
}
}
}
class DeadlockTest{
public static void main(String args[]){
ImportantWork work1=new ImportantWork();
ImportantWork work2=new ImportantWork();
ImportantWork work3=new ImportantWork();
Task task1=new Task();
task1.myWork=work1;
task1.otherWork=work2;
Task task2=new Task();
task2.myWork=work2;
task2.otherWork=work3;
Task task3=new Task();
task3.myWork=work3;
task3.otherWork=work1;
new Thread(task1).start();
new Thread(task2).start();
new Thread(task3).start();
}
}
Dans l'exemple ci-dessus, vous pouvez voir qu'il existe trois threads contenant Runnable
s task1, task2 et task3. Avant l'instruction sleep(100)
, les threads acquièrent les verrous des trois objets de travail lorsqu'ils entrent dans la méthode call()
(en raison de la présence de synchronized
). Mais dès qu’ils tentent de callAnother()
sur l’objet de leur thread voisin, ils sont bloqués, ce qui entraîne un blocage, car les verrous de ces objets ont déjà été utilisés.
package test.concurrent;
public class DeadLockTest {
private static long sleepMillis;
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public static void main(String[] args) {
sleepMillis = Long.parseLong(args[0]);
DeadLockTest test = new DeadLockTest();
test.doTest();
}
private void doTest() {
Thread t1 = new Thread(new Runnable() {
public void run() {
lock12();
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
lock21();
}
});
t1.start();
t2.start();
}
private void lock12() {
synchronized (lock1) {
sleep();
synchronized (lock2) {
sleep();
}
}
}
private void lock21() {
synchronized (lock2) {
sleep();
synchronized (lock1) {
sleep();
}
}
}
private void sleep() {
try {
Thread.sleep(sleepMillis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
To run the deadlock test with sleep time 1 millisecond:
Java -cp . test.concurrent.DeadLockTest 1
Une façon sournoise de vous bloquer avec un seul thread consiste à essayer de verrouiller deux fois le même mutex (non récursif). Ce n'était peut-être pas le simple exemple que vous recherchiez, mais j'ai déjà rencontré de tels cas.
#include <mutex>
#include <iostream>
int main()
{
std::mutex m;
m.lock();
m.lock();
std::cout << "Expect never to get here because of a deadlock!";
}
CountDownLatch countDownLatch = new CountDownLatch(1);
ExecutorService executorService = ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(() -> {
Future<?> future = executorService.submit(() -> {
System.out.println("generated task");
});
countDownLatch.countDown();
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
countDownLatch.await();
executorService.shutdown();
package ForkBlur;
public class DeadLockTest {
public static void main(String args[]) {
final DeadLockTest t1 = new DeadLockTest();
final DeadLockTest t2 = new DeadLockTest();
Runnable r1 = new Runnable() {
@Override
public void run() {
try {
synchronized (t1) {
System.out
.println("r1 has locked t1, now going to sleep");
Thread.sleep(100);
System.out
.println("r1 has awake , now going to aquire lock for t2");
synchronized (t2) {
Thread.sleep(100);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
try {
synchronized (t2) {
System.out
.println("r2 has aquire the lock of t2 now going to sleep");
Thread.sleep(100);
System.out
.println("r2 is awake , now going to aquire the lock from t1");
synchronized (t1) {
Thread.sleep(100);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
new Thread(r1).start();
new Thread(r2).start();
}
}
public class DeadlockProg {
/**
* @Gowtham Chitimi Reddy IIT(BHU);
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
final Object ob1 = new Object();
final Object ob2 = new Object();
Thread t1 = new Thread(){
public void run(){
synchronized(ob1){
try{
Thread.sleep(100);
}
catch(InterruptedException e){
System.out.println("Error catched");
}
synchronized(ob2){
}
}
}
};
Thread t2 = new Thread(){
public void run(){
synchronized(ob2){
try{
Thread.sleep(100);
}
catch(InterruptedException e){
System.out.println("Error catched");
}
synchronized(ob1){
}
}
}
};
t1.start();
t2.start();
}
}
J'ai créé un exemple ultra simple de travail DeadLock: -
package com.thread.deadlock;
public class ThreadDeadLockClient {
public static void main(String[] args) {
ThreadDeadLockObject1 threadDeadLockA = new ThreadDeadLockObject1("threadDeadLockA");
ThreadDeadLockObject2 threadDeadLockB = new ThreadDeadLockObject2("threadDeadLockB");
new Thread(new Runnable() {
@Override
public void run() {
threadDeadLockA.methodA(threadDeadLockB);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
threadDeadLockB.methodB(threadDeadLockA);
}
}).start();
}
}
package com.thread.deadlock;
public class ThreadDeadLockObject1 {
private String name;
ThreadDeadLockObject1(String name){
this.name = name;
}
public synchronized void methodA(ThreadDeadLockObject2 threadDeadLockObject2) {
System.out.println("In MethodA "+" Current Object--> "+this.getName()+" Object passed as parameter--> "+threadDeadLockObject2.getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
threadDeadLockObject2.methodB(this);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.thread.deadlock;
public class ThreadDeadLockObject2 {
private String name;
ThreadDeadLockObject2(String name){
this.name = name;
}
public synchronized void methodB(ThreadDeadLockObject1 threadDeadLockObject1) {
System.out.println("In MethodB "+" Current Object--> "+this.getName()+" Object passed as parameter--> "+threadDeadLockObject1.getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
threadDeadLockObject1.methodA(this);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Dans l'exemple ci-dessus, 2 threads exécutent les méthodes synchronisées de deux objets différents . La méthode synchroniséeA est appelée par l'objet threadDeadLockA et la méthode synchronisée B par l'objet threadDeadLockB. Dans methodA, une référence de threadDeadLockB est passée et dans methodB, une référence de threadDeadLockA est passée. Maintenant, chaque thread essaie de verrouiller l'autre objet. Dans methodA, le thread qui détient un verrou sur threadDeadLockA tente d'obtenir un verrou sur l'objet threadDeadLockB et, de manière similaire, dans methodB, le thread qui détient un verrou sur threadDeadLockB tente d'obtenir un verrou sur threadDeadLockA. Ainsi, les deux threads vont attendre pour toujours créant un blocage.
Voici mon exemple détaillé pour impasse, après avoir passé beaucoup de temps. J'espère que ça aide :)
package deadlock;
public class DeadlockApp {
String s1 = "hello";
String s2 = "world";
Thread th1 = new Thread() {
public void run() {
System.out.println("Thread th1 has started");
synchronized (s1) { //A lock is created internally (holds access of s1), lock will be released or unlocked for s1, only when it exits the block Line #23
System.out.println("Executing first synchronized block of th1!");
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
System.out.println("Exception is caught in th1");
}
System.out.println("Waiting for the lock to be released from parrallel thread th1");
synchronized (s2) { //As another has runned parallely Line #32, lock has been created for s2
System.out.println(s1 + s2);
}
}
System.out.println("Thread th1 has executed");
}
};
Thread th2 = new Thread() {
public void run() {
System.out.println("Thread th2 has started");
synchronized (s2) { //A lock is created internally (holds access of s2), lock will be released or unlocked for s2, only when it exits the block Line #44
System.out.println("Executing first synchronized block of th2!");
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
System.out.println("Exception is caught in th2");
}
System.out.println("Waiting for the lock to be released from parrallel thread th2");
synchronized (s1) { //As another has runned parallely Line #11, lock has been created for s1
System.out.println(s1 + s2);
}
}
System.out.println("Thread th2 has executed");
}
};
public static void main(String[] args) {
DeadlockApp deadLock = new DeadlockApp();
deadLock.th1.start();
deadLock.th2.start();
//Line #51 and #52 runs parallely on executing the program, a lock is created inside synchronized method
//A lock is nothing but, something like a blocker or wall, which holds access of the variable from being used by others.
//Locked object is accessible, only when it is unlocked (i.e exiting the synchronized block)
//Lock cannot be created for primitive types (ex: int, float, double)
//Dont forget to add thread.sleep(time) because if not added, then object access will not be at same time for both threads to create Deadlock (not actual runtime with lots of threads)
//This is a simple program, so we added sleep90 to create Deadlock, it will execute successfully, if it is removed.
}
//Happy coding -- Parthasarathy S
}