浏览 2703 次
精华帖 (0) :: 良好帖 (0) :: 灌水帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2005-10-24
Threading has sucked down a lot of my life in the past few years. Aside from work on earlier versions of Thinking in Java, I clocked 8 months on the threading chapter in "Thinking in C++, Volume 2." I've already spent several months on the "Concurrency" Chapter in "Thinking in Java 4th edition." And now I'm back working on it again. Because of all the changes in concurrency, the chapter has gotten big. At one point I thought of making it into a separate book. Even with the size that it is, I'm not able to cover all the new library components in java.util.concurrent. I'm actually having to remove examples. Here's one that I just couldn't make into something appropriate; it was an attempt to use the Exchanger class, which I've decided is just a bit too obscure to include in the book. However, I think I might get some mileage out of it by posting it here. First, to show you just how far threading has driven me, this example has degenerated into a simulation of a science fiction planet containing mythical creatures with completely questionable reasons for existence. I don't think they could have evolved this way, so perhaps it's an example of "unintelligent design." More importantly, I'm not sure that I have or haven't synchronized properly or made necessary fields volatile (see the previous posting). At the time I wrote this I had been immersed in threading for long enough that I might have gotten it right -- after all, many of the new components in java.util.concurrent are designed to provide thread safety by themselves. But I figure the eyeballs and feedback will help me ensure that all the rest of the examples in the chapter are correct. I'm hoping that you don't just say "make everything volatile" or "synchronize all methods," but give me the argument for why a particular field or method should be modified, so I can make a clear case for it in the other examples in the book. Here's the backstory for the example: Red Nibbins and Blue Nibbins try to fool each other into giving up their rample berries, which help them think. Basically, they’re confidence persons, albeit not very smart ones. The rample berries are carried in a special case, and the Nibbins trade cases, unopened, in the hope that the other case will have more berries than the one they have. When a Nibbin is asleep, a Berry Fairy may visit and add more Rample Berries to their case. As a Nibbin's side gains more rample berries than the other, they grow smarter and therefore have more cunning in acquiring more berries. Theoretically, one side should eventually win out over the other. Note that this example is close to the end of the chapter, so issues such as task termination have already been thoroughly explained. //: concurrency/Thwonk.java import java.util.concurrent.*; import java.util.concurrent.locks.*; import java.util.*; import static net.mindview.util.Print.*; class RampleBerryCase implements Runnable { private static Random rand = new Random(47);; private int berries = 3; public void run(); { try { while(!Thread.interrupted();); { Thread.sleep(200 + rand.nextInt(1000););; if(rand.nextInt(10); == 0); // 1 in 10 chance berries += 2; // Berry Fairy visits! } } catch(InterruptedException e); { // OK to terminate this way. } } public int numBerries(); { return berries; } } class Nibbin implements Runnable { public enum Color { RED, BLUE }; private Color color; static long counter = 0; long id = counter++; private static Random rand = new Random(47);; private RampleBerryCase rampleBerryCase = new RampleBerryCase();; private BerryBrokers berryBrokers ; // Construction must be done in steps to prevent // null pointers: public Nibbin(Color c, BerryBrokers brokers, Executor e);{ color = c; berryBrokers = brokers; e.execute(rampleBerryCase);; e.execute(this);; } Color getColor(); { return color; } public void run(); { try { while(!Thread.interrupted();); { int old = rampleBerryCase.numBerries();; rampleBerryCase = berryBrokers.exchange(this);; if(rampleBerryCase.numBerries(); > old); print(this + "Thwonk!");; // (Hooray!); else print(this + "Blatz!");; // (Darn!); } } catch(InterruptedException e); { // OK to terminate this way. } } public RampleBerryCase getCase(); { return rampleBerryCase; } public String toString(); { return String.format( "%1$-4s Nibbin %2$-3d berries: %3$-3d ", color, id, rampleBerryCase.numBerries(););; } } class BerryBrokers { private static class BerryBroker { private Exchanger<RampleBerryCase> exchanger = new Exchanger<RampleBerryCase>();; private ReentrantLock blueLock = new ReentrantLock();, redLock = new ReentrantLock();; public RampleBerryCase offerBlue(RampleBerryCase old); throws InterruptedException { return exchanger.exchange(old);; } public RampleBerryCase offerRed(RampleBerryCase old); throws InterruptedException { return exchanger.exchange(old);; } } private ArrayList<BerryBroker> list = new ArrayList<BerryBroker>();; public BerryBrokers(int size); { for(int i = 0; i < size; i++); list.add(new BerryBroker(););; } public RampleBerryCase exchange(Nibbin nibbin); throws InterruptedException { switch(nibbin.getColor();); { case RED: for(BerryBroker bb : list); { if(bb.redLock.tryLock();); try { return bb.offerRed(nibbin.getCase(););; } finally { bb.redLock.unlock();; } } // No red locks available. Return the old case: return nibbin.getCase();; case BLUE: for(BerryBroker bb : list); { if(bb.blueLock.tryLock();); try { return bb.offerBlue(nibbin.getCase(););; } finally { bb.blueLock.unlock();; } } // No blue locks available. Return the old case: return nibbin.getCase();; default: throw new RuntimeException("Not red or blue");; } } } public class Thwonk { static int size = 100; public static void main(String[] args); throws Exception { if(args.length > 0); { // Optional argument int n = new Integer(args[0]);; size = n > 0 ? n : size; } BerryBrokers brokers = new BerryBrokers(size/2);; ExecutorService e = Executors.newCachedThreadPool();; for(int i = 0; i < size; i++); { new Nibbin(Nibbin.Color.RED, brokers, e);; new Nibbin(Nibbin.Color.BLUE, brokers, e);; } Thread.sleep(5000);; e.shutdownNow();; } } /* Output: (Sample); RED Nibbin 0 berries: 3 Blatz! RED Nibbin 0 berries: 3 Blatz! BLUE Nibbin 1 berries: 3 Blatz! RED Nibbin 2 berries: 3 Blatz! BLUE Nibbin 1 berries: 3 Blatz! BLUE Nibbin 1 berries: 3 Blatz! RED Nibbin 4 berries: 3 Blatz! ... *///:~ Here's just one issue that I've recently become aware of: in the Nibbin constructor, two tasks are started using the executor e that's passed to the constructor. I've recently learned that creating and starting a thread inside a constructor is problematic, because the constructor doesn't normally release the object lock until construction is complete (thus guaranteeing object consistency when threads are present -- the object is fully constructed before any threads can modify it), but if you start a thread within a constructor, the object lock is then released and the resulting thread can then modify the object before construction is finished. But would the same be true in this case, where a task is handed to an executor? It would be lovely if the executor guards you against such an issue; I already prefer executors to raw threads but this would make the argument for executors far more compelling. I had made a note to myself to see if this example might be converted into the "beer game" (a business-school simulation of product distribution; see google), but I'm not sure if that's an easy conversion; it's probably a green-fields design instead. As interesting as I think it would be, time constrains will prevent the beer game from making it into the book. 这是原文地址:http://www.artima.com/forums/flat.jsp?forum=106&thread=127230 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |