Java语言内置多线程支持:
- 一个Java程序实际上是一个JVM进程
- JVM用一个主线程来执行main()方法
- 在main()方法中又可以启动多个线程
多线程特点:
- 多线程需要读写共享数据
- 多线程经常需要同步
- 多线程编程的复杂度高,调试更困难
Java多线程编程特点:
- 多线程模型是Java程序最基本的并发模型
- 网络、数据库、Web等都依赖多线程模型
Java多线程
- 直接调用run()方法是无效的,需要调用start()方法启动新线程
线程的优先级:
- 可以对线程设定优先级
- Thread.setPriority(int n) // 1~10,默认值5
- 优先级高的线程被操作系统调度的优先级高
- 不能通过设置优先级来确保功能的执行顺序 不建议设置优先级
- Java用Thread对象表示一个线程,通过调用start()启动一个线程
- 一个线程对象只能调用一次start()
- 线程的执行代码是run()方法
- 线程调度由操作系统决定,程序本身无法决定
- Thread.sleep()可以把当前线程暂停一段时间
线程的状态:
- New(新建)
- Runnable(运行中)
- Blocked(被阻塞)
- Waiting(等待)
- Timed Waiting(计时等待)
- Terminated(已终止)
线程终止的原因:
- run()方法执行到return语句返回(线程正常终止)
- 因为未捕获的异常导致线程终止(线程意外终止)
- 对某个线程的Thread实例调用stop()方法强制终止(不推荐)
- 可以通过对另一个线程对象调用join()方法可以等待其执行结束
- 可以指定等待时间,超过等待时间线程仍然没有结束就不再等待
- 对已经运行结束的线程调用join()方法会立刻返回
- 调用interrupt()中断线程
- 线程间共享变量需要使用volatile关键字标记,确保线程能读取到更新后的变量值
- volatile关键字的目的是告诉虚拟机:
- 每次访问变量时,总是获取内存的最新值
- 每次修改变量后,立刻回写到主内存
- volatile关键字解决的是可见性问题:
- 当一个线程修改了某个共享变量的值,其他线程能够立刻看到修改后的值
- 通过检测isInterrupted()标志获取当前线程是否中断
- 如果线程处于等待状态,该线程会捕获InterruptedException
- isInterrupted()为true或者捕获了InterruptedException都应该立刻结束
- 通过标志位判断需要使用volatile关键字
- volatile关键字解决了共享变量在线程间的可见性问题
守护线程特点:
- 守护线程不能持有资源(如打开文件等)
- 创建守护线程
- setDaemon(true)
- 守护线程是为其他线程服务的线程
- 所有非守护线程都执行完毕后,虚拟机退出
线程同步:
- 对共享变量进行写入时,必须保证是原子操作
- 原子操作是指不能被中断的一个或一系列操作
Synchronized保证了代码块在任意时刻最多只有一个线程能执行
Synchronized问题:
- 无法并发执行,性能下降
如何使用?
- 找出修改共享变量的线程代码块
- 选择一个实例作为锁
- 使用synchronized(lockObject){...}
- 同步的本质就是给制定对象加锁
- 加锁对象必须是同一个实例
- 对JVM定义的单个原子操作不需要同步
数据封装:把同步逻辑封装到持有数据的实例中
synchronized两种写法
//同步方法
public synchronized void add(int n) {
count += n;
total += n;
}
//同步代码块
public void add(int n) {
synchronized(this) {
count += n;
total += n;
}
}
//静态方法锁住的是Class实例
public class A {
static count;
static synchronized void add(int n) {
count += n;
}
}
public class A {
static count;
static void add(int n) {
synchronized(A.class) {
count +=n;
}
}
}
如果一个类被设计为允许多线程正确访问:
- 在这个类就是“线程安全”的(thread-safe) 如StringBuffer (方法全部用synchronized修饰)
死锁:
- Java的线程可以获取多个不同对象的锁(嵌套)
- 不同线程获取多个不同对象的锁可能导致死锁

死锁形成的条件:
- 两个线程各自持有不同的锁
- 两个线程各自试图获取对方已持有的锁
- 双方无线等待下去:导致死锁
死锁发生后
- 没有任何机制能解除死锁
- 只能强制结束JVM进程
如何避免死锁:
- 多线程获取锁的顺序要一致
synchronized解决了多线程竞争的问题,但没有解决多线程协调的问题
多线程协调运行:当条件不满足时,线程进入等待状态
线程协调机制:wait/notify
- wait释放锁

wait/notify用于多线程协调运行:
- 在synchronized内部可以调用wait()使线程进入等待状态
- 必须在已获得的锁对象上调用wait()方法
- 在synchronized内部可以调用notify/notifyAll()唤醒其他等待线程
- 必须在已获得的锁对象上调用notify/notifyAll()方法
Thread对象代表一个线程:
- 调用Thread.currentThread()获取当前线程
JDK提供了ThreadLocal,在一个线程中传递同一个对象

ThreadLocal一定要在finally中清除
- ThreadLocal表示线程的“局部变量”,它确保每个线程的ThreadLocal变量都是各自独立的
- ThreadLocal适合在一个线程的处理流程中保持上下文(避免了同一参数在所有方法中传递)
- 使用ThreadLocal要用try .. finally结构