- 实现Runnable 接口的类就是任务类(任务类不一定是实现Runnable接口的类)。
- 实现Runnable 接口,重写run()方法,run方法的返回值只能是 void
- 任务类就是表示这个类要做什么,run是任务执行的入口,可以在run中完成任务也可以run调用其他方法完成任务。
- run 方法不会产生一个线程,必须把任务附着到线程上,线程才会执行任务。
- 有时run中通常存在某种形式的循环,一直等待着接收信息准备执行任务,多见于服务器。
2.Thread类
- 构造器接收一个 Runnable 对象,当线程启动时会调用Runnable对象的run方法。
- Thread(Runnable target) 或者 传入一个string 作为线程的名字 Thread(Runnable target,String name)
- 调用Thread对象的start( )方法启动线程,它会自动调用Runnable的run( )方法。
- start方法调用之后会迅速返回,执行main线程后面的代码,任务在启动的线程中继续执行。
- Thread对象和其他对象不同,Thread对象启动线程后,线程任务未完成之前,即run 方法未退出之前,垃圾回收器不会回收它。
- 常用的一个方法currentThread() 返回当前线程对象的引用。
3.Executor
- ExecutorService 线程池,该接口实现了 Executor 他知道如何根据恰当的上下文来执行Runnable对象,即根据线程环境,类型有顺序有条件的启动。
- 线程的创建代价非常高昂,最好是提前创建几个线程然后一直持有它,当执行完任务后不销毁而是重新放回线程池内等待执行下一个任务。
- 调用execute方法可以往线程池中添加任务,这些任务由线程池中的线程去执行。
- 调用 shutdown 之后可以阻止线程池再加入线程。
- Executors 是一个工具类,他可以产生ExecutorService对象和一些线程相关的其他对象。
- newCachedThreadPool() 创建一个线程池
- newFixedThreadPool(int i) 也可以创建一个固定线程数的程序池
-
newSingleThreadExecutor() 创建一个没有限制的队列池,放入的任务会排队执行,执行完一个任务才开始执行下一个任务。
- 线程池中的线程在可能情况下都会被复用,有个线程执行完一个任务会接着执行另一个任务。
4.从任务中产生返回值
- Runnable 是执行工作的独立任务,他不能有任何返回值。
- 如果想要任务完成时可以返回一个值,那么创建任务类时不用继承Runnable 接口 而是实现 Callable 接口。
- Callable<V> 和 Runnable 接口使用方法一样 V call()执行任务 并且返回一个V类型的值
- ExecutorService 的 submit() 会返回一个Future 对象。
<T> Future<T> submit(Callable<T> task) 返回Callable任务执行结果的状态 Future<?> submit(Runnable task) 返回Runnable任务执行的状态 <T> Future<T> submit(Runnable task, T result) 返回Runnable任务执行的状态和 result 值
- Future(接口)对象可以对Runnable和Callable任务进行取消、查询结果是否完成、获取结果。
- Future<V> 接口提供了一些方法用来对结果进行检测
boolean cancel(boolean mayInterruptIfRunning) 尝试取消执行此任务。 V get() 等待计算完成,然后检索其结果。 V get(long timeout, TimeUnit unit) 如果在规定时间内得到结果就立刻返回,超时抛出TimeoutException boolean isCancelled() 如果此任务在正常完成之前被取消,则返回 true 。 boolean isDone() 如果任务已完成返回true。 完成可能是由于正常终止,异常或取消
调用get 之前最好 isDone判断一下,否则get将一直阻塞直到得到结果,或者使用超时get
ExecutorService pool = Executors.newFixedThreadPool(1); Future<String> submit = pool.submit(new Callable<String>() { @Override public String call() throws InterruptedException { Thread.sleep(5000); return "时间到"; } }); pool.shutdown(); System.out.println(new Date()); System.out.println(submit.get()); System.out.println(new Date()); Wed Aug 22 17:31:02 CST 2018 时间到 Wed Aug 22 17:31:07 CST 2018
- FutureTask 类 构造器FutureTask(Callable<V> callable)
FutureTask(Runnable runnable, V result) //Runnable 自身不返回结果,但可以设定返回一个V型值- FutureTask实现了 RunnableTask接口,RunnableTask接口又实现了Runnable,Future接口
- FutureTask 也是 Future接口的唯一实现
- 因为FutureTask 实现了Runnable接口 所以Thread可以通过启动FutureTask 启动Callable任务
FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() { @Override public String call() throws InterruptedException { Thread.sleep(5000); return "时间到"; } }); Thread thread = new Thread(futureTask); thread.start(); System.out.println(new Date()); System.out.println(futureTask.get()); System.out.println(new Date());
5.休眠
- 终止任务一段时间
- 可以使用TimeUtil.MILLISECONDS.sleep(1000); 休眠1000毫秒,也可以使用其他时间单位,或者使用Thread.sleep(1000),单位只能是毫秒
- 对sleep调用要抛出InterruptedException异常并且在run,call 方法中捕获,异常不能跨线程被捕获。
6.优先级
- 线程的优先级将线程的重要性传递给了调度器,调度器
- CPU 处理线程集的顺序不一定,但调度器倾向于让优先级高的线程执行。倾向于并不是一定。
- 优先权不会导致死锁,优先权低的只是执行频率较低,并不是不执行。
- 绝大多时间里所有线程应该使用默认优先级,试图操纵线程的优先级让其先执行往往的不到效果,CPU不一定按优先级执行。
- Thread.currentThread().setPriority(int i) i 可以是1-10,未设置默认5,一般可以设置MAX_PRIORITY 最大10,或者MIN_PRIORITY最小1,NORM_PRIORITY中间5。
- 设置优先级一般在run 任务中第一句设置。
- Thread.yield() 建议具有相同优先级的其他线程执行,这只是一种建议,无法确保一定执行。
7.后台线程
- daemon, 程序进程在运行的时候在后台提供一种通用服务的线程,并且不属于不可获取,程序可以没有后台线程,有时候需要后台线程提供一些服务。
- 一个程序(进程)在运行的时候如果所有非后台线程结束,该程序也就终止了,同时会杀死所有后台线程,不管后台线程是否执行完毕。
- 程序只要有一个非后台线程没有结束,程序就不会结束,main线程就是一个非后线程。
- 在一个线程调用start()之前调用 setDaemon(true) 可以设置该线程为后台线程。
- isDaemon() 判断一个线程是否为后台线程.
- 后台线程中创建的任线程都自动设置为后台线程。
- 所有非后台线程结束后,后台线程立刻被杀死即使有finally语句也不会执行。
8.ThreadFactory
- 该接口可以设置线程的属性,仅有一个方法Thread newThread(Runnable r)
- 实现ThreadFacory接口可以定制有由Executors创建的 线程池中的所有线程的属性。
- 由Executors创建线程池的静态方法都被重载为一个可以接收ThreadFactory对象
ExecutorService service = Executors.newCachedThreadPool(new DaemonThread(),); service.submit(new Prints()); class DaemonThread implements ThreadFactory{ @Override public Thread newThread(Runnable r) { Thread t = new Thread(r); t.setDaemon(true); return t; } }
9.代码的变体
- 一般都是任务类实现Runable接口,再传递给线程Thread启动,有时也可以用别的方法来替换这种方式。
- 任务类不一定要从Runnable或者Callable接口实现,只要有run 或者call方法 并且线程能够执行这两个方法中,他们就统称任务类。
- 任务类直接从Thread继承:任务类直接继承Thread,在构造器中调用start(),当创建任务类对象时就启动了线程并执行任务
lass SimpleThread extends Thread{ SimpleThread(){ start(); } @Override public void run() { System.out.println("继承Thread的任务类"); } }
- 任务类实现Runnable,内置Thread对象并传入任务类自己的引用this,并在构造器中启动start()
class SelfManaged implements Runnable{ SelfManaged(){ thread.start(); } Thread thread = new Thread(this); @Override public void run() { System.out.println("内置Thread的任务类"); } }
- 注意1、2都是在构造器中启动线程,这意味着这个任务对象还未完全稳定线程就启动了,有可能另一个线程会访问这个不稳定的对象,这样就存在隐患。显示创建Thread就会存在这种问题,这也是优先使用Executor的原因。
- 可以通过内部类或者匿名内部类将线程代码隐藏在类中或者方法中。
10.加入一个线程
- A线程调用B线程的join()方法,则A线程会被挂起直到B线程执行被中断或者正常结束再继续执行A。
View Code
class A extends Thread{ Thread b; A(Thread b){ this.b = b; start(); } @Override public void run() { System.out.println("A线程启动"); System.out.println("B线程加入"); try { b.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("A线程结束"); } } class B extends Thread{ B(){ start(); } @Override public void run() { System.out.println("B线程执行"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("B结束执行"); } } B b = new B(); A a = new A(b);