Thinking In Java 21.2 基本的线程进制 学习摘要

  对笔者近期刚阅读完的Thinking In Java的12.2节简单作个概述:java

本节的主要内容:编程

  1. 为何要引入多线程
  2. 多线程实现的机制:抢占式的轮转调度机制(如何避免抢占能够参考使用协做线程)
  3. 多线程的基本两种实现方式:实现 Runnable 接口、Callable 接口以及继承 Thread 类,而且各自实现的优点
  4. 多线程的安全使用方式:引入了相似线程池的管理机制--Executor(线程执行器),简要介绍了 CachedThreadPool、SingleThreadPool 以及 FixedThreadPool 三种基本的线程执行器
  5. 讲述了 Runnable 接口(无返回值的任务描述)以及 Callable接口(有返回值的任务描述)
  6. Thread 对象的 基本操做方式: Thread t => t.yield() t.start() t.sleep() t.join() t.wait() t.interrupt( join, waitinterrupt笔者没有书写特定的示例,在下方的博客资料中有详细的更优秀的博客作了相关方面的描述)
  7. 如何处理 run() 方法中逃逸的异常(在Main线程中没法try/catch到的)

如下是笔者的笔记,对于Java中的进程的基本概念有不少知识还须要参考操做系统(如:线程的三种基本态:执行态、等待态、执行态以及补充的挂起态和激活态)。大部分笔者本身的理解都放在了代码中,若是有疏漏的还期望dalao们私聊指点下笔者(小萌新一只)!缓存

package cnboy.henu.xb;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/**
 * Thinking In Java 21.2 基本的线程进制  学习的样例代码
 * @author Administrator
 *
 * 最重要要理清概念 “线程”与“任务”,任务须要线程的驱动,Runnable以及Callable接口所作的事情是描述一个任务,而Thread则是开启一个线程,附着在任务上,驱动任务的执行
 */
public class ExcutorTry {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
//		// 执行器对象 提供中间层管理任务对象(线程)		
//		/**
//		 * 下面是使用的SingleTheadPool作的示例
//		 */
//		ExecutorService exec1 = Executors.newSingleThreadExecutor();
//		// 此对象缓存的线程对象仅只有一个,所以其它须要执行的任务会自动排队等待执行
//		for(int i=0;i<10;i++) {
//			exec1.execute(new TaskWithoutResult(i));
//		}
//		exec1.shutdown();
		
//		/**
//		 * 下面是利用的 CachedThreadPool 作的示例
//		 */
//		ExecutorService exec = Executors.newCachedThreadPool();
//		// Future对象正如名字所示,能够表明一个异步任务返回的对象并提供获取其值的方式
//		List<Future<String>>  results = new ArrayList<Future<String>>();
//		for(int i=0;i<10;i++) {
//			results.add(exec.submit(new TaskWithResult(i)));
//		}
//		// 关闭执行器,不让新的任务添加进来
//		exec.shutdown();
//		for(Future<String> fs:results) {
//			try {
//				// 判断fs是否已经执行完毕,也能够不加,不加的状况下会自动阻塞等待其执行完毕
//				if(fs.isDone()) {
//					System.out.println(fs.get());
//				}
//			}catch(InterruptedException e){
//				e.printStackTrace();
//			} catch (ExecutionException e) {
//				// TODO Auto-generated catch block
//				e.printStackTrace();
//			}
//		}
		
//		// 这里执行描述的任务的Runner其实是Main线程而不是launch对象
		LiffOff launch = new LiffOff(10);launch.run();
		Thread.yield();
		
//		// 这里执行描述的任务对象是线程 t ,此处才是真正的多线程并发执行(main 线程以及 t 线程)
//		Thread t = new Thread(new LiffOff(10));
//		t.start();System.out.println("Waiting for Liff Out!");
		
//		// 这里模仿了6个线程并发的执行状况(main + 五个new Thread)
//		for(int i=0;i<5;i++) {
//			new Thread(new LiffOff(10)).start();
//		}
		
		/**
		 * Daemon线程 (守护线程,定义为DaemonThread意味着此线程不属于程序中不可或缺的一部分,常见的 GC线程是Daemon线程)
		 * Daemon进程fork出来的子线程也会被自动设置为Daemon线程
		 */
//		// 将线程执行器设置为后台线程执行器(即建立执行任务的线程都是后台线程),采用构造注入的方式将后台线程Factory注入执行器中
//		ExecutorService daemonExec = Executors.newCachedThreadPool(new DaemonThreadFactory());
//		for(int i =0;i<5;i++) {
//			// 后台执行
//			daemonExec.execute(new TaskWithoutResult(i));
//		}
//		daemonExec.shutdown();
//		System.out.println("All Daemon Thread Started!");
//		try {
//			// 等待一下Daemon 线程的执行(有趣的是能够调节等待时间观察Daemon线程执行与非后台线程执行的关系,即非后台线程执行完毕,Daemon线程也会同时退出)
//			//TimeUnit.SECONDS.sleep(10);// 后台线程执行完毕
//			TimeUnit.MICROSECONDS.sleep(1000);// 后台线程来不及执行完毕
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
		
		/**
		 * 使用继承Thread类的方式实现多线程,缺点在于没法再继承其余类,而实现Runnable接口任然能够继承其余类
		 */
//		for(int i=0;i<5;i++) {
//			new SimpleThread();
//		}		
		
