Skip to content

Commit 4456a44

Browse files
committed
Monitor Object pattern iluwatar#466
1 parent c48a1e9 commit 4456a44

19 files changed

+654
-0
lines changed

monitor-object/pom.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0"?>
2+
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>com.iluwatar</groupId>
7+
<artifactId>java-design-patterns</artifactId>
8+
<version>1.20.0-SNAPSHOT</version>
9+
</parent>
10+
<artifactId>monitor-object</artifactId>
11+
<name>monitor-object</name>
12+
<dependencies>
13+
<dependency>
14+
<groupId>org.junit.jupiter</groupId>
15+
<artifactId>junit-jupiter-api</artifactId>
16+
<scope>test</scope>
17+
</dependency>
18+
<dependency>
19+
<groupId>org.junit.jupiter</groupId>
20+
<artifactId>junit-jupiter-engine</artifactId>
21+
<scope>test</scope>
22+
</dependency>
23+
</dependencies>
24+
</project>
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
package com.iluwatar.monitor;
2+
3+
import java.util.ArrayList;
4+
5+
/**
6+
* A class for Monitors. Monitors provide coordination of concurrent threads.
7+
* Each Monitor protects some resource, usually data. At each point in time a
8+
* monitor object is occupied by at most one thread.
9+
*/
10+
public abstract class AbstractMonitor {
11+
final Semaphore entrance = new Semaphore(1);
12+
volatile Thread occupant = null;
13+
private final ArrayList<MonitorListener> listOfListeners = new ArrayList<>();
14+
private final String name;
15+
16+
public String getName() {
17+
return name;
18+
}
19+
20+
protected AbstractMonitor() {
21+
this(null);
22+
}
23+
24+
protected AbstractMonitor(String name) {
25+
this.name = name;
26+
}
27+
28+
/**
29+
* The invariant. The default implementation always returns true. This method
30+
* should be overridden if at all possible with the strongest economically
31+
* evaluable invariant.
32+
*/
33+
protected boolean invariant() {
34+
return true;
35+
}
36+
37+
/**
38+
* Enter the monitor. Any thread calling this method is delayed until the
39+
* monitor is unoccupied. Upon returning from this method, the monitor is
40+
* considered occupied. A thread must not attempt to enter a Monitor it is
41+
* already in.
42+
*/
43+
protected void enter() {
44+
notifyCallEnter();
45+
entrance.acquire();
46+
// The following assertion should never trip!
47+
Assertion.check(occupant == null, "2 threads in one monitor");
48+
occupant = Thread.currentThread();
49+
notifyReturnFromEnter();
50+
Assertion.check(invariant(), "Invariant of monitor " + getName());
51+
}
52+
53+
/**
54+
* Leave the monitor. After returning from this method, the thread no longer
55+
* occupies the monitor. Only a thread that is in the monitor may leave it.
56+
*
57+
* @throws AssertionError
58+
* if the thread that leaves is not the occupant.
59+
*/
60+
protected void leave() {
61+
notifyLeaveMonitor();
62+
leaveWithoutATrace();
63+
}
64+
65+
/**
66+
* Leave the monitor. After returning from this method, the thread no longer
67+
* occupies the monitor. Only a thread that is in the monitor may leave it.
68+
*
69+
* @throws AssertionError
70+
* if the thread that leaves is not the occupant.
71+
*/
72+
protected <T> T leave(T result) {
73+
leave();
74+
return result;
75+
}
76+
77+
void leaveWithoutATrace() {
78+
Assertion.check(invariant(), "Invariant of monitor " + getName());
79+
Assertion.check(occupant == Thread.currentThread(), "Thread is not occupant");
80+
occupant = null;
81+
entrance.release();
82+
}
83+
84+
/**
85+
* Run the runnable inside the monitor. Any thread calling this method will be
86+
* delayed until the monitor is empty. The "run" method of its argument is then
87+
* executed within the protection of the monitor. When the run method returns,
88+
* if the thread still occupies the monitor, it leaves the monitor.
89+
*
90+
* @param runnable
91+
* A Runnable object.
92+
*/
93+
protected void doWithin(Runnable runnable) {
94+
enter();
95+
try {
96+
runnable.run();
97+
} finally {
98+
if (occupant == Thread.currentThread()) {
99+
leave();
100+
}
101+
}
102+
}
103+
104+
/**
105+
* Run the runnable inside the monitor. Any thread calling this method will be
106+
* delayed until the monitor is empty. The "run" method of its argument is then
107+
* executed within the protection of the monitor. When the run method returns,
108+
* if the thread still occupies the monitor, it leaves the monitor. Thus the
109+
* signalAndLeave method may be called within the run method.
110+
*
111+
* @param runnable
112+
* A RunnableWithResult object.
113+
* @return The value computed by the run method of the runnable.
114+
*/
115+
protected <T> T doWithin(RunnableWithResult<T> runnable) {
116+
enter();
117+
try {
118+
return runnable.run();
119+
} finally {
120+
if (occupant == Thread.currentThread()) {
121+
leave();
122+
}
123+
}
124+
}
125+
126+
/**
127+
* Create a condition queue associated with a checked Assertion. The Assertion
128+
* will be checked prior to an signal of the condition.
129+
*/
130+
protected Condition makeCondition(Assertion prop) {
131+
return makeCondition(null, prop);
132+
}
133+
134+
/**
135+
* Create a condition queue with no associated checked Assertion.
136+
*/
137+
protected Condition makeCondition() {
138+
return makeCondition(null, TrueAssertion.singleton);
139+
}
140+
141+
/**
142+
* Create a condition queue associated with a checked Assertion. The Assertion
143+
* will be checked prior to an signal of the condition.
144+
*/
145+
protected Condition makeCondition(String name, Assertion prop) {
146+
return new Condition(name, this, prop);
147+
}
148+
149+
/**
150+
* Create a condition queue with no associated checked Assertion.
151+
*/
152+
protected Condition makeCondition(String name) {
153+
return makeCondition(name, TrueAssertion.singleton);
154+
}
155+
156+
/** Register a listener. */
157+
public void addListener(MonitorListener newListener) {
158+
listOfListeners.add(newListener);
159+
}
160+
161+
private void notifyCallEnter() {
162+
for (MonitorListener listener : listOfListeners) {
163+
listener.callEnterMonitor(this);
164+
}
165+
}
166+
167+
private void notifyReturnFromEnter() {
168+
for (MonitorListener listener : listOfListeners) {
169+
listener.returnFromEnterMonitor(this);
170+
}
171+
}
172+
173+
private void notifyLeaveMonitor() {
174+
for (MonitorListener listener : listOfListeners) {
175+
listener.leaveMonitor(this);
176+
}
177+
}
178+
179+
void notifyCallAwait(Condition condition) {
180+
for (MonitorListener listener : listOfListeners) {
181+
listener.callAwait(condition, this);
182+
}
183+
}
184+
185+
void notifyReturnFromAwait(Condition condition) {
186+
for (MonitorListener listener : listOfListeners) {
187+
listener.returnFromAwait(condition, this);
188+
}
189+
}
190+
191+
void notifySignallerAwakesAwaitingThread(Condition condition) {
192+
for (MonitorListener listener : listOfListeners) {
193+
listener.signallerAwakesAwaitingThread(condition, this);
194+
}
195+
}
196+
197+
void notifySignallerLeavesTemporarily(Condition condition) {
198+
for (MonitorListener listener : listOfListeners) {
199+
listener.signallerLeavesTemporarily(condition, this);
200+
}
201+
}
202+
203+
void notifySignallerReenters(Condition condition) {
204+
for (MonitorListener listener : listOfListeners) {
205+
listener.signallerReenters(condition, this);
206+
}
207+
}
208+
209+
void notifySignallerLeavesMonitor(Condition condition) {
210+
for (MonitorListener listener : listOfListeners) {
211+
listener.signallerLeavesMonitor(condition, this);
212+
}
213+
}
214+
}

monitor-object/src/main/java/com/iluwatar/monitor/Assertion.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package com.iluwatar.monitor;/** * Assertions that may be checked from time to time. */public abstract class Assertion { private static final String DEFAULTMESSAGE = "Assertion Failure"; protected String message = DEFAULTMESSAGE; /** This method says whether the assertion is true. */ public abstract boolean isTrue(); /** Throw an AssertionError if the assertion is not true. */ public void check() { check(isTrue(), message); } /** Throw an AssertionError if the parameter is not true. */ public static void check(boolean b) { check(b, DEFAULTMESSAGE); } /** Throw an AssertionError if the boolean parameter is not true. */ public static void check(boolean b, String message) { if (!b) { throw new AssertionError(message); } }}

0 commit comments

Comments
 (0)