Java高效并发(八)----扩展线程池、退出线程池、优化线程池的线程数量

 ThreadPoolExecutor是一个可以扩展的线程池

在 ThreadPoolExecutor类中有三个空的方法,可以看到这三个方法都是protected权限的

protected void beforeExecute(Thread t, Runnable r) { }
protected void afterExecute(Runnable r, Throwable t) { }
protected void terminated() { }

protected的可见性在于两点:

  • 父类的protected成员是包内可见的,并且对子类可见;

  • 若子类与基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的protected方法,而不能访问基类实例的protected方法。

那么我们创建的线程池实例就不可以直接访问 ThreadPoolExecutor的这三个方法,但是可以通过匿名内部类重写这三个方法来提供方法的可见性。

new ThreadPoolExecutor()其实是子类的对象,就是匿名类的对象,这个类其实继承了ThreadPoolExecutor,pool是父类的引用。子类重写父类的方法,是多态的体现

多态中,父类的引用只能使用子类已经覆盖的方法,不能使用子类特有的方法,在这个例子中,要访问的不不是子类特有的方法,而是父类引用不可见的方法(protected的可见性的第二点

package xidian.lili.reentrantlock;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import xidian.lili.reentrantlock.ThreadFactoryDemo.MyTask;

public class ExtThreadPool {

	public static class MyTask implements Runnable{
		public String name;
		
		public MyTask(String name) {
			this.name = name;
		}
		@Override
		public void run() {
			try {
				System.out.println("正在执行:Thread ID"+Thread.currentThread().getId()+" is execute "+this.name);
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}		
		}	
	}
	
	public static void main(String[] args) throws InterruptedException {
		ExecutorService pool=new ThreadPoolExecutor(5,5,0L,TimeUnit.MILLISECONDS,
				new LinkedBlockingQueue<Runnable>(10)){

					@Override
					protected void beforeExecute(Thread t, Runnable r) {
						System.out.println(t.getId()+"准备执行 "+((MyTask)r).name);
					}

					@Override
					protected void afterExecute(Runnable r, Throwable t) {
						System.out.println(((MyTask)r).name+" 执行完毕");
					}

					@Override
					protected void terminated() {
						System.out.println("线程池退出");
					}
			
		};
		for(int i=0;i<5;i++){
			MyTask task=new MyTask("task"+i);
			pool.execute(task);
			Thread.sleep(10);
		}
		pool.shutdown();
	}

}

 那么通过重写这三个方法我们可以对线程池的运行状态进行跟踪,输出一些有用的调试信息,最后我们使用shutdown方法退出线程池。

退出线程池的方法

(1)shutdown()关闭线程池,但是不会强制退出,而是等到已提交的任务全部执行完,但是一旦调用shutdown,就不会接受新提交的任务,会抛出RejectedExecutionException异常

(2) public List<Runnable> shutdownNow()方法,立刻停止不再接受新任务,试图停止所有正在执行的线程,试图终止的方法是调用Thread.interrupt(),但是如果线程中没有sleep 、wait、Condition、定时锁等应用来响应中断, interrupt()方法是无法中断当前的线程的。所以,ShutdownNow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。如果响应了中断,会把把任务队列中还没有执行的任务以列表的形式返回

(3)上一篇中我们重写了ThreadFactory将线程池的线程设置为守护线程,程序执行完(主线程执行完),也会强制退出线程池

停止线程的方法

(1)使用标志位,while(true)

(2)Thread.interrupt()发出中断信号中断目标线程,实际上也只是设置中断标志位为true,对中断正确的理解是:并不真正中断正在运行的线程,而只是发出中断请求,由线程在下一个合适的时刻中断自己。中断是取消线程最合理的方式。

会检查中断状态并响应的阻塞库包括:

1、Thread.sleep()

2、Object.wait()

3、BlockingQueue.put()/take()

4、Thread.join()

5、lock.lockinterruptibly()  (lock.lock不支持中断)

6、Condition.await()  (Condition.awaitUninterruptibly()不支持中断)

7、SemapDemo.acquire()   (SemapDemo.acquireUninterruptibly()不支持中断 )

8、CyclicBarrier.await()

9、CountDownLatch.await()

10、LockSupport.park()

如果代码中不会调用可中断的阻塞方法,那么也可以通过在任务代码中轮询当前线程的中断状态来响应中断,只是这样中断响应性依赖于任务执行的时间。Thread.currentThread().isInterrupted()方法检查中断标志

(3)从Future的实现子类FutureTask针对cancel()方法的实现中可以看出,cancel()方法取消线程的方法是调用interrupt()方法尝试中断线程。

优化线程池线程数量

有经验公式

Nthread=Ncpu*Ucpu*(1+W/C)

W/C:等待时间与计算时间的比值

Ncpu:CPU数量

Ucpu:目标cpu的使用率

Java中下面方法获取CPU数目

int Ncpus=Runtime.getRuntime().availableProcessors();
		System.out.println(Ncpus);

猜你喜欢

转载自blog.csdn.net/wangdongli_1993/article/details/81268216
今日推荐