Java并发(1)
线程和进程的区别
每个进程拥有自己的一整套变量,而线程共享变量。
在有些操作系统中,线程更轻量级,创建,撤销一个线程比启动新进程的开销要小得多
启动线程
应该讲要运行的任务与运行机制解耦
Runnable r = new Runnable() {
@Override
public void run() {
}
};
final Thread t = new Thread(r);
t.start();
interrupt方法请求终止线程
interrupt()方法用来请求终止线程,调用interrupt()时,中断状态(boolean类型)将被置位,每个线程应该不时检查这个状态。
while (!Thread.currentThread().isInterrupted()) { //do more work }
如果线程被阻塞,调用interrupt()会抛出InterruptedException异常。如果已经调用了interrupt(),线程调用sleep()会清除中断状态位并抛出InterruptedException异常。
线程状态
new,用new操作符新建一个线程,线程还没开始运行,处于new状态
runnable,调用start(),线程处于runnable状态。可能正在运行,也可能没有运行,取决于操作系统给线程提供运行时间。
blocked,一个线程试图获取一个内部的对象锁,该锁被其他线程所持有,该线程进入阻塞状态。
waiting,线程等待另一个线程通知调度器一个条件时,进入等待状态。如调用Object.wait(),Condition.await()等
timed waiting,加超时参数,进入timed waiting状态,这一状态保持到超时期满或接受到适当的通知。
terminated,线程终止。终止的两种情况:run()正常退出自然死亡。因一个未捕获异常而意外死亡。
线程优先级
每个线程都有优先级,setPriority()设置,范围为1到10,默认为5。
每当线程调度器选择新线程时,会选择有较高优先级的线程。当虚拟机依赖于宿主机的线程实现机制时,Java线程优先级被映射到宿主机的线程优先级上,优先级个数也许更多,也许更少。
守护线程
Thread.setDaemon(true)将线程转为守护线程。守护线程的唯一用途是为其他线程提供服务,当只剩下守护线程时,虚拟机退出。
守护线程随时会发生中断。
竞争条件(race condition)
两个线程同时去更新一个对象,更新的过程不是原子操作,导致数据出现了讹误。
锁的种类
对象的内部锁。使用关键字synchronized,修饰方法,代码块,使用对象的内部锁,改锁只有一个条件。
ReentrantLock。java.util.concurrent包下的类。
ReentrantLock和条件对象
代码基本结构如下:
lock.lock(); try { //do something } finally { lock.unlock(); }
ReentrantLock是可重入的,线程可以重复获得已持有的锁,锁保持一个持有计数(hold count)跟踪lock()和unlock()的嵌套调用。
条件对象,来管理那些已经获得了一个锁但是却不能做有效工作的线程。一个锁对象可以有一个或多个条件对象,通过newCondition()获得。
Condition myCondition = lock.newCondition();
myCondition.await();
await()使得线程进入waiting状态,并放弃了锁,调用await()的线程进入该条件对象的等待集。当锁可用时,线程不能马上解除阻塞,直到另一个线程调用同一个条件对象的signal(),signalAll()才行能。
signal(),signalAll()不会立即激活一个线程,仅仅是解除等待线程的阻塞,以便这些线程可以在当前线程退出同步方法后,通过竞争实现对对象的访问。
锁和条件
锁用来保护代码片段,任何时刻只能有一个线程执行被保护的代码片段
锁可以管理试图进入被保护代码段的线程
锁可以有一个或多个条件对象
条件对象管理那些已经进入被保护的代码段却不能执行的线程
volatile关键字
volatile关键字为实例域的同步访问提供一个免锁机制,那么编译器和虚拟机知道该字段可能被另一个线程并发更新。
final关键字
final保证其他线程在构造函数完成构造之后才能看到这个变量,而不是null。
导致死锁的情况
线程局部变量
ThreadLocal为各个线程提供各自的实例
final ThreadLocal<SimpleDateFormat> threadLocal = ThreadLocal.withInitial(() -> new SimpleDateFormat()); threadLocal.get().format(new Date());