This blog is a subsequent article of the "Java multi-thread programming" series. For details about other blogs of the "Java multithreading programming" series, refer to the end of this blog.
How can we control the execution order of multiple threads?
Method 1:Set thread priority.
Java. lang. thread provides the setpriority (INT newpriority) method to set the priority of the thread, but the priority of the thread cannot guarantee the execution order of the thread, the priority only increases the probability that a thread with a higher priority obtains CPU resources. That is to say, this method is unreliable.
Method 2:Merge using threads.
Use the join () method of Java. Lang. thread. For example, if there is thread A, and the current thread wants to wait for a to execute it again, A. Join () can be used (). Once the thread uses a. Join (), the current thread will continue to run after a is extinct. When will a die out? The execution of the run () method of a ends and a disappears.
This method can be used for Thread Scheduling effectively, but it can only be used to wait for the execution scheduling of a thread. It is obviously powerless to wait for n threads. In addition, the waiting thread must get the instruction to continue to execute after it is waiting for the thread to die. The real concurrency of the two threads cannot be achieved, and the flexibility is poor.
Method 3:Use thread Communication.
Java. Lang. Object provides methods for inter-thread communication, such as Wait, y, and yyall. Every Java object has a implicit thread lock concept. Through this thread lock concept, we can allow communication between threads, and each thread will not work alone. The famous "producer-consumer" model is implemented based on this principle.
This method can also be effectively used for thread scheduling. It is not only limited to waiting for the execution and scheduling of a thread, but also flexible to a great extent. However, the operations are complex, difficult to control, and easy to cause confusion, and the program maintenance is not convenient.
Method 4:Use locking.
Locking is like a door. Before the prerequisite is met, the door is closed, and the thread cannot pass. After the prerequisite is met, the thread can continue to execute the lock. Java. util. concurrent. countdownlatch is a very practical locking implementation. It provides countdown () and await () methods to achieve thread execution queues, this method is most suitable for M threads waiting for n threads to finish and then execute. First, initialize a countdownlatch object, such as countdownlatch donesignal = new countdownlatch (N). This object has
N is used as the Count Threshold. Each waiting thread can use countdown () to reduce the Count Threshold of donesignal by one by holding the donesignal object; each waiting thread uses await () to block the current thread by holding the donesignal object until the value of the donesignal Count Threshold is reduced to 0.
This method can also effectively perform Thread Scheduling and is easier to manage than method 3. Developers only need to control countdownlatch. However, the thread execution order management is relatively simple. It only indicates the number of threads waiting currently, and the initial threshold value of countdownlatch can only be decreased once set, and cannot be reset. If you need to reset the threshold value in the decreasing process, refer to Java. util. Concurrent. javasicbarrier.
In any case, countdownlatch is useful for the fulfillment of thread queues under certain conditions. It is still effective for thread management in complex environments. Therefore, it is necessary to be familiar with and grasp the use of it.
The following is an example of countdownlatch in an actual project:
Private Map <long, decryptsignalandpath> partition = new hashmap <long, decryptsignalandpath> (); // todo pay attention to the cleaning of container junk data class decryptrunnable implements runnable {private serverfilebean serverfile; private long FID; // point to the decryption file private countdownlatch decryptsignal; protected decryptrunnable (long FID, serverfilebean serverfile, countdownlatch decryptsignal) {This. FID = FID; this. serverfile = serverfile; this. decryptsignal = decryptsignal;} @ overridepublic void run () {// start decryption string encryption = NULL; decryptsignalandpath = new decryptsignalandpath (); decryptsignalandpath. setdecryptsignal (decryptsignal); afterdecryptfilepathmap. put (FID, decryptsignalandpath); afterdecryptfilepath = decryptfile (serverfile); decryptsignalandpath. setafterdecryptfilepath (afterdecryptfilepath); decryptsignal. countdown (); // notify all blocked threads} class decryptsignalandpath {private string afterdecryptfilepath; private countdownlatch decryptsignal; Public String getafterdecryptfilepath () {return afterdecryptfilepath ;} public void setafterdecryptfilepath (string afterdecryptfilepath) {This. afterdecryptfilepath = afterdecryptfilepath;} public countdownlatch getdecryptsignal () {return decryptsignal;} public void setdecryptsignal (countdownlatch decryptsignal) {This. decryptsignal = decryptsignal ;}}
To be executed first, the waiting thread is added here:
Countdownlatch decryptsignal = new countdownlatch (1); new thread (New decryptrunnable (FID, serverfile, decryptsignal )). start (); // No need to get the new thread handle. countdownlatch tracks try {decryptsignal. await ();} catch (interruptedexception e) {// todo auto-generated Catch Block}
After execution is required, the waiting thread can be added as follows:
CountDownLatch decryptSignal = afterDecryptFilePathMap.get(fid).getDecryptSignal();try {decryptSignal.await();} catch (InterruptedException e) {// TODO Auto-generated catch block}
Of course, this is just a simple example of countdownlatch. It is a little useless for countdownlatch, because it can be used in a more complex multi-threaded environment. In this example, we can use thread communication. Because the countdownlatch threshold value is initially 1, we can even replace it with the merge of threads mentioned in method 2.
If you think the above examples are not clear enough, you can refer to the demo provided by jdk api. This is clear:
class Driver2 { // ... void main() throws InterruptedException { CountDownLatch doneSignal = new CountDownLatch(N); Executor e = ... for (int i = 0; i < N; ++i) // create and start threads e.execute(new WorkerRunnable(doneSignal, i)); doneSignal.await(); // wait for all to finish } } class WorkerRunnable implements Runnable { private final CountDownLatch doneSignal; private final int i; WorkerRunnable(CountDownLatch doneSignal, int i) { this.doneSignal = doneSignal; this.i = i; } public void run() { try { doWork(i); doneSignal.countDown(); } catch (InterruptedException ex) {} // return; } void doWork() { ... } }
Other blogs in the "Java multi-thread programming" series:
The difference between a process and a thread in Java multi-thread programming, concurrency and Parallelism: the metaphor of eating steamed bread
Use of the volatile keyword for Java multi-thread programming
Java multi-thread programming 3: Use of the synchronized keyword
Java multi-thread programming 4: getting all the threads currently running in Java VM
Java multi-thread programming 5: An Example of understanding wait () and notify ()
Java multi-thread programming 6: communication between threads (with source code)
Java multi-thread programming 7: deadlock (source code)