		/**
		 * 一般咱们在run()里出现的异常(从线程中逃逸,向父线程传播)若是不加以特殊处理(如使用UncaughtExceptionHandle),而只是简单的在Main里经过try/catch是没办法捕获并处理异常的
		 * 所以咱们须要引入UncaughtExceptionHandle为咱们的线程加上一道安全防线(使用在ThreadFactory、Executor)
		 * 
		 */
//		ExecutorService exceptionExec = 
//				Executors.newCachedThreadPool(new HandleThreadFactory());
//		exceptionExec.execute(new ExceptionThread());
//		exceptionExec.shutdown();
		
	}
}
/**
 * 不带返回值的任务
 * @author Administrator
 *
 */
class TaskWithoutResult implements Runnable{
	private int id;	
	public TaskWithoutResult(int id) {
		// TODO Auto-generated constructor stub
		this.id = id;
		System.out.println("Generate Runnable Task : id = "+id);
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println(String.format("Task Without Result => id = %d", id));
	}
	
}

/**
 * 带返回值的任务  实现的Callable接口
 * @author Administrator
 *
 */
class TaskWithResult implements Callable<String>{

	private int id=0;
	
	public TaskWithResult(int id) {
		// TODO Auto-generated constructor stub
		this.id = id;
	}
	
	/**
	 * 注意不是Run方法
	 */
	@Override
	public String call() throws Exception {
		// TODO Auto-generated method stub
		// 模仿下有任务的线程		
		return String.format("ask With Result => id = %d", id);
	}
	
}

/**
 * 这个任务描述是模拟的火箭发射的例子
 * @author Administrator
 *
 */
class LiffOff implements Runnable{

	protected int countDown =10;
	private static int taskCount = 0;
	private final int id=taskCount++;
	public LiffOff(int countDown) {
		// TODO Auto-generated constructor stub
		this.countDown = countDown;
	}
	
	public String Status() {
		return String.format("# %d (", id)+(countDown>0?countDown:"Liff Off!")+")";
	}
	
	@Override
	public void run() {
		// 模拟火箭发射倒计时
		while(countDown-- >0) {
			System.out.println(Status());
			// 让步 告诉线程调度器此线程核心工做已完成,能够进行上下文切换了
			/**
			 * 与sleep不一样的是,sleep是让线程进入阻塞态,这时不管比线程高优先级仍是低优先级的线程均可以被执行
			 * 而yeild仅仅只是让出此时间片,进入可执行态也就是它并不会等待一次完整的轮转后再之执行,而是有可能随时又进入执行态(可能刚yeild完毕又被调用了),所以它只能让步与它同优先级或者是高优先级的线程
			 */
			//Thread.yield();
			/**
			 * 换成sleep,能够看到每一个线程都会按照时间轮转进行输出,而不是抢占的输出(会按照顺序输出)
			 */
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
}

/**
 * 专门用于建立后台进程的ThreadFactory
 * @author Administrator
 *
 */
class DaemonThreadFactory implements ThreadFactory{

	@Override
	public Thread newThread(Runnable r) {
		// TODO Auto-generated method stub
		Thread t = new Thread(r);
		// 这里设置线程为后台线程
		t.setDaemon(true);
		return t;
	}
	
}

/**
 * 模拟一种实现线程的方式:继承自Thread类
 * @author Administrator
 *
 */
class SimpleThread extends Thread{
	protected int countDown =5;
	private static int taskCount = 0;
	public SimpleThread() {
		// TODO Auto-generated constructor stub
		// 经过调用父类的构造函数,给线程名字赋值
		super(Integer.toString(taskCount++));
		// 在构造函数中便启动线程  -- 不推荐的方式,仅为了方便作示例
		start();
	}
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "#"+getName()+"("+countDown+")";
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			System.out.println(this);
			if(countDown-- ==0) {
				return;
			}
		}
	}
}

/**
 * 模拟一个在run()方法中抛出异常的Runnable对象
 * @author Administrator
 *
 */
class ExceptionThread implements Runnable{
	@Override
	public void run() {
		// TODO Auto-generated method stub
		// 模拟执行过程当中抛出异常
		Thread t = Thread.currentThread();
		System.out.println("run() by "+ t);
		System.out.println("eh = "+t.getUncaughtExceptionHandler());
		throw new RuntimeException();
	}
	
}

/**
 * 模拟一个用以处理逃逸的异常的UncaughtExceptionHandler对象
 * @author Administrator
 *
 */
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
	/**
	 * uncaughtException方法会在线程因未捕获的异常而面临死亡的时候被调用
	 */
	@Override
	public void uncaughtException(Thread t, Throwable e) {
		// TODO Auto-generated method stub
		System.out.println("caught :"+e);
	}
	
}

/**
 * 模拟的一个为线程设置异常处理的ThreadFactory
 * @author Administrator
 *
 */
class HandleThreadFactory implements ThreadFactory{
	@Override
	public Thread newThread(Runnable r) {
		// TODO Auto-generated method stub
		// 记录建立线程的过程
		System.out.println(this+"\tcreating new Thread");
		Thread t = new Thread(r);
		System.out.println("created \t" + t);
		// 给与线程UncaughtException处理器
		t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
		System.out.println("eh = "+t.getUncaughtExceptionHandler());
		return t;
	}
	
}

附:学习中借鉴的几篇优秀的博客安全

Java多线程之interrupt()方法与sleep(),join(),wait()的关系多线程

Java多线程编程:Callable、Future和FutureTask浅析(多线程编程之四)并发

相关文章
相关标签/搜索