Bonjour,

Suite à beaucoups de remarques/questions sur les forums et dans ma société sur l'utilisation correcte des Threads, je soumet la classe qui suit.
Celle-ci permet de simplifier l'utilisation de Thread notamment pour des traitements que l'on veut mettre en pause/reprendre/stopper.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
package fr.brouillard.lang;
 
/**
 * ThreadTemplate ease the use of threads that need to be stop/pause/resume.
 *   
 * @author McFoggy aka Matthieu BROUILLARD (brouillard[DOT]matthieu[AT]gmail.com) : http://blog.matthieu.brouillard.fr
 */
public abstract class ThreadTemplate {
    /**
     * Default time used to wait on each execution loop
     */
    public static int DEFAULT_LOOP_WAIT = 50;
 
    private boolean executeOnlyOnce;
    private int loopWaitTime;
    private boolean mustWait;
    private Thread t;
    private boolean threadSuspended;
    private Object waiter = new Object();   // private object used for synchronisation purposes to pause/resume the inner thread
 
    /**
     * Default constructor, will build a ThreadTemplate that will run an execution loop without waiting between two loops and that will start immediately after the call to {@link #start()}
     */
    public ThreadTemplate() {
        this(false, false, DEFAULT_LOOP_WAIT, false);
    }
 
    /**
     * Constructor allowing to choose a single execution only if the given parameter is true.
     * The execution will start once the {@link #start()} method is called. If the given parameter is false then the ThreadTemplate will run an execution loop without waiting between two loops.
     * @param executeOnce true to execute the {@link #doIt()} method only once, false to execute it several times  
     */
    public ThreadTemplate(boolean executeOnce) {
        this(executeOnce, false, DEFAULT_LOOP_WAIT, false);
    }
 
    /**
     * Builds a ThreadTemplate object allowing to choose the waiting mode in the execution loop.
     * @param waitOnLoop true to wait between two loops execution
     * @param waitTime time in milliseconds to wait between two loops execution, will only be used if waitOnLoop is true
     */
    public ThreadTemplate(boolean waitOnLoop, int waitTime) {
        this(false, waitOnLoop, waitTime, false);
    }
 
    /**
     * Constructor allowing to choose the loop mode and the suspend state of the ThreadTemplate
     * @param executeOnce true to execute the {@link #doIt()} method only once, false to execute it several times  
     * @param startSuspended if true then the execution will be paused once the {@link #start()} method is called until a call to the {@link #resume()} method 
     */
    public ThreadTemplate(boolean executeOnce, boolean startSuspended) {
        this(executeOnce, false, DEFAULT_LOOP_WAIT, startSuspended); 
    }
 
    /**
     * Constructor defining the wait mode and the suspend state of the ThreadTemplate
     * @param waitOnLoop true to wait between two loops execution
     * @param waitTime time in milliseconds to wait between two loops execution, will only be used if waitOnLoop is true
     * @param startSuspended if true then the execution will be paused once the {@link #start()} method is called until a call to the {@link #resume()} method 
     */
    public ThreadTemplate(boolean waitOnLoop, int waitTime, boolean startSuspended) {
        this(false, waitOnLoop, waitTime, startSuspended); 
    }
 
    /**
     * Constructor allowing to choose all the initialisation values.
     * @param executeOnce true to execute the stuff to do only once, false will make it executed until a call to {@link #stop()} or until {@link #shouldEnd()} returns true. 
     * @param waitOnLoop boolean telling if a sleep must be done between each execution of the running loop
     * @param waitTime time in milliseconds to sleep between each execution of the running loop, only used if waitOnLoop is true 
     * @param startSuspended
     */
    public ThreadTemplate(boolean executeOnce, boolean waitOnLoop, int waitTime, boolean startSuspended) {
        loopWaitTime = waitTime;
        executeOnlyOnce = executeOnce;
        threadSuspended = startSuspended;
        mustWait = waitOnLoop;
    }
 
