After a "multi-threaded Design Model summary (a)", this blog introduces 5 multi-threading Design Patterns
7) Thread-per-message
Implementing a method creates a new thread to complete the task, rather than completing the task in this method, which improves responsiveness because some tasks are time-consuming.
Sample program:
12345678910 |
public class Host {private final Handler _handler=new Handler ();p ublic void request (final int count, final char c) {new THR EAD () {public void run () {_handler.handle (count, c);}}. Start ();}} |
When implementing the method of the host class, a new thread is called to call the handler object to process the request requests.
New threads are created and started each time the request method of the host object is called, and the boot order of these new threads is not deterministic.
Applicable scenarios:
Suitable for use when the order of operation does not matter, because the new thread in the requested method is not deterministic in the boot order.
It can be used when no return value is required, because the request method does not wait for the thread to end to return, but returns immediately, without the result of the request being processed.
Precautions:
Each call creates and starts a new thread, and there is no control over the new thread, and only a very simple request in the actual application will use the Thread-per-message mode, because usually we will focus on the return result and also control the number of threads created, otherwise the system will be overwhelmed.
8) Worker Thread
In thread-per-message mode, each function call starts a new thread, but the operation to start a new thread is cumbersome, requires more time, and the system has a limit on the number of threads created. We can start a certain number of threads in advance, make up the thread pool, create a new task in the task pool each time the function call, and the pre-boot thread takes the task out of the task pool and executes it. This allows you to control the number of threads, and avoids the high cost of starting a new thread each time, enabling reuse of resources.
Sample program:
Channel class:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950 |
public class Channel {private static final int max_request = 100;private final request[] _request_queue;private int tail;p rivate int head;private int count; private workerthread[] _thread_pool; public Channel (int threads) {_request _queue = new Request[max_request];tail = 0;head = 0;count = 0;_thread_pool = new Workerthread[threads];for (int i = 0; I & Lt Threads i++) {_thread_pool[i] = new Workerthread ("worker-" + I, this);}} public void Startworkers () {for (int i = 0; i < _thread_pool.length; i++) _thread_pool[i].start ();} public synchronized Request takerequest () throws Interruptedexception {while (Count <= 0) {wait ();} Request Request = _request_queue[head];head = (head + 1)% _request_queue.length;count--;notifyall (); return request;} public synchronized void Putrequest (Request request) throws Interruptedexception {while (Count >= _request_ Queue.length) {wait ();} _request_queue[tail] = Request;tail = (tail + 1)% _request_queue.length;cOunt++;notifyall ();} } |
Workerthread class:
1234567891011121314151617181920212223 |
public class Workerthread extends Thread {private final Channel _channel; public workerthread (String name, channel Channel {super (name); _channel = Channel;} @Overridepublic void Run () {while (true) {request Request;try {request = _channel.takerequest (); Request.execute ();} catch (Interruptedexception e) {//TODO auto-generated catch Blocke.printstacktrace ();}}} } |
The Channel class integrates the thread pool and task pool, provides the Startworkers method externally, calls the method to start all worker threads, and then adds the task to the task pool through the Putrequest method, and the worker thread automatically pulls the task out of the task pool and executes it.
Applicable scenarios:
Like the thread-per-message pattern, the Worker thread pattern implements the separation of invocation and exectution, that is, the invocation and execution of the detach, where the caller invokes the method to run on one thread, and the task executes on another thread. The caller can return immediately after invoking the method, which improves the responsiveness of the program. It is also because of the separation of invocation and execution, we can control the order of execution of the task, we can also cancel the task, but also decentralized processing, the task to different machine execution, if the call and execution is not separated, these features are not possible.
Programs that are suitable for a large number of tasks and that require separate tasks, such as application distribution apps, require frequent and server communication to obtain data, and communication messages may have priority.
Precautions:
Pay attention to the number of worker threads, if too many, then there will be a lot of worker threads do not work, will waste system resources, if too little will make the task pool full, causing other threads long-term blocking. The number of threads can be adjusted according to the actual work, and the maximum number of task pools in the task pool.
If the worker thread is only one, the scope of worker threading becomes single-threaded, eliminating the need to share mutexes. Usually the GUI framework is so implemented, the interface of the thread only one, interface elements of the method does not need to share mutual exclusion. If the interface of the thread has more than one, then must be shared mutually exclusive, we also often design the subclass of the interface element, the subclass implementation of the overriding method must also use synchronized to share the mutex, the introduction of shared mutex will introduce the cost of lock synchronization, the program performance is reduced, And if there is an inappropriate order to acquire locks, it is easy to create deadlocks, which makes GUI programming very complex, so the GUI framework generally uses single-threaded.
Java 5 's concurrent package already has a thread pool-related class, eliminating the need to implement threading pooling on its own. The thread pool can be started using the Executors method, which includes Newfixedthreadpool,newsinglethreadexecutor,newcachedthreadpool, Newscheduledthreadpool and so on.
9) Future
In thread-per-message mode and worker thread mode, we implemented a call and execution separation. But usually we call a function to get the return value, in the above two modes, although the implementation of the call and execution phase separation, but can not get the return result of the call execution. The future mode can get execution results, return a future on call, and get real execution results through the future.
Sample program:
Host class
123456789101112131415161718 |
public class Host {public Data request (final int count, final char c) {System.out.println ("request (" + count + "," + C + ") BEGIN"), final futuredata future = new Futuredata (); new Thread () {@Overridepublic void run () {Realdata realdata = new Realdata (count, c); Future.setrealdata (realdata);} }.start (); return to the future;} } |
Data interface
123 |
Public interface Data {public String getcontent ();} |
Futuredata class
123456789101112131415161718192021222324 |
public class Futuredata implements Data {private Boolean _ready = false;private Realdata _real_data = null; public synchro nized void Setrealdata (Realdata realdata) {if (_ready) return;_real_data = Realdata;_ready = True;notifyall ();} @ Overridepublic synchronized String getcontent () {while (!_ready) {try {wait ()} catch (Interruptedexception e) {}}return _ Real_data.getcontent ();} } |
Realdata class
1234567891011121314151617181920212223242526 |
public class Realdata implements Data {private final String _content; public realdata (int count, char c) {System.out.prin TLN ("Making realdata (" + count + "," + C + ") BEGIN"); char[] buffer = new Char[count];for (int i = 0; I < count; i++) {Buffer[i] = c;try {thread.sleep (+);} catch (Exception e) {}}system.out.println ("Making Real Data (" + count + "," + C + ") END"); _content = new String (buffer);} @Overridepublic String getcontent () {return _content;}} |
Applicable scenarios:
If you want to implement both invocation and execution separation and want to get execution results, it is appropriate to use future mode.
The future mode can get the return value of the Async method call, separating both the prepare return value and the use return value procedure.
Precautions:
Java 5 Concurrent package has a future interface, not only to get the results returned, but also to cancel the task execution. When you invoke the Submit method of the Executorservice object to submit a callable task to the task pool, you get a future object that gets the result of the task execution and cancels the task execution.
TEN) two-phase termination
This section describes how to stop a thread, and when we start learning a thread, it may be easy to make the mistake of invoking the thread's stop method of stopping the threads, which can really quickly stop the thread and cause the thread to throw an exception. However, calling the Stop method is not safe, and if the line is impersonating acquires a lock on an object, the lock is not freed, the other threads continue to be blocked in the condition queue of the lock, and perhaps the work the thread is doing is not interrupted, which can cause system damage. From a threading perspective, only the thread executing the task knows when to properly stop the task, so we need to use the two-phase termination mode to stop the thread, and in that mode, if you want to stop a thread, set the flag for the request thread to stop first to true, The thread's interrupt method is then called, and every work done in that thread checks the flag for the request thread to stop, and if true, ends the threads safely.
Sample program:
1234567891011121314151617181920212223242526272829303132333435363738 |
public class Countupthread extends Thread {private long counter = 0; private volatile Boolean _shutdown_requested = false; public void Shutdownrequest () {_shutdown_requested = True;interrupt (),} public boolean isshutdownrequested () {return _sh utdown_requested;} @Overridepublic void Run () {try {while (!_shutdown_requested) {doWork ()}} catch (Interruptedexception e) { E.printstacktrace ();} finally {Doshutdown ();}} private void DoWork () throws interruptedexception {counter++; System.out.println ("Dowork:counter =" + counter); Thread.Sleep (500);} private void Doshutdown () {System.out.println ("Doshutdown:counter =" + counter);}} |
The outside world can call shutdownrequest to stop the thread.
Applicable scenarios:
Consider using two-phase termination mode when you need to stop a thread
Precautions:
When we stop the request thread, it is not enough to set the request stop flag only, because if the thread is performing a sleep operation, then the execution of the statement to the check stop flag exits after the sleep operation is executed, so the program is not responsive.
A thread that responds to a stop request is not enough if it checks only the interrupt state (not the STOP flag we set), and if the thread is asleep or wait, it throws a Interruptedexception exception, even if no exception is thrown, and the thread becomes interrupted. It seems that we do not need to set the STOP flag, just check the interruptedexception or use the Isinterrupted method to check the current thread's interrupt state, but this will introduce a potential danger, If the method called by the thread ignores interruptedexception, or if a method of the object used by the thread ignores interruptedexception, this is common, especially if we do not know its implementation when we use some class library code. Even if we ignore the interruptedexception, we do not know that in this case we cannot check to see if any other thread is requesting this thread to exit, so it is necessary to set the terminal flag, unless you can guarantee that all objects referenced by the thread (including indirectly referenced) The interruptedexception is not ignored, or the interrupt state can be saved.
The interrupt state and interruptedexception can be transferred to each other:
1) interrupt status, Interruptedexception
- 1) interrupt status, Interruptedexception
123 |
if (thread.interrupted) {throw new Interruptedexception ()} |
- 2) Interrupt Status Interruptedexception
12345 |
Try{thread.sleep (1000);} catch (Interruptedexception e) {thread.currentthread (). interrupt ();} |
- 3) Interruptedexception-interruptedexception
12345678910 |
Interruptedexception savedinterruptexception = null;...try{thread.sleep (1000);} catch (Interruptedexception e) {savedinterruptexception=e;} ... if (savedinterruptexception! = null) throw savedinterruptexception; |
One) thread-specific Storage
We know that if an object is not accessed by multiple threads, there is no thread-safety issue. Thread-specific storage mode is a design pattern that generates a separate object for each thread, addressing thread safety issues. However, the details of generating individual objects for a thread are hidden from the user, and can only be used simply by the user. You need to use the Threadlocal class, which is a thread safe that holds separate objects for each thread.
Sample program:
Log class
123456789101112131415161718192021222324 |
public class Log {private static threadlocal<tslog> _ts_log_collection = new threadlocal<tslog> (); public static void println (String s) {Gettslog (). println (s), public static void Close () {Gettslog (). Close ();} Private STA Tic Tslog Gettslog () {Tslog tslog = _ts_log_collection.get (); if (Tslog = = null) {Tslog = new Tslog (Thread.CurrentThread () . GetName () + "-log.txt"); _ts_log_collection.set (Tslog);} return tslog;} } |
Tslog class
12345678910111213141516171819 |
public class Tslog {private PrintWriter _writer = null, public tslog (String fileName) {try {_writer = new PrintWriter (file Name);} catch (FileNotFoundException e) {e.printstacktrace ();}} public void println (String s) {_writer.write (s),} public void Close () {_writer.close ();}} |
Applicable scenarios:
Using the Thread-specific storgae mode can be a good solution to multithreading security problems, each thread has a separate object, if the time to get a unique object from the Threadlocal class is much less than the call object method execution time, can improve program performance. Therefore, in the log system if you can create a log file for each thread, it is particularly appropriate to use the Thread-specific storage mode.
Precautions:
Using the Thread-specific storage mode means that the thread thread has information placed outside the thread, and in the example program, we place the tslog of the line thread in the Threadlocal instance. Usually we generally put the thread thread information inside the thread, such as the creation of a thread class sub-class Mythread, we declare the Mythread field, is the line thread information. Because the thread thread has information placed outside the thread, each of the threads accesses its own unique information when accessing its unique information, but it can be difficult to debug because there is a hidden context (the current thread environment), and the previous behavior of the program may cause the context to appear abnormally. But the real cause of the current bug, we are more difficult to find out what the previous behavior of the thread caused the context to appear abnormally.
Design multi-threaded programs, the principal refers to the active operation of the object, generally refers to the thread, the object refers to the thread calls the objects, generally refers to the task object, because the focus on the "subject" and "Object" different, there are two ways to develop:
- 1) actor-based focus on the subject
- 2) task-based focus on the object
Actor-based focuses on the subject, focuses on threading, maintains state by thread, and puts work-related information into the fields of the thread class, like this
123456 |
Class Actor extends thread{internal state public void run () {Take a task externally, change the loop of its internal State}} |
Task-based focuses on the object, encapsulates the state into a task object, and passes these task objects between threads, called messages, requests, or commands. The most typical example of using this development approach is the worker Thread pattern, the producer consumer model. The task looks like this:
123456 |
Class Task implements runnable{the information required to perform the task public void Run () { The processing content required to perform the task}} |
In fact, these two development methods are mixed, I just design multi-thread program, always based on actor-based thinking mode, even in solving the problem of producer consumers also use actor-based thinking mode, resulting in procedural structure confusion, so it is best to follow the actual scene, Suitable for use actor-based development way of use actor-based, suitable for task-based development way of use task-based.
Reprint http://www.cloudchou.com/softdesign/post-616.html
Summary of multithreading design pattern (II.)