View Javadoc

1   /*
2    * @(#) Monitor.java Jan 15, 2004
3    * 
4    * Copyright (c) 2003 Delft University of Technology Jaffalaan 5, 2628 BX Delft,
5    * the Netherlands All rights reserved.
6    * 
7    * This software is proprietary information of Delft University of Technology
8    * The code is published under the General Public License
9    */
10  package nl.tudelft.simulation.language.concurrent;
11  
12  import java.util.HashMap;
13  import java.util.Map;
14  
15  /***
16   * In the Java programming language there is a lock associated with every
17   * object. The language does not provide a way to perform separate lock and
18   * unlock operations; instead, they are implicitly performed by high-level
19   * constructs that always arrange to pair such operations correctly. This
20   * Monitor class, however, provides separate monitorenter and monitorexit
21   * instructions that implement the lock and unlock operations.) <br>
22   * (c) copyright 2003 <a href="http://www.simulation.tudelft.nl">Delft
23   * University of Technology </a>, the Netherlands. <br>
24   * See for project information <a
25   * href="http://www.simulation.tudelft.nl">www.simulation.tudelft.nl </a> <br>
26   * License of use: <a href="http://www.gnu.org/copyleft/gpl.html">General Public
27   * License (GPL) </a>, no warranty <br>
28   * 
29   * @author <a href="http://www.simulation.tudelft.nl/people/jacobs.html">Peter
30   *         Jacobs </a>
31   * @version 1.0 Jan 15, 2004 <br>
32   * @since 1.3
33   */
34  public final class Monitor
35  {
36  	/*** the locks held */
37  	private static Map locks = new HashMap();
38  
39  	/***
40  	 * constructs a new Monitor
41  	 */
42  	private Monitor()
43  	{
44  		super();
45  		//unreachable code
46  	}
47  
48  	/***
49  	 * locks an object for the current thread
50  	 * 
51  	 * @param object the object to lock
52  	 */
53  	public static void lock(final Object object)
54  	{
55  		Monitor.lock(object, Thread.currentThread());
56  	}
57  
58  	/***
59  	 * locks an object for the given requestor.
60  	 * 
61  	 * @param object the object to lock.
62  	 * @param requestor the requesting thread.
63  	 */
64  	public static void lock(final Object object, final Thread requestor)
65  	{
66  		synchronized (Monitor.locks)
67  		{
68  			if (!Monitor.locks.containsKey(object))
69  			{
70  				Monitor.locks.put(object, new MonitorThread(requestor, object));
71  			} else
72  			{
73  				MonitorThread thread = (MonitorThread) Monitor.locks
74  						.get(object);
75  				if (thread.getOwner().equals(requestor))
76  				{
77  					thread.increaseCounter();
78  				} else
79  				{
80  					synchronized (object)
81  					{
82  						//We wait until we gained access to the monitor
83  						Monitor.locks.put(object, new MonitorThread(requestor,
84  								object));
85  					}
86  				}
87  			}
88  		}
89  	}
90  
91  	/***
92  	 * unlocks an object locked by the current Thread
93  	 * 
94  	 * @param object the object to unlock
95  	 */
96  	public static void unlock(final Object object)
97  	{
98  		Monitor.unlock(object, Thread.currentThread());
99  	}
100 
101 	/***
102 	 * unlocks an object locked by owner.
103 	 * 
104 	 * @param object the object to unlock.
105 	 * @param owner the owning thread.
106 	 */
107 	public static void unlock(final Object object, final Thread owner)
108 	{
109 		synchronized (Monitor.locks)
110 		{
111 			MonitorThread thread = (MonitorThread) Monitor.locks.get(object);
112 			if (thread == null)
113 			{
114 				throw new IllegalMonitorStateException("object(" + object
115 						+ ") is not locked");
116 			}
117 			if (!thread.getOwner().equals(owner))
118 			{
119 				throw new IllegalMonitorStateException(owner + " cannot"
120 						+ " unlock object owned by " + 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 	 * A MonitorThread is used to lock an object
133 	 */
134 	private static class MonitorThread extends Thread
135 	{
136 		/*** the monitor to use */
137 		private Object object = null;
138 
139 		/*** the owning thread */
140 		private Thread owner = null;
141 
142 		/*** the counter */
143 		private int counter = 0;
144 
145 		/***
146 		 * constructs a new MonitorThread
147 		 * 
148 		 * @param owner the owning thread
149 		 * @param object the object
150 		 */
151 		public MonitorThread(final Thread owner, final Object object)
152 		{
153 			super("MonitorThread on " + object.getClass());
154 			this.setDaemon(true);
155 			this.owner = owner;
156 			synchronized (object)
157 			{
158 				this.object = object;
159 				this.counter++;
160 				this.start();
161 			}
162 			synchronized (owner)
163 			{
164 				try
165 				{
166 					this.owner.wait();
167 				} catch (InterruptedException exception)
168 				{
169 					exception = null;
170 					//This interrupted exception is thrown because this monitor
171 					// thread
172 					//has started and interrupted its constructor. We now know
173 					//object is locked and may therefore return.
174 				}
175 			}
176 		}
177 
178 		/***
179 		 * @return Returns the counter.
180 		 */
181 		public synchronized int getCounter()
182 		{
183 			return this.counter;
184 		}
185 
186 		/***
187 		 * decreases the counter with one
188 		 */
189 		public synchronized void decreaseCounter()
190 		{
191 			this.counter = Math.max(0, this.counter - 1);
192 		}
193 
194 		/***
195 		 * increases the counter of this thread with one
196 		 */
197 		public synchronized void increaseCounter()
198 		{
199 			this.counter++;
200 		}
201 
202 		/***
203 		 * @return Returns the owning thread.
204 		 */
205 		public Thread getOwner()
206 		{
207 			return this.owner;
208 		}
209 
210 		/***
211 		 * @see java.lang.Runnable#run()
212 		 */
213 		public void run()
214 		{
215 			try
216 			{
217 				//We lock the object
218 				synchronized (this.object)
219 				{
220 					//Since we have locked the object, we can now return
221 					//the constructor
222 					this.owner.interrupt();
223 
224 					//We join
225 					this.join();
226 				}
227 			} catch (Exception exception)
228 			{
229 				// This is OK.. We use this construction in the
230 				// MonitorTest.unlock to release a lock
231 				exception = null;
232 			}
233 		}
234 	}
235 }