- 1 线程简介
- 11 什么是线程
- 12为什么要使用线程
- 2 线程状态
- 21 线程6种状态
- 22 线程状态变迁图
- 3 线程启动和中止
- 31 创建
- 32 启动
- 33 中断
- 4 线程间通信
- 41 volatile和synchronized关键字
- 42 synchronized
- 421 原理
- 422 线程对象监视器 同步队列和同步块之间关系
- 43 等待通知机制生产者消费者模式
- 431 代码示例
- 432 原理
- 433 工作的过程
- 44 等待通知经典范式
- 441等待消费者模式
- 442 通知生产者模式
- 45 Threadjoin
- 46 ThreadLocal线程本地变量
- 5 线程技术应用
- 51 等待超时模式
- 52 简单的数据库连接池
- 53 线程池技术
- 54 基于线程池技术的简单Web服务器
3.1 线程简介
3.1.1 什么是线程?
一个java进程包含多个线程,每个线程有属于自己的计数器 ,线程栈,局部变量等。
3.1.2为什么要使用线程?
- CPU多核。
- 异步化,加快响应速度。
3.2 线程状态
3.2.1 线程6种状态
- NEW
初始化状态,线程刚被创建,还没有start。 - RUNNABLE
运行中状态,包括操作系统中的就绪和运行中状态。 - BLOCKED
阻塞状态,因竞争锁共享资源而阻塞于锁。 - WAITING
等待状态,线程等待,不竞争锁资源,需要别的线程唤醒(通知或者中断)。 - TIME_WAITING
超时等待状态,区别于等待状态,设置了超时时间,定时返回。
3.2.2 线程状态变迁图
3.3 线程启动和中止
3.3.1 创建
3.3.2 启动
Thread thread=new Thread();
thread.start();
3.3.3 中断
线程中断标识位
Thread.interrupted();
3.4 线程间通信
3.4.1 volatile和synchronized关键字
- volatile修饰的成员变量,一个线程对它的写会立马刷新同步到主存,对于后续线程的读要直接读主存,这样保证了所有线程对变量访问的可见性。
- synchronized可以修饰方法和同步代码块,它确保多个线程在同一时刻,只能有一个线程在方法或者代码块里,它保证了线程对变量的互斥性和可见性。
3.4.2 synchronized
3.4.2.1 原理
- synchronized在线程进入代码块之前设置了对象监视器。
- 每个对象都有监视器,只有获取监视器的线程才能进入同步块中,获取监视器的过程是排他的。
- 没有获取到监视器的线程在该对象的阻塞队列中阻塞等待,进入BLOCKED状态。
3.4.2.2 线程,对象监视器 ,同步队列和同步块之间关系
3.4.3 等待/通知机制(生产者消费者模式)
3.4.3.1 代码示例
- 等待(消费者)
static class Wait implements Runnable {
@Override
public void run() {
synchronized (lock) {
while (flag) {
try {
lock.wait();//线程等待,状态为WAITING,并且释放锁,等待的线程不参与竞争锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//3.等待线程接收到通知并且获取锁从wait返回,执行消费业务
doSomeThing();
}
}
- 通知(生产者)
static class Notify implements Runnable {
@Override
public void run() {
synchronized (lock) {
//1.这里生产了一条消息(flag共享变量值改了)
flag = false;
//2.通知lock上等待的线程,这里不会释放锁;
lock.notify();//notify后,WaitThread不会立马从wait方法回,还需竞争锁
try {
TimeUnit.SECONDS.sleep(1);//睡眠1s,不释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//3.这里和WaitThread同时竞争锁
synchronized (lock) {
try {
TimeUnit.SECONDS.sleep(1);//睡眠1s,不释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.4.3.2 原理
- 使用wait(),notify()和notifyAll(),要先对对象加锁。
- 使用wait() ,线程状态由RUNNABLE变为WAITING,当前线程加入到对象的等待队列里,释放锁,不参与竞争锁资源。
- notify()和notifyAll()调用方法,只是让一个或者全部等待线程从等待队列转移到同步队列中,等待线程还需竞争到锁的资源才会从wait()方法返回。
- wait()返回的前提是获取到锁。
总的来说,等待通知机制实际上还是依赖了同步机制,其目的就是确保等待线程接收到通知从wait返回时,能感知通知线程对共享变量的修改。
3.4.3.3 工作的过程
3.4.4 等待/通知经典范式
3.4.4.1等待(消费者)模式
- 获取对象的锁
- 检查条件是满足,不满足调用对象的wait(),进入等待,唤醒时仍要检查条件。
- 条件满足时,执行消费的逻辑。
伪代码如下:
synchronized (对象) {
while (条件不满足) {
对象.wait();
}
执行消费逻辑;
}
3.4.4.2 通知(生产者)模式
- 获取对象的锁
- 改变条件
- 通知所有等待在对象上的线程
伪代码如下:
synchronized (对象) {
修改条件
对象.notifyAll();
}
3.4.5 Thread.join()
3.4.6 ThreadLocal线程本地变量
3.5 线程技术应用
3.5.1 等待超时模式
3.5.2 简单的数据库连接池
3.5.3 线程池技术
3.5.4 基于线程池技术的简单Web服务器