异步调用
public class Test1 {
public static void main(String[] args) {
new Thread(()->{
try {
Thread.sleep(1000); // 休眠一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"发短息----------------");
},"thread1").start();
System.out.println("主线程输出..........................");
}
}
提高效率
充分利用多核cpu的优势,提高运行效率。想象下面的场景,执行三个计算,最后将计算结果汇总。
1. 继承Thread类
实现案例:
public class Test2 {
public static void main(String[] args) {
// 创建线程对象
Thread t = new Thread() {
public void run() {
// 要执行的任务
System.out.println("执行任务。。。。");
}
};
// 启动线程
t.start();
}
}
这里继承Thread类 用的是匿名内部类的形式。(使用匿名内部类有个前提条件:必须继承一个父类或实现一个接口)
2. 使用Runnable配合Thread(常用)
把【线程】和【任务】(要执行的代码)分开
Runnable是一个接口,且只有一个实现方法,如下图源码所示:
实现案例:
public class Runnable_Thread_test {
public static void main(String[] args) {
// 1. 编写Runnable的实现类
Runnable runnable = new Runnable() {
//这里实现Runnable接口 用的是匿名内部类的形式
public void run(){
// 要执行的任务
System.out.println("执行任务-Runnable_Thread。。。。。。。");
}
};
// 2. 创建线程对象
Thread t = new Thread(runnable);
// 3. 启动线程
t.start();
}
}
Java 8 以后可以使用lambda精简代码:
// 1. 编写Runnable的实现类
Runnable runnable = ()->{
System.out.println("执行任务-Runnable_Thread 哈哈。。。。。。。");};
// 2. 创建线程对象
Thread t = new Thread(runnable);
// 3. 启动线程
t.start();
3. 使用Callable、FutureTask配合Thread(可以接收返回值)
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class FutureTask_Callable_Thread_test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1. 编写FutureTask类
FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
// 2. FutureTask对象的构造方法参数是一个 Callable接口的实现对象
@Override
public Integer call() throws Exception {
System.out.println("Callable——FutureTask执行中。。。。。");
Thread.sleep(5000); // 休眠5秒
return 100; // 这里有一个返回值
}
});
// 3. 创建Thread线程类
Thread t = new Thread(task);
// 4. 执行
t.start();
// 5. 获取线程执行后的返回值
Object result = task.get(); // 此刻主线程会一直等待线程任务结果的返回(阻塞)
System.out.println(result);
System.out.println("哈哈哈");
}
}
windows
linux
Java
因为以下一些原因导致cpu不再执行当前的线程,转而执行另一个线程的代码
当Context Switch 发生时,需要有操作系统保存当前线程的状态,并恢复另一个线程的状态,java中对应的概念就是程序计数器(Program Counter Register),它的作用是记住下一条jvm指令的执行地址,是线程私有的
start() : 启动一个新线程,在新的线程运行run方法中的代码。该启动的线程不会马上运行,start方法只是让线程进入就绪状态,会放到等待队列中等待 CPU 调度,只有线程真正被 CPU 调度时才会调用 run() 方法执行。 每个线程对象的start方法只能调用一次,否则就会出现IllegalThreadStateException
run() : 新线程启动后会调用的方法。如果在构造Thread对象时传递了Runnable参数,则线程启动后会调用Runnable中的run方法,否则默认不执行任何操作。但可以创建Thread的子类对象,来覆盖默认行为。
join(): 等待线程运行结束
join(long n): 等待线程运行结束,最多等待n毫秒
getId(): 获取线程唯一的id
getName: 获取线程名
setName(String): 修改线程名
getPriority(): 获取线程优先级
setPriority(int): 修改线程优先级
getState(): 获取线程状态
currentThread(): static方法,获取当前正在执行的线程
sleep(long n): static方法,让当前执行的线程休眠n毫秒,休眠时让出cpu的时间片给其他线程
yield(): static方法,提示线程调度器让出当前线程对CPU的使用(主要是为了测试和调试)
上代码:
public class startVsRunTest {
public static void main(String[] args) {
Thread t1 = new Thread(){
public void run(){
System.out.println(Thread.currentThread().getName()); // 输出调用当前run方法的线程
try {
Thread.sleep(5000); // 阻塞5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.run(); // 直接调用t1的run方法
System.out.println("我是主线程输出");
}
}
结论:
如果直接调用run方法,main线程会将其当做普通方法对待(还是main线程调用)
如果想另开启线程应当调用对应线程的start方法
sleep:
- 调用sleep会让当前线程从Running进入Timed Waiting(阻塞)状态
- 其他线程可以使用interrupt方法打断正在睡眠的线程,这时sleep方法会抛出InterruptedException
- 睡眠结束后的线程未必会立刻得到执行
- 建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性
yield:
- 调用yield会让当前线程从Running进入Runnable(就绪)状态,然后调度执行其他线程
join:等待其他线程运行结束
public class JoinTest {
static int r = 0;
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("开始休眠");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束休眠");
r = 10;
});
t.start();
System.out.println("最终的结果为"+r);
}
}
上图代码有没有人认为输出结果r会是10的? 哈哈 应该会有不认真的朋友这样认为吧
上图看结果:
可以看出,最后的执行结果是0,而不是10。 其实很好明白,因为给r赋值为10那个线程休眠了一秒,这里主线程是跑得比较快的,t线程还没来得及修改,主线程就直接输出r的值了。
那么问题来了,怎么实现让线程t执行完,主线程再输出r的值呢?
解决这个问题我们可以使用join方法
public class JoinTest {
static int r = 0;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
System.out.println("开始休眠");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束休眠");
r = 10;
});
t.start();
t.join(); // t线程加入主线程,等t线程结束后再执行后面代码
System.out.println("最终的结果为"+r);
}
}
可以在主线程中加入xxx.join(),可以理解为xxx线程加入了主线程
如果主线程中加入多个线程,那么主线程应该等待多长时间呢?
上代码:
public class JoinTest {
static int r = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000); // 休眠一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(() -> {
try {
Thread.sleep(2000); // 休眠两秒
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t3 = new Thread(() -> {
try {
Thread.sleep(3000); // 休眠三秒
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
t2.start();
t3.start();
long start = System.currentTimeMillis();
t1.join(); // 线程1加入
t2.join(); // 线程2加入
t3.join(); // 线程3加入
long end = System.currentTimeMillis();
System.out.println("主线程总共等待时间:"+(end-start));
}
}
最后运行结果:
可以看出 最后等待的结果是三秒。
也就是说当主线程中有多个线程的join时,主线程等待时间是其他多个线程中最大的执行完成时间。 因为其他的线程执行是相互独立的,互不影响
打断阻塞线程:
像某些线程调用了sleep、wait、join等方法进入阻塞状态时,如果被interrute打断就会抛出异常,并将打断标记设为false
public class Interrupt_test {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
try {
Thread.sleep(6000); // wait、join
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
Thread.sleep(1000);
System.out.println("interrupt");
t1.interrupt();
System.out.println("打断标记:---"+t1.isInterrupted());
}
}
打断正常运行的线程:
正常运行的线程(非阻塞状态下)被调用interrupt()方法,线程不会被打断,只是将线程的打断标记设成true
如果想打断的话需要在线程里写逻辑手动退出
public class Interrupt_test {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
while(true){
boolean interrupted = Thread.currentThread().isInterrupted();
if (interrupted){
System.out.println("有被打断-- 退出循环");
break;
}
}
});
t1.start();
Thread.sleep(1000);
System.out.println("interrupt");
t1.interrupt();
System.out.println("打断标记:---"+t1.isInterrupted());
}
}
两阶段终止模式
在一个线程T1中如何“优雅”的终止线程T2?这里的【优雅】指的是给T2一个料理后事的机会
错误思路:
正确方式:
public class Interrupt_test {
public static void main(String[] args) throws InterruptedException {
TwoPhaseTermination twoPhaseTermination = new TwoPhaseTermination();
twoPhaseTermination.start();
Thread.sleep(3000);
twoPhaseTermination.stop();
}
}
class TwoPhaseTermination {
private Thread monitor;
// 启动监控线程
public void start(){
monitor = new Thread(()->{
while (true){
Thread current = Thread.currentThread();
System.out.println(current.getName());
if(current.isInterrupted()){
System.out.println("料理后事");
break;
}
try {
Thread.sleep(1000); // 如果在这个位置被打断 会进入catch块儿
System.out.println("执行监控");
} catch (InterruptedException e) {
e.printStackTrace();
current.interrupt(); // 重新设置打断标记
}
}
},"t1");
monitor.start();
}
// 停止监控线程
public void stop(){
monitor.interrupt();
}
}
从操作系统层面来描述:分为5种
1. 【初始状态】: 仅是在语言层面创建了线程对象,还未与操作系统线程关联
2. 【可运行状态】(就绪状态):指线程已经被创建(与操作系统线程关联),可以由CPU调度执行
3. 【运行状态】: 指获取了CPU时间片运行中的状态
当CPU时间片用完,会从【运行状态】转换至【就绪状态】,会导致线程的上下文切换
4. 【阻塞状态】:
1. 如果调用了阻塞API,如BIO读写文件,这时该线程实际不会用到CPU,会导致线程的上下文切换,进入【阻塞状态】
2. 等BIO操作完毕,会有操作系统唤醒阻塞的线程,转换至【就绪状态】
3. 与【就绪状态】的区别是,对【阻塞状态】的线程来说只要他们一直不唤醒,调度器就一直不会考虑调度他们。
5. 【终止状态】: 表示线程已经执行完毕,生命周期已经结束,不会再转换为其他状态
从Java API层面描述: 分为6种
根据Thread.State 枚举,分为六种状态
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
1. NEW 线程刚被创建,但还没有调用start()方法
2. RUNNABLE 当调用start() 方法后,注意,Java API 层面的RUNNABLE状态涵盖了操作系统层面的【可运行状态】(就绪)、【运行状态】
3. BLOCKED、WAITING、TIMED_WAITING都是Java API层面对【阻塞状态】的细分
4. TERMINATED 当线程代码运行结束
演示代码看这些状态对应哪些情况:
public class Thread_state {
public static void main(String[] args) {
Thread t1 = new Thread("t1"){
// 不调用start方法
public void run(){
}
};
Thread t2 = new Thread("t2"){
// 一直循环一直执行下去
public void run(){
while (true){
}
}
};
t2.start();
Thread t3 = new Thread("t3"){
// 最后会执行结束 会终止
public void run(){
}
};
t3.start();
Thread t4 = new Thread("t4"){
// 加了一个Thread.sleep(100000000) 有时限的sleep
public void run(){
synchronized (Thread_state.class){
try {
Thread.sleep(100000000); // time_waiting 有时限的等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t4.start();
Thread t5 = new Thread("t5"){
public void run(){
// 加了t2.join();会阻塞
try {
t2.join(); // waiting 没有时间限制的等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t5.start();
Thread t6 = new Thread("t6"){
// 加了锁由于t4线程先获取锁,所以t6也会阻塞
public void run(){
synchronized (Thread_state.class){
// BLOCKED 锁阻塞
try {
Thread.sleep(100000000); // time_waiting 有时限的等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t6.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t1 state---"+t1.getState());
System.out.println("t2 state---"+t2.getState());
System.out.println("t3 state---"+t3.getState());
System.out.println("t4 state---"+t4.getState());
System.out.println("t5 state---"+t5.getState());
System.out.println("t6 state---"+t6.getState());
}
}