线程的4个状态:
创建(new),就绪(runnable),阻塞(blocked),终止(dead)
Runnable接口:
定义了一个描述任务的方式,他具有任何内在的线程能力,使用它必须将它显示的依附在线程上。
传统的方法是将其传递给一个Thread的构造方法。
Thread对象:
当主线程创建Thread对象后,并没有创建其对象的引用,在start()之后每个Thread都会注册自己,并创建自己一个单独的执行线程,在其执行完run()方法并死亡前是无法回收该对象的。
Exector:
线程池可以为我们管理Thread对象,相当于在客户端与执行任务之间的一个间接层。
ExcetorService由Excetor的静态方法创建。
newCachedThreadpool()会在程序中创建与任务数相同的线程, 然后在回收旧线程时才会停止创建新线程。当所需要的线程的数量过多影响到性能时应选用newFixedThreadpool();
newFixedThreadpool()可以指定线程数量的上限。
newSingleThreadExector()数量为1的FixedThreadpool,所有任务以队列的形式一个个顺序运行。
shutdown()方法防止其他任务提交给该Excetor.
Callable接口与Future:
是带有返回值的Runnable接口,内部声明的方法是call().一般情况下配合ExctoeService使用。Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。Future由ExecutorService.sumbit返回。
Future提供了三种功能:
1)判断任务是否完成;
2)能够中断任务;
3)能够获取任务执行结果。
因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此有FutureTask。
FutureTask:
publicclassFutureTaskimplementsRunnableFuture
FutureTask类实现了RunnableFuture接口,我们看一下RunnableFuture接口的实现:
publicinterfaceRunnableFutureextendsRunnable, Future {
voidrun();
}
可以看出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
FutureTask提供了2个构造器:
publicFutureTask(Callable callable) {
}
publicFutureTask(Runnable runnable, V result) {
}
事实上,FutureTask是Future接口的一个唯一实现类。
后台线程:
调用setDaemon方法可以将线程设置成后台线程,但是必须在线程start后才能起作用。当程序终止时就会杀死全部后台线程。后台线程派生出的子线程全部默认为后台线程。
线程中捕获异常:
线程中产生的异常不能够在主线程捕获。如果需要在主线程捕获子线程产生的异常需要用到uncanughtException.
uncanughtException是一个接口。实现该接口,并调用Thread.setUncanughtExceptionHandle(..),就可以在指定的线程中捕获其他线程产生的异常。
synchronized与Lock的区别:
ReentrantLock是Lock的一个实现,他允许尝试获得锁但最终未获得锁,因为可以决定离开去做别事情。而synchronized则会等待锁。因此ReentrantLock赋予了更细粒度的控制力。
原子性:
一个操作是原子性去取决于其底层的实现,因此是与操作系统有关,应避免使用原子性来替代线程同步。
线程的终止:
当线程被阻塞时(如调用了wait/sleep等方法时),可以使用 Thread.interrupt()方法打断阻塞的状态,当调用此方法后 Thread的 interrupted 标记被设置为 true. 此时如果你调用 interrupted()方法来测试中断状态(此方法会清空中断状态,也可以使用isInterrupted()来测试中断状态,这时中断状态不会被清空).
当调用 interrupte()方法时会抛出 InterruptedException,这时中断状态被清空.
线程被 sleep/wait 阻塞时可以被 interrupt,但如果线程被 IO或synchronized 阻塞则不可以被 interrupt,
线程间的协作:
我们上面讲的主要是如何让线程互斥的访问一个共享的资源,现在要研究如何让线程间进行协作,也就说二个线程有个先后,必须第一个线程完成后,第二个线程才能开始执行(像生产者与消费者)。
要完成上面所说的就要用到 Object.wait()/notify()/notifyAll()了.
wait/notify/notifyAll 的调用都必须在获得这个对象的 monitor 时.
当wait 被调用时,当前线程被加入 wait set,这时这个线程不再被系统调度,直到有其它线程调用特定对象的notify或notifyAll方法时,此线程从wait set 中移除,开始被系统调度,但它必须再次获得特定对象的monitor才能继续。当获得对象的monitor后,wait方法会返回,这时此线程会恢复到调用wait方法前的状态.
注意: 当调用 wait()时会释放对象锁,而 sleep/yield 等方法不会释放锁.
wait/notify/notifyAll 及 synchronized 都是相对于一个对象来说的,都需要这个对象的锁.