View Javadoc

1   /*
2    * @(#) Monitor.java Jan 15, 2004 Copyright (c) 2002-2005 Delft University of Technology Jaffalaan 5, 2628 BX
3    * Delft, the Netherlands. All rights reserved. This software is proprietary information of Delft University
4    * of Technology The code is published under the Lesser General Public License
5    */
6   package nl.tudelft.simulation.language.concurrent;
7   
8   import java.util.ArrayList;
9   import java.util.List;
10  
11  /***
12   * In the Java programming language there is a lock associated with every object. The language does not
13   * provide a way to perform separate lock and unlock operations; instead, they are implicitly performed by
14   * high-level constructs that always arrange to pair such operations correctly. This Monitor class, however,
15   * provides separate monitorenter and monitorexit instructions that implement the lock and unlock operations.)
16   * <p>
17   * (c) copyright 2002-2005 <a href="http://www.simulation.tudelft.nl">Delft University of Technology </a>, the
18   * Netherlands.
19   * <p>
20   * See for project information <a
21   * href="http://www.simulation.tudelft.nl/dsol/language">www.simulation.tudelft.nl/language </a> <br>
22   * License of use: <a href="http://www.gnu.org/copyleft/lesser.html">Lesser General Public License (LGPL)
23   * </a>, no warranty
24   * 
25   * @author <a href="http://www.peter-jacobs.com">Peter Jacobs </a>
26   * @version $Revision: 1.9 $ $Date: 2005/08/04 12:08:56 $
27   * @since 1.5
28   */
29  public final class Monitor
30  {
31      /*** the locks held. */
32      private static List<Monitor.Entry> locks = new ArrayList<Monitor.Entry>();
33  
34      /***
35       * constructs a new Monitor.
36       */
37      private Monitor()
38      {
39          super();
40          // unreachable code
41      }
42  
43      /***
44       * locks an object for the current thread.
45       * 
46       * @param object
47       *            the object to lock
48       */
49      public static void lock(final Object object)
50      {
51          Monitor.lock(object, Thread.currentThread());
52      }
53  
54      /***
55       * locks an object for the given requestor.
56       * 
57       * @param object
58       *            the object to lock.
59       * @param requestor
60       *            the requesting thread.
61       */
62      public static void lock(final Object object, final Thread requestor)
63      {
64          synchronized (Monitor.locks)
65          {
66              if (Monitor.get(object) == null)
67              {
68                  Monitor.locks.add(new Entry(object, new MonitorThread(requestor, object)));
69              }
70              else
71              {
72                  MonitorThread thread = Monitor.get(object);
73                  if (thread.getOwner().equals(requestor))
74                  {
75                      thread.increaseCounter();
76                  }
77                  else
78                  {
79                      synchronized (object)
80                      {
81                          // We wait until we gained access to the monitor
82                          Monitor.locks.add(new Entry(object, new MonitorThread(requestor, object)));
83                      }
84                  }
85              }
86          }
87      }
88  
89      /***
90       * unlocks an object locked by the current Thread.
91       * 
92       * @param object
93       *            the object to unlock
94       */
95      public static void unlock(final Object object)
96      {
97          Monitor.unlock(object, Thread.currentThread());
98      }
99  
100     /***
101      * unlocks an object locked by owner.
102      * 
103      * @param object
104      *            the object to unlock.
105      * @param owner
106      *            the owning thread.
107      */
108     public static void unlock(final Object object, final Thread owner)
109     {
110         synchronized (Monitor.locks)
111         {
112             MonitorThread thread = Monitor.get(object);
113             if (thread == null)
114             {
115                 throw new IllegalMonitorStateException("object(" + object + ") is not locked");
116             }
117             if (!thread.getOwner().equals(owner))
118             {
119                 throw new IllegalMonitorStateException(owner + " cannot" + " unlock object owned by "
120                         + thread.getOwner());
121             }
122             thread.decreaseCounter();
123             if (thread.getCounter() == 0)
124             {
125                 thread.interrupt();
126                 Monitor.locks.remove(object);
127             }
128         }
129     }
130 
131     /***
132      * returns the MonitorThread for a specific key.
133      * 
134      * @param key
135      *            the key to resolve
136      * @return the MonitorThread
137      */
138     private static MonitorThread get(final Object key)
139     {
140         for (Entry next : Monitor.locks)
141         {
142             if (next.getKey().equals(key))
143             {
144                 return next.getThread();
145             }
146         }
147         return null;
148     }
149 
150     /***
151      * The Entry specifies entries in the set.
152      */
153     private static final class Entry
154     {
155         /*** the key to use. */
156         private Object key = null;
157 
158         /*** the monitorThread. */
159         private MonitorThread thread = null;
160 
161         /***
162          * constructs a new Entry.
163          * 
164          * @param key
165          *            the key that locked the thread
166          * @param thread
167          *            the thread to be locked
168          */
169         public Entry(final Object key, final MonitorThread thread)
170         {
171             super();
172             this.key = key;
173             this.thread = thread;
174         }
175 
176         /***
177          * @return the key that is locked by a thread
178          */
179         public Object getKey()
180         {
181             return this.key;
182         }
183 
184         /***
185          * @return the thread that locked the key
186          */
187         public MonitorThread getThread()
188         {
189             return this.thread;
190         }
191     }
192 
193     /***
194      * A MonitorThread is used to lock an object.
195      */
196     private static class MonitorThread extends Thread
197     {
198         /*** the monitor to use. */
199         private Object object = null;
200 
201         /*** the owning thread. */
202         private Thread owner = null;
203 
204         /*** the counter. */
205         private int counter = 0;
206 
207         /***
208          * constructs a new MonitorThread.
209          * 
210          * @param owner
211          *            the owning thread
212          * @param object
213          *            the object
214          */
215         public MonitorThread(final Thread owner, final Object object)
216         {
217             super("MonitorThread on " + object.getClass());
218             this.setDaemon(true);
219             this.owner = owner;
220             synchronized (object)
221             {
222                 this.object = object;
223                 this.counter++;
224                 this.start();
225             }
226             synchronized (owner)
227             {
228                 try
229                 {
230                     this.owner.wait();
231                 }
232                 catch (InterruptedException exception)
233                 {
234                     exception = null;
235                     // This interrupted exception is thrown because this monitor
236                     // thread
237                     // has started and interrupted its constructor. We now know
238                     // object is locked and may therefore return.
239                 }
240             }
241         }
242 
243         /***
244          * @return Returns the counter.
245          */
246         public synchronized int getCounter()
247         {
248             return this.counter;
249         }
250 
251         /***
252          * decreases the counter with one.
253          */
254         public synchronized void decreaseCounter()
255         {
256             this.counter = Math.max(0, this.counter - 1);
257         }
258 
259         /***
260          * increases the counter of this thread with one.
261          */
262         public synchronized void increaseCounter()
263         {
264             this.counter++;
265         }
266 
267         /***
268          * @return Returns the owning thread.
269          */
270         public Thread getOwner()
271         {
272             return this.owner;
273         }
274 
275         /***
276          * @see java.lang.Runnable#run()
277          */
278         @Override
279         public void run()
280         {
281             try
282             {
283                 // We lock the object
284                 synchronized (this.object)
285                 {
286                     // Since we have locked the object, we can now return
287                     // the constructor
288                     this.owner.interrupt();
289 
290                     // We join
291                     this.join();
292                 }
293             }
294             catch (Exception exception)
295             {
296                 // This is OK.. We use this construction in the
297                 // MonitorTest.unlock to release a lock
298                 exception = null;
299             }
300         }
301     }
302 }