Some thoughts on using the thread pool to cause the Tomcat process not to exit normally

Source: Internet
Author: User
Tags thread class thread stop volatile

A multithreaded producer consumer model was recently used in the project to simulate Message Queuing processing, but it was found that when a thread was required to exit, the Tomcat process could not be stopped because it failed to handle the exit thread. After some toss to summarize the experience in this area.

There are two common ways of threading interrupts, one is to use a tag bit, to decide whether to exit the thread's execution in code execution, or to terminate the thread by using the interrupt method of the thread class.

So, at first, I used the second way to break the thread of execution. In my code, I use Linkedblockingqueue as a queue for producer and consumer tasks, and the Put method and take method of this queue respond to interrupts, so as long as the thread's interrupt method is invoked, These two methods throw interruptedexception, so I just catch the exception in the logic of the code and do some processing to get the thread out of the way.

However, the fact is not as good as I think, according to the above, I call executor at the end of the thread pool Shutdownnow method, see the source code of this method know that it will call on all tasks interrupt method. Because there are many places in my code that invoke the HBase API, and HBase has many APIs that create new threads to perform some I/O calls such as RPC, these I/O calls throw out the unhandled exception as the thread's interrupt method is invoked.

Interruptedioexception, because these exceptions are thrown in a new thread, my code cannot catch, so it causes the thread to stop abnormally. This results in the Tomcat process not exiting normally. Using the Jstack tool under the JDK, you can see that the virtual machine has been waiting for the thread to terminate. Therefore, you must use other mechanisms to enable the thread to stop properly.


After discovering the problem, I used the method of setting the flag bit to handle the thread's exit behavior.

When you want to end a thread, set the flag bit of the thread, and the code can exit after the decision bit has been modified. But there is a problem with the take or put method that the code in Blockingqueue might block, causing the thread to be unable to determine its flag bit. Looking at the source code of the Linkedblockingqueue, it is found that there are ways to solve this problem, that is, using the poll method and the Offer method, which can set a timeout flag that stops blocking when the blocking times out, So that we can do some other things.

Below, I use a very rough way to manage these threads.

A private flag bit is set in both the producer and consumer's code to determine whether the thread should stop and to set a private method to modify the flag bit.

The first is the code for producer Runnable, whose logical logic is to traverse each row in the HBase table and then put each row of data into Blockingqueue. If an offer causes a blockage, it blocks until the timeout, and then traverses the next line or exits, and the rows that are not traversed are traversed in the next traversal, so there is no need to worry.

Package Com.cyber_space.

Queue;
Import java.io.IOException;
Import java.io.InterruptedIOException;
Import java.nio.channels.ClosedByInterruptException;
Import Java.util.Iterator;
Import Java.util.concurrent.BlockingQueue;

Import Java.util.concurrent.TimeUnit;
Import Org.apache.hadoop.hbase.client.Result;
Import Org.apache.hadoop.hbase.client.ResultScanner;

Import org.apache.hadoop.hbase.util.Bytes; Import Com.cyber_space.
hbasemanager.queuehtable; Import Com.cyber_space.
Queue.Operation.QueueOperation;