    /**
     * Starts/restart the execution of the ThreadTemplate 
     */
    public void start() {
        stop();
        init();
        t = new Thread(new ThreadTemplateRunnable());
        t.start();
    }
 
    /**
     * Stops definitely the execution of the ThreadTemplate 
     */
    public void stop() {
        t = null;
        synchronized (waiter) {
            waiter.notify();
        }
        synchronized (this) {
            notify();
        }
    }
 
    /**
     * Makes the ThreadTemplate to pause until the {@link #resume()} method is called
     */
    public void pause() {
        threadSuspended = true;
    }
 
    /**
     * Makes the ThreadTemplate to continue its execution after a call to {@link #pause()}
     */
    public void resume() {
        threadSuspended = false;
        synchronized (waiter) {
            waiter.notify();
        }
    }
 
    /**
     * Tells if the inner thread of the ThreadTemplate is still alive or not.
     * @return true if the inner thread is still alive.
     */
    public boolean isAlive() {
        return (t != null) && (t.isAlive());
    }
 
    /**
     * Return the pause status of the ThreadTemplate
     * @return a boolean value indicating the pause status of the ThreadTemplate
     */
    public boolean isPaused() {
        return threadSuspended;
    }
 
    /**
     * Method used to stop the loop of execution if the ThreadTemplate is not declared as executing only once.
     * By default it returns false to have an infinite loop.
     * @return true to stop the loop
     */
    protected boolean shouldEnd() {
        return false;
    }
 
    /**
     * Sample initialisation, override to do something more useful
     */
    protected void init() {
    }
 
    /**
     * Method that need to be overridden to do the required job.<br />
     * This method is executed several times if the ThreadTemplate is not declared as executing only once. 
     */
    protected abstract void doIt();
 
    /**
     * @return the defined time in milliseconds to wait between two execution loops if {@link #mustWaitOnLoop()} is true.
     */
    public int getLoopWaitTime() {
        return loopWaitTime;
    }
 
    /**
     * Defines the time to wait between two execution loops. 
     * @param loopWaitTime time in milliseconds to wait, if <= 0 then it is considered like a non waiting mode, i.e. calling {@link #setLoopWaitTime(int)} with a zero or negative value is the same as calling {@link #setMustWaitOnLoop(boolean)} with a false value 
     */
    public void setLoopWaitTime(int loopWaitTime) {
        if (loopWaitTime <= 0) {
            this.loopWaitTime = DEFAULT_LOOP_WAIT;
            this.mustWait = false;
        } else {
            this.loopWaitTime = loopWaitTime;
        }
    }
 
    /**
     * @return true if the ThreadTemplate is configured to wait between two execution loops.
     */
    public boolean mustWaitOnLoop() {
        return mustWait;
    }
 
    /**
     * Defines if the ThreadTemplate must wait between two execution loops.
     * @param mustWait true to make the ThreadTemplate object waiting between two execution loops.
     */
    public void setMustWaitOnLoop(boolean mustWait) {
        this.mustWait = mustWait;
    }
 
    /**
     * This the Runnable class used to do the stuff.
     * It contains all the logic for pause/resume/stop the Thread execution.<br />
     * Let's read @see <a href="http://java.sun.com/j2se/1.4.2/docs/guide/misc/threadPrimitiveDeprecation.html">Thread deprecation page</a>, for more explanation on the following code.
     */
    private class ThreadTemplateRunnable implements Runnable {
        public void run() {
            if (executeOnlyOnce) {
                doIt();
            } else {
                Thread thisThread = Thread.currentThread();
                while (t == thisThread && !shouldEnd()) {
                    // First we check if we need to suspend execution
                    try {
                        synchronized(waiter) {
                            while (threadSuspended && t==thisThread) {
                                waiter.wait();
                            }
                        }
                    } catch (InterruptedException e){}
 
                    // We then do the stuff, the test is needed in order to not execute
                    // the stuff after a call to stop if the ThreadTemplate was paused
                    if (!threadSuspended && t==thisThread && !shouldEnd()) {   
                        doIt();
                    }
 
                    // If we must wait for some time between two executions of the loop, we do it now
                    if (mustWait) {
                        try {
                            Thread.sleep(loopWaitTime);
                        } catch (InterruptedException ie) {}
                    }
                }
            }
 
            // We enforce a good end to the ThreadTemplate, we will call a notify on waiters
            stop();
        }
    }
}
Voici un example d'utilisation SWING avec une JProgressBar et des boutons stop/pause/reprendre.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package fr.brouillard.lang;
 
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
 
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
 
