1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package com.ochafik.lang;
23
24 import java.awt.event.ActionEvent;
25 import java.awt.event.ActionListener;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.NoSuchElementException;
29 import java.util.concurrent.Semaphore;
30
31
32
33
34
35
36
37
38
39
40
41
42
43 public final class Threads {
44 private final List<Runner> runners = new ArrayList<Runner>();
45 private final Semaphore semaphore = new Semaphore(0);
46 private boolean fired = false, started = false;
47
48 private List<ActionListener> actionListeners;
49
50 private class Runner extends Thread {
51 private final Runnable runnable;
52 public Runner(Runnable runnable) {
53 this.runnable = runnable;
54 }
55 public void run() {
56 try {
57 runnable.run();
58 } finally {
59 int nThreads = runners.size();
60 if (semaphore.tryAcquire(nThreads - 1)) {
61 semaphore.release(nThreads);
62 synchronized (this) {
63 if (!fired) {
64 fired = true;
65 fireActionPerformed();
66 }
67 }
68 } else {
69 semaphore.release();
70 }
71 }
72 }
73 }
74
75
76
77
78
79
80 public synchronized <T extends Runnable> T add(T runnable) {
81 if (started)
82 throw new IllegalThreadStateException("Cannot add another runnable to " + getClass().getSimpleName() + " after it started !");
83
84 runners.add(new Runner(runnable));
85 return runnable;
86 }
87
88
89
90
91
92
93 public synchronized void start() {
94 if (started)
95 throw new IllegalThreadStateException(getClass().getSimpleName() + " already started !");
96
97 if (runners.isEmpty())
98 throw new NoSuchElementException("No runnable were added to this " + getClass().getSimpleName());
99
100 for (Runner t : runners) {
101 t.start();
102 }
103 started = true;
104 }
105
106
107
108
109
110 public synchronized void interrupt() {
111 if (!started)
112 throw new IllegalThreadStateException(getClass().getSimpleName() + " not started !");
113
114 for (Runner t : runners) {
115 try {
116 t.interrupt();
117 } catch (IllegalThreadStateException ex) {
118
119 ex.printStackTrace();
120 }
121 }
122 }
123
124
125
126
127
128
129
130 public synchronized void join() throws InterruptedException {
131 int nThreads = runners.size();
132 if (nThreads == 0)
133 return;
134
135 if (!started)
136 start();
137
138 semaphore.acquire(nThreads);
139 semaphore.release(nThreads);
140 }
141
142 public enum State {
143 NotStarted, Running, Finished, NoRunnables
144 }
145
146 public synchronized State getState() {
147 int nThreads = runners.size();
148 if (nThreads == 0)
149 return State.NoRunnables;
150
151 if (!started)
152 return State.NotStarted;
153
154 if (semaphore.tryAcquire(nThreads)) {
155 semaphore.release(nThreads);
156 return State.Finished;
157 }
158 return State.Running;
159 }
160
161
162
163
164
165
166 public synchronized void addActionListener(ActionListener actionListener) {
167 if (actionListeners == null)
168 actionListeners = new ArrayList<ActionListener>();
169
170 actionListeners.add(actionListener);
171
172 if (fired) {
173 actionListener.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, ""));
174 }
175 }
176
177 private synchronized void fireActionPerformed() {
178 if (actionListeners == null)
179 return;
180
181 ActionEvent a = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "");
182 for (ActionListener l : actionListeners)
183 l.actionPerformed(a);
184 }
185
186 }