Import Com.cyber_space.util.Log;
	public class Producer implements Runnable {private queuehtable QH;
	Private Resultscanner RS;
	Private final blockingqueue<queueoperation> Bq;
	private int sleeptime;

	Private volatile Boolean isinterrupted = false;
		Public Producer (blockingqueue<queueoperation> bq, int sleeptime) {THIS.BQ = BQ;
	This.sleeptime = Sleeptime;
		@Override public void Run () {Log.logger.info ("producer thread starts working"); while (!isinterrupted) {try {WhilE (bq.size ()!= 0) {//When the queue is not empty, the queue is not placed into the task TimeUnit.SECONDS.sleep (sleeptime);
				Thread.yield ();
				} QH = new queuehtable ();
				rs = Qh.getall ();
				Iterator<result> iterator = Rs.iterator ();
					while (Iterator.hasnext () &&!isinterrupted) {result r = Iterator.next ();
					String Rowkey = bytes.tostring (R.getrow ());
					String tag = Qh.gettag (Rowkey);
					String Userrowkey = Qh.getuserrowkey (Rowkey);
					String operationinfo = Qh.getoperationinfo (Rowkey);
					Queueoperation df = new Queueoperation (Operationinfo, Userrowkey, Rowkey, tag);
					Log.logger.info ("will operate" + tag + "put queue");
				Bq.offer (DF, 2, timeunit.seconds); catch (Interruptedexception | interruptedioexception |
				Closedbyinterruptexception e) {//exit thread immediately after receiving interrupt request Log.logger.info ("Producer receives interrupt request, exit thread");
			Break
				A catch (Exception e) {if (Thread.CurrentThread (). isinterrupted ()) break;
			Log.logexception (e); finally {if (QH!= null) try {QH. Close ();
						A catch (IOException e) {if (Thread.CurrentThread (). isinterrupted ()) break;
					Log.logexception (e);
	}} Log.logger.info ("Producer thread exits:" + thread.currentthread (). GetId ());
	public void Stopwork () {this.isinterrupted = true;
 }
}

The following code is the consumer thread, the consumer thread is constantly reading data from the Blockingqueue, and then performing the appropriate action, it uses the poll method, also set the time-out, after the timeout, the code needs to make some logical judgments, decide whether to exit the thread.

Package Com.cyber_space.

Queue;
Import java.io.IOException;
Import Java.util.concurrent.BlockingQueue;

Import Java.util.concurrent.TimeUnit; Import Com.cyber_space.
Exception.fileexception; Import Com.cyber_space.
Queue.Operation.QueueOperation;

Import Com.cyber_space.util.Log;
	public class Consumer implements Runnable {blockingqueue<queueoperation> BQ;

	Private volatile Boolean isinterrupted = false;
	Public Consumer (blockingqueue<queueoperation> df) {BQ = df;
			@Override public void Run () {try {Log.logger.info ("Consumer thread starts working");
				while (!isinterrupted) {//a task is in the queue, the queueoperation df = null cannot be exited directly because of the interruption;
					try {df = Bq.poll (2, timeunit.seconds);
					if (df = null) {continue;
				} df.dooperation ();
					The data in the catch (Interruptedexception e) {//queue is not lost even if it is not processed, because the queue data is the Log.logger.info that deletes the table data in the HBase ("Receive interrupt request, exit thread");
				Break
					catch (Fileexception e) {log.logexception (e); Log.storesystemlog ("Delete queue Delete file error", E, Log.)logsign.delete_failed, "Delete queue processing threads", "Delete queue processing threads");
				Bq.add (DF);
		} Log.logger.info ("Consumer thread Exit" + Thread.CurrentThread (). GetId ());
		catch (IOException e) {log.logexception (e);
	The public void Stopwork () {this.isinterrupted = true;
 }
}


Here is a slightly modified executor class that I used to do a special thread stop operation. The code uses a combination of methods to wrap the excutors-generated thread pool. When it calls shutdown, it first executes a stopwork call to our custom runnable to stop the thread service and then call the shutdown method. Notice that the code that looks at the thread pool will know that the shutdown method for the task being executed (worker) does not call its interrupt method because it cannot get the lock on which the task is being performed, that is, the tryacquire of the worker returns false. So it's not going to affect our work.

Package Com.cyber_space.

Queue;
Import java.util.ArrayList;
Import Java.util.concurrent.ExecutorService;
Import java.util.concurrent.Executors;

Import Java.util.concurrent.TimeUnit;

Import Com.cyber_space.util.Log;
	public class Queueexcutor {Executorservice exec = Executors.newfixedthreadpool (10);

	arraylist<runnable> runnables = new arraylist<> ();
		public void Execute (Runnable r) {if (R instanceof Producer | | | r instanceof Consumer) {Runnables.add (R);
	} exec.execute (R); public void Shutdown () {for (Runnable r:runnables) {if (R instanceof Consumer) {Consumer c = (Consumer)
				R
			C.stopwork ();
				else if (r instanceof Producer) {Producer p = (Producer) r;
			P.stopwork ();
		} try {TimeUnit.SECONDS.sleep (queue.sleep_time);
		catch (Interruptedexception e) {log.logexception (e);
	} exec.shutdown (); public void Awaittermination (long timeout, timeunit unit) throws Interruptedexception {exec.awaittermination (timeout, unit);
 }

}

Then, for the thread pool to start and stop, just write it in the listener frame of Tomcat, OK. Start the thread pool in the Contextinitialized method and stop the threads at the contextdestroyed thread pool.

Package Com.cyber_space.

Listener;

Import Java.util.concurrent.TimeUnit;
Import javax.servlet.ServletContextEvent;
Import Javax.servlet.ServletContextListener;

Import Javax.servlet.annotation.WebListener; Import Com.cyber_space.
Queue.consumer; Import Com.cyber_space.
Queue.queue; Import Com.cyber_space.
Queue.queueexcutor; Import Com.cyber_space.
Queue.producer;

Import Com.cyber_space.util.Log;
	@WebListener () public class Queuelistener implements Servletcontextlistener {Queue dq;

	Queueexcutor exec = new Queueexcutor ();
		public void contextdestroyed (Servletcontextevent arg0) {Log.logger.info ("Delete queue ready to stop");
			if (exec!= null) {Exec.shutdown ();
			try {exec.awaittermination (3, timeunit.seconds);
			catch (Interruptedexception e) {log.logexception (e);
		}} public void contextinitialized (Servletcontextevent arg0) {Log.logger.info ("Delete queue begins work");
			try {dq = new Queue ();
			Exec.execute (New Producer (Dq.getqueue (), queue.sleep_time)); for (int i = 0; I &Lt Queue.consumer_num;
			++i) {Exec.execute (New Consumer (Dq.getqueue ()));
		The catch (Exception e) {log.logexception (e); }
	}
}

The code above will ensure that the thread pool is started and terminated normally in Tomcat. This code is actually too rough, in fact, you can create a Threadpoolexecutor instance to customize your own threadfactory, this threadfactory create our custom thread, rewrite the interrupt method, before the stop operation, The Stopwork method is called first. Still have time to do it, a holiday.




Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.