@SuppressWarnings("serial")
public class ProgressTest extends JFrame implements WindowListener {
    private static int MIN = 1;
    private static int MAX = 100;
    private JProgressBar pb;
    private ThreadTemplate threadTemplate;
 
    public ProgressTest() {
        this.threadTemplate = new ThreadTemplate(true, ThreadTemplate.DEFAULT_LOOP_WAIT) {
            private int value;
 
            @Override
            protected void doIt() {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        pb.setValue(++value);
                    }
                });
            }
 
            @Override
            protected boolean shouldEnd() {
                return value >= MAX;
            }
 
            @Override
            protected void init() {
                value = MIN;
            }
        };
    }
 
    public static void main(String[] args) {
        JFrame f = new JFrame();
        ProgressTest ttt = new ProgressTest();
        f.addWindowListener(ttt);
        f.getContentPane().add(ttt.buildPanel());
        f.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        f.pack();
        f.setVisible(true);
    }
 
    private Component buildPanel() {
        JPanel p = new JPanel(new BorderLayout());
        pb = new JProgressBar(MIN, MAX);
 
        p.add(pb, BorderLayout.NORTH);
        p.add(buildActionBar());
        return p;
    }
 
    private Component buildActionBar() {
        JPanel bar = new JPanel(new FlowLayout());
 
        bar.add(new JButton(new AbstractAction("start") {
            public void actionPerformed(ActionEvent e) {
                threadTemplate.start();
            }
        }));
        bar.add(new JButton(new AbstractAction("pause") {
            public void actionPerformed(ActionEvent e) {
                threadTemplate.pause();
            }
        }));
        bar.add(new JButton(new AbstractAction("resume") {
            public void actionPerformed(ActionEvent e) {
                threadTemplate.resume();
            }
        }));
        bar.add(new JButton(new AbstractAction("stop") {
            public void actionPerformed(ActionEvent e) {
                threadTemplate.stop();
            }
        }));
        return bar;
    }
 
    public void windowActivated(WindowEvent e) {
    }
 
    public void windowClosed(WindowEvent e) {
        threadTemplate.stop();
    }
 
    public void windowClosing(WindowEvent e) {
    }
 
    public void windowDeactivated(WindowEvent e) {
    }
 
    public void windowDeiconified(WindowEvent e) {
    }
 
    public void windowIconified(WindowEvent e) {
    }
 
    public void windowOpened(WindowEvent e) {
    }
}
Ainsi qu'un exemple de synchronisation entre 2 threads.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package fr.brouillard.lang;
 
public class WaitTest {
    public static void main(String[] args) {
        ThreadTemplate tt = new ThreadTemplate(true) {
            @Override
            protected void doIt() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {}
            }
        };
 
        tt.start();
 
        synchronized (tt) {
            try {
                tt.wait(1000);
            } catch (InterruptedException e) {}
            if (tt.isAlive()) {
                System.out.println("We have waited but the Thread is not finished yet");
            } else {
                System.out.println("The thread terminated its job before the end of our wait");
            }
        }
 
        tt.stop();
    }
}
N'hésitez surtout pas à faire des reqmarques pour améliorer la chose.