• 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等待(消费者)模式
  1. 获取对象的锁
  2. 检查条件是满足,不满足调用对象的wait(),进入等待,唤醒时仍要检查条件。
  3. 条件满足时,执行消费的逻辑。
    伪代码如下:
synchronized (对象) {
      while (条件不满足) {
               对象.wait();
      }
      执行消费逻辑;
}
3.4.4.2 通知(生产者)模式
  1. 获取对象的锁
  2. 改变条件
  3. 通知所有等待在对象上的线程

伪代码如下:

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服务器