前言
Java的多线程学习笔记
Java中,所有非守护线程(主线程和子线程全部)执行结束后,JVM才会自动退出,程序才会终止
平台线程由操作系统调度,虚拟线程由JVM调度,Java8的线程都是平台线程,本篇介绍的线程都是Java8的平台线程
创建线程对象
通过继承Thread类创建线程对象
- 定义一个类,继承
Thread类,重写run()方法,在run()方法内定义线程执行的代码
- 这种方式创建的线程对象没有返回值
1 2 3 4 5 6 7 8 9 10 11 12
| class Cls extends Thread { @Override public void run() { ... } }
class Main { public static void main(String[] args) { Thread thread = new Cls(); } }
|
通过实现Runnable接口创建线程对象
- 定义一个类,实现
Runnable接口,实现run()方法,在run()方法内定义线程执行的代码
- 这种方式创建的线程对象没有返回值
1 2 3 4 5 6 7 8 9 10 11 12
| class Cls implements Runnable { @Override public void run() { ... } }
class Main { public static void main(String[] args) { Thread thread = new Thread(new Cls()); } }
|
Lambda表达式
1 2 3 4 5 6 7
| class Main { public static void main(String[] args) { Thread thread = new Thread(() -> { ... }); } }
|
通过Callable接口创建线程对象
- 创建一个类,实现
Callable接口,实现call()方法,在call()方法内定义线程执行的代码
- 这种方式创建的线程对象有返回值
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Cls implements Callable<String> { @Override public String run() { ... } }
class Main { public static void main(String[] args) { FutureTask<String> futureTask = new FutureTask<>(new Cls()); Thread thread = new Thread(futureTask); } }
|
类方法
获取当前线程对象
1
| Thread thread = Thread.currentThread();
|
当前线程让位
当前线程延迟
<num>:毫秒值
获取线程组中存活的线程总数
- 获取当前线程所在的线程组及其子组的所有存活线程总数
1
| int count = Thread.activeCount();
|
实例方法
启动线程
异步执行
同步执行
修改线程名
获取线程名
获取线程组
1
| ThreadGroup group = thread.getThreadGroup();
|
设置线程优先级
Thread.MIN_PRIORITY:1
Thread.NORM_PRIORITY:缺省值,5
Thread.MAX_PRIORITY:10
1
| thread.setPriority(<num>);
|
指定线程加入到当前线程
设置是否为守护线程
- 守护线程会在JVM退出时自动退出
- 守护线程中创建的子线程也是守护线程
- 守护线程得到CPU时间片的概率会降低
false:缺省值,非守护线程
true:守护线程
获取线程编号
获取线程状态
线程中断
- 通过中断信号提醒线程需要终止线程了,但是实际终止行为由线程自身决定
为指定线程设置中断信号为true
- 为已经处于阻塞状态的线程设置中断信号为true时,会抛
InterruptedException异常
重置当前线程中断信号为false
1
| boolean interrupted = Thread.interrupted();
|
判断当前线程的中断信号
1
| boolean interrupted = Thread.currentThread().isInterrupted();
|
线程同步
加锁
- 锁只有一个,多个线程抢锁只有一个线程能得到锁,得到锁的线程执行逻辑,执行完逻辑释放锁,其他线程等待锁释放后继续抢锁
synchronized修饰代码块
1 2 3
| synchronized(Main.class) { ... }
|
1 2 3 4 5
| Object obj = new Object();
synchronized(obj) { ... }
|
synchronized修饰方法
所有调用这个方法的线程都需要先抢锁
包含static关键字的方法需要抢类对象锁
1 2 3
| static synchronized void method() { ... }
|
1 2 3
| synchronized void method() { ... }
|
死锁判定
命令行工具
- 通过
jps获取进程id,通过jstack查看指定进程是否死锁(Found 1 deedlock.)
GUI工具
设置线程为等待状态
- 必须在
synchronized代码块中使用wait()方法
- 通过
wait()让当前线程变为等待状态(Thread.State.WAITING),并释放锁
1 2 3 4 5
| Object obj = new Object();
synchronized(obj) { obj.wait(); }
|
设置超时时间
<timestamp>:毫秒时间戳
1 2 3 4 5
| Object obj = new Object();
synchronized(obj) { obj.wait(<timestamp>); }
|
唤醒等待状态的线程
- 必须在
synchronized代码块中使用notify()方法
- 通过
notify()方法随机唤醒一个需要抢相同锁的等待状态(Thread.State.WAITING)的线程,或者通过notifyAll()方法唤醒所有需要抢相同锁的等待状态(Thread.State.WAITING)的线程,被唤醒的线程从等待状态(Thread.State.WAITING)变为就绪状态(Thread.State.RUNNABLE),仍然需要重新抢锁
1 2 3 4 5
| Object obj = new Object();
synchronized(obj) { obj.notify(); }
|
1 2 3 4 5
| Object obj = new Object();
synchronized(obj) { obj.notifyAll(); }
|
ThreadLocal
创建对象
1
| ThreadLocal<String> threadLocal = new ThreadLocal<>();
|
设置数据
获取数据
1
| String value = threadLocal.get();
|
InherableThreadLocal
- 将数据绑定到指定线程,线程只能获取自己设置的数据
- 在父线程创建子线程时,子线程会继承父线程定义的数据作为初始值,但是子线程与父线程的数据是隔离的
创建对象
1
| InherableThreadLocal<String> inherableThreadLocal = new InherableThreadLocal<>();
|
设置数据
1
| inherableThreadLocal.set("");
|
获取数据
1
| String value = inherableThreadLocal.get();
|
线程同步
- 同步:多个线程,步调一致地执行
- 让多个线程,争夺同一个对象的“同步锁”,谁抢到谁执行,抢不到要等待
- 同步锁
- 任何对象,都有唯一的同步锁
- 遇到同步关键字(
synchronized),要先抢到锁才能执行,抢不到要等待
同步代码块
<obj>:指定对象
1 2 3
| synchronized(<obj>) { ... }
|
在方法上加锁
1 2 3
| synchronized void func() { ... }
|
静态同步代码块
1 2 3
| static synchronized void func() { ... }
|
完成