3.Java 并发编程基础
3.1 线程简介
3.1.1 什么是线程
现代操作系统的最小调度单位。
3.1.2 为什么使用多线程
- 更多的处理核心
- 更快的响应时间
- 更好的编程模型
3.1.3 线程的优先级(没什么用)
3.1.4 线程的状态
- NEW
- RUNNABLE
- BLOCKED
- WAITING
- TIMED_WAITING
- TERMINATED
3.1.5 线程状态的变换
- NEW -> RUNNABLE
- Thread.start()
- RUNNABLE -> TERMINATED
- 执行完成
- RUNNABLE -> TIMED_WAITING
- Thread.sleep()
- Object.wait()
- Thread.join()
- LockSupport.parkNanos()
- LockSupport.parkUtil()
- RUNNABLE -> TIMED_WAITING
- Object.notify()
- Object.notifyAll()
- LockSupport.unpark(Thread)
- 超时时间到
- RUNNABLE -> BLOCKED
- 等待进入synchronized方法
- 等待进入synchronized块
- BLOCKED -> RUNNABLE
- 获取到锁
- RUNNABLE -> WAITING
- Object.join()
- Object.wait()
- LockSupport.park()
- WAITING -> RUNNABLE
- Object.notify()
- Object.notifyAll()
- LockSupport.unpark(Thread)
3.1.6 Daemon线程
主要用于程序中后台调度。
JVM中不存在非Daemon线程,将会退出。
3.2 启动和终止线程
3.2.1 构造线程
新构造的线程对象是由其parent线程来进行空间分配的。
3.2.2 启动线程
线程对象在初始化完成后,调用start()方法就可以启动。
3.2.3 中断
- 中断是线程的一个标志属性,表示一个运行中的线程是否被其他线程终端。
- 其他线程调用该线程的interrupt()方法对其进行中断。
- 线程通过isInterrupted()判断是否被中断
- Thread.interrupted() 对当前线程的中断标示位进行复位。
- 抛出InterruptedException的线程,其中断位会被清除。
3.2.4 过时的suspend,resume,stop
- suspend 不会释放已有资源,而是占有资源进入睡眠
- stop 不会释放资源
3.2.5 安全的终止线程
通过标志位或者interrupted 终止线程。
3.3 线程间通信
3.3.1 volatile 和 synchronized
volatile 修饰变量表示程序对该变量的获取必须从内存中获取,对该变量的修改必须刷回到内存中。
synchronized 修饰,保证多个线程,只有一个在同步方法块中。
3.3.2 等待 通知
线程A调用对象O的wait()进入等待状态,线程B调用对象O的notify()或者notifyAll(),线程A收到通知后,从对象O的wait()中返回,进行后续操作。
使用细节:
- 使用wait notify notifyAll 要先对对象O加锁
- 调用wait 方法后,线程从RUNNING变为WAITING,并将线程放入对象O的等待队列中
- notify notifyAll 调用之后,等待线程A在调用notify notifyAll线程B释放锁之后,A才有机会从wait 返回
- notify 将等待队列中的一个等待线程移动到同步队列,notifyAll将所有线程移动,被移动的从WAITING变为BLOCKED
- 要从wait中返回,必须持有对象的锁。
3.3.3 管道输入/输出流
管道使用内存用于线程之间的数据传输。
包括四种具体的实现:
- PipedOutputStream 面向字节
- PipedInputStream 面向字节
- PipedReader 面向字符
- PipedWriter 面向字符
使用的时候,输入输出流要绑定在一起。
3.3.4 Thread.join
线程A执行thread.join(), 意味着线程A等待线程thread终止之后,才从thread.join()返回。
public class Join {
public static void main(String[] args) throws Exception {
Thread previous = Thread.currentThread();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new Domino(previous), String.valueOf(i));
thread.start();
previous = thread;
}
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName() + "terminate");
}
static class Domino implements Runnable{
private Thread thread;
public Domino(Thread thread) {
this.thread = thread;
}
@Override
public void run() {
try {
thread.join();
} catch (InterruptedException e){
}
System.out.println(Thread.currentThread().getName() + " terminate");
}
}
}
- join(long millis)
- join(long millis, int nanos)
这两个有超时机制,超过一定时间之后,会从thread.join()返回。
原理:
public final synchronized void join() throws InterruptedException {
// 若thread 存活,等待被notify 或者notifyAll
while(isAlive()){
wait(0);
}
}
- 线程A调用thread.join()一直在等待thread的锁.
- thread 终止后,调用notifyAll()
- 线程A获得锁,执行 join()方法
3.3.5 ThreadLocal
ThreadLocal 是线程变量,k-v 结构,k为ThreadLocal对象,值为任意对象。一个线程可以根据ThreadLocal对象查询到绑定在该线程上的变量的值。
public class Profile {
private static final ThreadLocal<Long> TIME_THREADLOCAL = new ThreadLocal<Long>(){
protected Long initalValue(){
return System.currentTimeMillis();
}
};
public static final void begin(){
TIME_THREADLOCAL.set(System.currentTimeMillis());
}
public static final Long end(){
return System.currentTimeMillis() - TIME_THREADLOCAL.get();
}
public static void main(String[] args) throws Exception{
Profile.begin();
TimeUnit.SECONDS.sleep(1);
System.out.println("Cost: " + Profile.end());
}
}