编程须知
接口
Runnable
纯粹的运行方法,无返回值的运行方法,无返回值,不能抛异常
Runnable.java 1 2 3 4 @FunctionalInterface public interface Runnable { public abstract void run () ; }
Callable
可以返回值,可以抛异常
Callable.java 1 2 3 4 @FunctionalInterface public interface Callable <V > { V call () throws Exception ; }
Executors类提供工具方法,把Runnable转成Callable,实际上就是通过适配器增加了返回值逻辑
Runnable本身没有返回值,可以通过外部指定一个
Executors.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static Callable<Object> callable (Runnable task) { if (task == null ) throw new NullPointerException(); return new RunnableAdapter<Object>(task, null ); } public static <T> Callable<T> callable (Runnable task, T result) { if (task == null ) throw new NullPointerException(); return new RunnableAdapter<T>(task, result); } static final class RunnableAdapter <T > implements Callable <T > { final Runnable task; final T result; RunnableAdapter(Runnable task, T result) { this .task = task; this .result = result; } public T call () { task.run(); return result; } }
Future
代表了异步计算结果,使用get方法获取结果
提供了一些操作,可以配置最长等待时间,可以取消计算任务
Future.java 1 2 3 4 5 6 7 8 9 10 11 12 public interface Future <V > { boolean cancel (boolean mayInterruptIfRunning) ; boolean isCancelled () ; boolean isDone () ; V get () throws InterruptedException, ExecutionException ; V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException ; }
Future作为一种操作扩展,可以分别应用到Runnable和Callable上
RunnableFuture
同时结合Runnable和Future特性
RunnableFuture.java 1 2 3 public interface RunnableFuture <V > extends Runnable , Future <V > { void run () ; }
FutureTask
FutureTask实现了异步结果返回的逻辑,底层是Callable
可以看到开始执行run方法,内部使用CAS操作将当前线程保存到runner,后续可以用于取消操作
执行get方法时,没执行完会进行等待
FutureTask.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class FutureTask <V > implements RunnableFuture <V > { private Callable<V> callable; private volatile Thread runner; private volatile int state; public FutureTask (Callable<V> callable) { if (callable == null ) throw new NullPointerException(); this .callable = callable; this .state = NEW; } public void run () { if (state != NEW || !UNSAFE.compareAndSwapObject(this , runnerOffset, null , Thread.currentThread())) return ; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true ; } catch (Throwable ex) { result = null ; ran = false ; setException(ex); } if (ran) set(result); } } finally { runner = null ; int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } } public V get () throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false , 0L ); return report(s); } }
FutureTask本身就是具备异步返回的Runnable,最终的载体依然是Thread,使用时用Thread包装,或者提交给线程池
载体
上述各种形式实质上都是Runnable,最终的载体都是线程
Thread本身就是个Runnable,所以也有run方法
Thread还可以作为其他Runnable的载体,构造时传入
Runnable本身没有名字,线程可以绑定名字
Thread.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Thread implements Runnable { private Runnable target; public Thread (Runnable target) { init(null , target, "Thread-" + nextThreadNum(), 0 ); } @Override public void run () { if (target != null ) { target.run(); } } }
注意:
虽然Thread有run方法,但是绝不应该直接调用,如果直接调用,那么跟线程没关系,run只是一个普通的方法,正确的方式是通过start,JVM会负责启动线程并调用run方法
可以直接继承并重写Thread的run方法,但是会造成逻辑和线程本身混合,比如线程本身有一些方法,会造成不必要的方法遮蔽
Runnable剥离了线程逻辑,Thread作为载体,同一逻辑可以运行在多个Thread中,适合共享资源实现。在Runnable中如果需要访问Thread,可以使用Thread.currentThread()
另外Runnable作为接口,可以多实现,不占用单继承机会,代价远比继承小,更加灵活
线程组
线程可以加入线程组进行统一管理,构造时进行指定
Thread.java 1 2 3 public Thread (ThreadGroup group, Runnable target, String name, long stackSize) { init(group, target, name, stackSize); }
线程组底层就是线程的数组,可以进行统一管理,比如获取活跃线程数量activeCount,进行批量中断interrupt
线程组除了包含线程外,还可以继续划分子线程组,嵌套结构
异常处理
线程中的RuntimeException异常无法被父线程捕获,需要自定义异常处理器,注册给线程
全局设置Thread.setDefaultUncaughtExceptionHandler
单一线程设置thread.setUncaughtExceptionHandler
1 2 3 public interface UncaughtExceptionHandler { void uncaughtException (Thread t, Throwable e) ; }
如果有返回值场景下,可以通过Future类,get方法会报出ExecutionException包装内部异常
优先级
默认级别5,最高10,最低1。不保证有用,有些操作系统会无视级别。
Thread.java 1 2 3 public final static int MIN_PRIORITY = 1 ;public final static int NORM_PRIORITY = 5 ;public final static int MAX_PRIORITY = 10 ;
守护线程
JVM中只有守护线程时,才会退出,相当于扩展了生命周期
setDeamon只能在线程启动前设置,否则无效
Thread创建
自己创建出的Thread不是凭空出现的,而是由当前线程创建新线程,存在一个父子关系
如果没有显式指定新配置,新线程会继承父线程相关配置
Thread.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public Thread () { init(null , null , "Thread-" + nextThreadNum(), 0 ); } private void init (ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) { if (name == null ) { throw new NullPointerException("name cannot be null" ); } this .name = name; Thread parent = currentThread(); if (g == null ) { if (g == null ) { g = parent.getThreadGroup(); } } g.addUnstarted(); this .group = g; this .daemon = parent.isDaemon(); this .priority = parent.getPriority(); if (security == null || isCCLOverridden(parent.getClass())) this .contextClassLoader = parent.getContextClassLoader(); else this .contextClassLoader = parent.contextClassLoader; this .inheritedAccessControlContext = acc != null ? acc : AccessController.getContext(); this .target = target; setPriority(priority); if (parent.inheritableThreadLocals != null ) this .inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); this .stackSize = stackSize; tid = nextThreadID(); }
线程中止
线程类中包含废弃方法
thread.stop()
强制停止执行,抛出Error中的ThreadDeath
粗暴停止不安全,会释放所有锁,如果线程没有正常做完会导致被锁保护的对象处于错误状态,进而暴露给其他等待的线程
如果ThreadDeath被catch,一定要重新抛出,因为这是给JVM的,拦截会导致线程没法真正结束
thread.destroy()
本来预留,但是考虑到安全性,从没实现。。
线程优雅的停止,发出通知让线程感知而非强制中止
利用volatile设置标志位
无等待的逻辑可以用volatile标志
while外围检测作为停止,但是如果线程在等待,轮不到外围检测
1 2 3 4 5 6 public static volatile boolean stop = false ;public void run () { while (!stop) { } }
1 2 3 4 5 public void run () { while (!Thread.currentThread.isInterrupted()) { } }