活锁的好例子?
我知道什么是活锁,但我想知道是否有人有一个好的基于代码的例子呢? 而通过基于代码的方式,我不是指“两个人在走廊上试图相互穿越”。 如果我再读一遍,我会失去午餐。
这里有一个非常简单的Java活锁例子,夫妻俩正在尝试吃汤,但他们之间只有一个勺子。 每个配偶都太客气了,如果对方还没有吃,就会通过勺子。
public class Livelock { static class Spoon { private Diner owner; public Spoon(Diner d) { owner = d; } public Diner getOwner() { return owner; } public synchronized void setOwner(Diner d) { owner = d; } public synchronized void use() { System.out.printf("%s has eaten!", owner.name); } } static class Diner { private String name; private boolean isHungry; public Diner(String n) { name = n; isHungry = true; } public String getName() { return name; } public boolean isHungry() { return isHungry; } public void eatWith(Spoon spoon, Diner spouse) { while (isHungry) { // Don't have the spoon, so wait patiently for spouse. if (spoon.owner != this) { try { Thread.sleep(1); } catch(InterruptedException e) { continue; } continue; } // If spouse is hungry, insist upon passing the spoon. if (spouse.isHungry()) { System.out.printf( "%s: You eat first my darling %s!%n", name, spouse.getName()); spoon.setOwner(spouse); continue; } // Spouse wasn't hungry, so finally eat spoon.use(); isHungry = false; System.out.printf( "%s: I am stuffed, my darling %s!%n", name, spouse.getName()); spoon.setOwner(spouse); } } } public static void main(String[] args) { final Diner husband = new Diner("Bob"); final Diner wife = new Diner("Alice"); final Spoon s = new Spoon(husband); new Thread(new Runnable() { public void run() { husband.eatWith(s, wife); } }).start(); new Thread(new Runnable() { public void run() { wife.eatWith(s, husband); } }).start(); } }
对于浮夸的评论,一个已知的例子是在代码中试图检测和处理死锁情况。 如果两个线程检测到一个死锁,并且试图互相“搁置”,不pipe它们将会被卡在一个总是“放开”的循环中,而永远不会设法向前移动。
通过“放弃”我的意思是说,他们将释放锁,并试图让另一个获得它。 我们可以想象两个线程这样做的情况(伪代码):
// thread 1 getLocks12(lock1, lock2) { lock1.lock(); while (lock2.locked()) { // attempt to step aside for the other thread lock1.unlock(); wait(); lock1.lock(); } lock2.lock(); } // thread 2 getLocks21(lock1, lock2) { lock2.lock(); while (lock1.locked()) { // attempt to step aside for the other thread lock2.unlock(); wait(); lock2.lock(); } lock1.lock(); }
抛开竞争条件,我们在这里遇到的情况是,如果两个线程同时进入,将最终在内部循环中运行而不继续运行。 显然这是一个简单的例子。 一个令人生厌的解决方法是在线程等待的时间内放置某种随机性。
正确的解决办法是始终尊重locking的层面 。 select一个您获取locking的订单并坚持。 例如,如果两个线程始终在lock2之前获取lock1,则不存在死锁的可能性。
由于没有答案被标记为接受答案,我试图创build活锁示例;
原来的程序是我在2012年4月写的,学习了multithreading的各种概念。 这一次,我修改它,造成僵局,竞争条件,活锁等。
所以我们先来了解一下这个问题。
Cookie制造商问题
有一些配料容器: ChocoPowederContainer , WheatPowderContainer 。 CookieMaker从配料容器中取出一定量的粉末来烘烤cookies 。 如果cookie制造商发现容器是空的,它会检查另一个容器以节省时间。 并等待Filler填充所需的容器。 有一个填充者定期检查容器,并在容器需要时填充一些数量。
请检查github上的完整代码;
让我简单介绍一下你的实现。
- 我将Filler作为守护线程启动。 所以它会定期保持灌装容器。 首先填充容器,然后locking容器 – >检查是否需要粉末 – >填充 – >指示正在等待的所有制造商 – >解锁容器。
- 我创buildCookieMaker,并设置它可以并行烘烤8cookies。 我开始8线程烤cookies。
- 每个制造商线程创build2个可调用的子线程从容器中取粉。
- 子线程在容器上locking并检查是否有足够的粉末。 如果没有,请等待一段时间。 一旦填料填满容器,它将粉末,并解锁容器。
- 现在它完成了其他的活动,如:混合和烘烤等。
让我们来看看代码:
CookieMaker.java
private Integer getMaterial(final Ingredient ingredient) throws Exception{ : container.lock(); while (!container.getIngredient(quantity)) { container.empty.await(1000, TimeUnit.MILLISECONDS); //Thread.sleep(500); //For deadlock } container.unlock(); : }
IngredientContainer.java
public boolean getIngredient(int n) throws Exception { : lock(); if (quantityHeld >= n) { TimeUnit.SECONDS.sleep(2); quantityHeld -= n; unlock(); return true; } unlock(); return false; }
一切都运行良好,直到填料填充容器。 但是,如果我忘记启动填充程序,或者填充程序意外离开,则子线程会不断更改其状态以允许其他制造商去检查容器。
我也创build了一个守护线程状态和死锁的守护进程ThreadTracer 。 这从控制台输出;
2016-09-12 21:31:45.065 :: [Maker_0:WAITING, Maker_1:WAITING, Maker_2:WAITING, Maker_3:WAITING, Maker_4:WAITING, Maker_5:WAITING, Maker_6:WAITING, Maker_7:WAITING, pool-7-thread-1:TIMED_WAITING, pool-7-thread-2:TIMED_WAITING, pool-8-thread-1:TIMED_WAITING, pool-8-thread-2:TIMED_WAITING, pool-6-thread-1:TIMED_WAITING, pool-6-thread-2:TIMED_WAITING, pool-5-thread-1:TIMED_WAITING, pool-5-thread-2:TIMED_WAITING, pool-1-thread-1:TIMED_WAITING, pool-3-thread-1:TIMED_WAITING, pool-2-thread-1:TIMED_WAITING, pool-1-thread-2:TIMED_WAITING, pool-4-thread-1:TIMED_WAITING, pool-4-thread-2:RUNNABLE, pool-3-thread-2:TIMED_WAITING, pool-2-thread-2:TIMED_WAITING] 2016-09-12 21:31:45.065 :: [Maker_0:WAITING, Maker_1:WAITING, Maker_2:WAITING, Maker_3:WAITING, Maker_4:WAITING, Maker_5:WAITING, Maker_6:WAITING, Maker_7:WAITING, pool-7-thread-1:TIMED_WAITING, pool-7-thread-2:TIMED_WAITING, pool-8-thread-1:TIMED_WAITING, pool-8-thread-2:TIMED_WAITING, pool-6-thread-1:TIMED_WAITING, pool-6-thread-2:TIMED_WAITING, pool-5-thread-1:TIMED_WAITING, pool-5-thread-2:TIMED_WAITING, pool-1-thread-1:TIMED_WAITING, pool-3-thread-1:TIMED_WAITING, pool-2-thread-1:TIMED_WAITING, pool-1-thread-2:TIMED_WAITING, pool-4-thread-1:TIMED_WAITING, pool-4-thread-2:TIMED_WAITING, pool-3-thread-2:TIMED_WAITING, pool-2-thread-2:TIMED_WAITING] WheatPowder Container has 0 only. 2016-09-12 21:31:45.082 :: [Maker_0:WAITING, Maker_1:WAITING, Maker_2:WAITING, Maker_3:WAITING, Maker_4:WAITING, Maker_5:WAITING, Maker_6:WAITING, Maker_7:WAITING, pool-7-thread-1:TIMED_WAITING, pool-7-thread-2:TIMED_WAITING, pool-8-thread-1:TIMED_WAITING, pool-8-thread-2:TIMED_WAITING, pool-6-thread-1:TIMED_WAITING, pool-6-thread-2:TIMED_WAITING, pool-5-thread-1:TIMED_WAITING, pool-5-thread-2:TIMED_WAITING, pool-1-thread-1:TIMED_WAITING, pool-3-thread-1:TIMED_WAITING, pool-2-thread-1:TIMED_WAITING, pool-1-thread-2:TIMED_WAITING, pool-4-thread-1:TIMED_WAITING, pool-4-thread-2:TIMED_WAITING, pool-3-thread-2:TIMED_WAITING, pool-2-thread-2:RUNNABLE] 2016-09-12 21:31:45.082 :: [Maker_0:WAITING, Maker_1:WAITING, Maker_2:WAITING, Maker_3:WAITING, Maker_4:WAITING, Maker_5:WAITING, Maker_6:WAITING, Maker_7:WAITING, pool-7-thread-1:TIMED_WAITING, pool-7-thread-2:TIMED_WAITING, pool-8-thread-1:TIMED_WAITING, pool-8-thread-2:TIMED_WAITING, pool-6-thread-1:TIMED_WAITING, pool-6-thread-2:TIMED_WAITING, pool-5-thread-1:TIMED_WAITING, pool-5-thread-2:TIMED_WAITING, pool-1-thread-1:TIMED_WAITING, pool-3-thread-1:TIMED_WAITING, pool-2-thread-1:TIMED_WAITING, pool-1-thread-2:TIMED_WAITING, pool-4-thread-1:TIMED_WAITING, pool-4-thread-2:TIMED_WAITING, pool-3-thread-2:TIMED_WAITING, pool-2-thread-2:TIMED_WAITING]
你会注意到子线程和改变它们的状态并等待。
一个真正的(尽pipe没有确切的代码)的例子是两个竞争的进程实时locking,试图纠正SQL服务器死锁,每个进程使用相同的等待重试algorithm进行重试。 虽然这是计时的好运,但是我已经看到这种情况发生在具有类似性能特征的单独机器上,以响应添加到EMS主题的消息(例如,多次保存单个对象图的更新),并且不能够控制locking顺序。
在这种情况下,一个好的解决scheme就是让竞争的消费者(通过在不相关的对象上划分工作,尽可能避免重复的处理)。
不太理想的(好的,肮脏的)解决scheme是提前打破计时不好的运算(处理中的一种力差异),或者使用不同的algorithm或一些随机因素在死锁之后打破它。 这仍然可能有问题,因为它的可能性对于每个进程来说,locking顺序是“粘性”的,并且这在等待重试中花费了一定的最小时间。
另一个解决scheme(至less对于SQL Server)是尝试一个不同的隔离级别(例如快照)。
Jelbourn的代码的C#版本:
using System; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; namespace LiveLockExample { static class Program { public static void Main(string[] args) { var husband = new Diner("Bob"); var wife = new Diner("Alice"); var s = new Spoon(husband); Task.WaitAll( Task.Run(() => husband.EatWith(s, wife)), Task.Run(() => wife.EatWith(s, husband)) ); } public class Spoon { public Spoon(Diner diner) { Owner = diner; } public Diner Owner { get; private set; } [MethodImpl(MethodImplOptions.Synchronized)] public void SetOwner(Diner d) { Owner = d; } [MethodImpl(MethodImplOptions.Synchronized)] public void Use() { Console.WriteLine("{0} has eaten!", Owner.Name); } } public class Diner { public Diner(string n) { Name = n; IsHungry = true; } public string Name { get; private set; } private bool IsHungry { get; set; } public void EatWith(Spoon spoon, Diner spouse) { while (IsHungry) { // Don't have the spoon, so wait patiently for spouse. if (spoon.Owner != this) { try { Thread.Sleep(1); } catch (ThreadInterruptedException e) { } continue; } // If spouse is hungry, insist upon passing the spoon. if (spouse.IsHungry) { Console.WriteLine("{0}: You eat first my darling {1}!", Name, spouse.Name); spoon.SetOwner(spouse); continue; } // Spouse wasn't hungry, so finally eat spoon.Use(); IsHungry = false; Console.WriteLine("{0}: I am stuffed, my darling {1}!", Name, spouse.Name); spoon.SetOwner(spouse); } } } } }
这里的一个例子可能是使用定时tryLock来获得多个锁,如果你不能获得所有的锁,请退出并重试。
boolean tryLockAll(Collection<Lock> locks) { boolean grabbedAllLocks = false; for(int i=0; i<locks.size(); i++) { Lock lock = locks.get(i); if(!lock.tryLock(5, TimeUnit.SECONDS)) { grabbedAllLocks = false; // undo the locks I already took in reverse order for(int j=i-1; j >= 0; j--) { lock.unlock(); } } } }
我可以想象这样的代码会有问题,因为你有很多的线程碰撞,并等待获得一组锁。 但是我不确定这个例子对我来说是非常有吸引力的。
我编了一个走廊里的两个人的例子。 这两个线程一旦意识到他们的方向是相同的,就会避开对方。
public class LiveLock { public static void main(String[] args) throws InterruptedException { Object left = new Object(); Object right = new Object(); Pedestrian one = new Pedestrian(left, right, 0); //one's left is one's left Pedestrian two = new Pedestrian(right, left, 1); //one's left is two's right, so have to swap order one.setOther(two); two.setOther(one); one.start(); two.start(); } } class Pedestrian extends Thread { private Object l; private Object r; private Pedestrian other; private Object current; Pedestrian (Object left, Object right, int firstDirection) { l = left; r = right; if (firstDirection==0) { current = l; } else { current = r; } } void setOther(Pedestrian otherP) { other = otherP; } Object getDirection() { return current; } Object getOppositeDirection() { if (current.equals(l)) { return r; } else { return l; } } void switchDirection() throws InterruptedException { Thread.sleep(100); current = getOppositeDirection(); System.out.println(Thread.currentThread().getName() + " is stepping aside."); } public void run() { while (getDirection().equals(other.getDirection())) { try { switchDirection(); Thread.sleep(100); } catch (InterruptedException e) {} } } }
jelbourn的代码的Python版本:
import threading import time lock = threading.Lock() class Spoon: def __init__(self, diner): self.owner = diner def setOwner(self, diner): with lock: self.owner = diner def use(self): with lock: "{0} has eaten".format(self.owner) class Diner: def __init__(self, name): self.name = name self.hungry = True def eatsWith(self, spoon, spouse): while(self.hungry): if self != spoon.owner: time.sleep(1) # blocks thread, not process continue if spouse.hungry: print "{0}: you eat first, {1}".format(self.name, spouse.name) spoon.setOwner(spouse) continue # Spouse was not hungry, eat spoon.use() print "{0}: I'm stuffed, {1}".format(self.name, spouse.name) spoon.setOwner(spouse) def main(): husband = Diner("Bob") wife = Diner("Alice") spoon = Spoon(husband) t0 = threading.Thread(target=husband.eatsWith, args=(spoon, wife)) t1 = threading.Thread(target=wife.eatsWith, args=(spoon, husband)) t0.start() t1.start() t0.join() t1.join() if __name__ == "__main__": main()
我修改@jelbourn的答案。 当他们中的一个注意到对方饿了时,他(她)应该放开勺子,等待另一个通知,这样就会发生活塞。
public class LiveLock { static class Spoon { Diner owner; public String getOwnerName() { return owner.getName(); } public void setOwner(Diner diner) { this.owner = diner; } public Spoon(Diner diner) { this.owner = diner; } public void use() { System.out.println(owner.getName() + " use this spoon and finish eat."); } } static class Diner { public Diner(boolean isHungry, String name) { this.isHungry = isHungry; this.name = name; } private boolean isHungry; private String name; public String getName() { return name; } public void eatWith(Diner spouse, Spoon sharedSpoon) { try { synchronized (sharedSpoon) { while (isHungry) { while (!sharedSpoon.getOwnerName().equals(name)) { sharedSpoon.wait(); //System.out.println("sharedSpoon belongs to" + sharedSpoon.getOwnerName()) } if (spouse.isHungry) { System.out.println(spouse.getName() + "is hungry,I should give it to him(her)."); sharedSpoon.setOwner(spouse); sharedSpoon.notifyAll(); } else { sharedSpoon.use(); sharedSpoon.setOwner(spouse); isHungry = false; } Thread.sleep(500); } } } catch (InterruptedException e) { System.out.println(name + " is interrupted."); } } } public static void main(String[] args) { final Diner husband = new Diner(true, "husband"); final Diner wife = new Diner(true, "wife"); final Spoon sharedSpoon = new Spoon(wife); Thread h = new Thread() { @Override public void run() { husband.eatWith(wife, sharedSpoon); } }; h.start(); Thread w = new Thread() { @Override public void run() { wife.eatWith(husband, sharedSpoon); } }; w.start(); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } h.interrupt(); w.interrupt(); try { h.join(); w.join(); } catch (InterruptedException e) { e.printStackTrace(); } } }
package concurrently.deadlock; import static java.lang.System.out; /* This is an example of livelock */ public class Dinner { public static void main(String[] args) { Spoon spoon = new Spoon(); Dish dish = new Dish(); new Thread(new Husband(spoon, dish)).start(); new Thread(new Wife(spoon, dish)).start(); } } class Spoon { boolean isLocked; } class Dish { boolean isLocked; } class Husband implements Runnable { Spoon spoon; Dish dish; Husband(Spoon spoon, Dish dish) { this.spoon = spoon; this.dish = dish; } @Override public void run() { while (true) { synchronized (spoon) { spoon.isLocked = true; out.println("husband get spoon"); try { Thread.sleep(2000); } catch (InterruptedException e) {} if (dish.isLocked == true) { spoon.isLocked = false; // give away spoon out.println("husband pass away spoon"); continue; } synchronized (dish) { dish.isLocked = true; out.println("Husband is eating!"); } dish.isLocked = false; } spoon.isLocked = false; } } } class Wife implements Runnable { Spoon spoon; Dish dish; Wife(Spoon spoon, Dish dish) { this.spoon = spoon; this.dish = dish; } @Override public void run() { while (true) { synchronized (dish) { dish.isLocked = true; out.println("wife get dish"); try { Thread.sleep(2000); } catch (InterruptedException e) {} if (spoon.isLocked == true) { dish.isLocked = false; // give away dish out.println("wife pass away dish"); continue; } synchronized (spoon) { spoon.isLocked = true; out.println("Wife is eating!"); } spoon.isLocked = false; } dish.isLocked = false; } } }