文章目录
- 1、进程和线程的区别
- 2、并行和并发的区别
- 3、创建线程的四种方式
- 3.1 Runnable和Callable创建线程的区别
- 3.2 线程的run和start
- 4、线程的所有状态与生命周期
- 5、新建T1、T2、T3,如何保证线程的执行顺序
- 6、notify和notifyAll方法有什么区别
- 7、wait方法和sleep方法有什么不同
- 8、如何停止一个正在运行的线程
- 8.1 使用退出标志
- 8.2 调用stop方法
- 8.3 调用interrupt方法
1、进程和线程的区别
- 一个程序或者应用被运行,就是开启了一个进程(应用.exe背后的代码被从磁盘加载到内存)
- 一个进程下,包含了多个线程,分别处理着不同的任务
- 线程更轻量,上下文成本切换更低(上下文切换即从一个线程切换到另一个线程)
2、并行和并发的区别
- 并行:多项工作一起执行,下班后,你收拾屋子,你对象做饭。与其相反的就是串行,即一步一步来,或者一件一件事情的做。(4核CPU同时执行4个线程)
- 并发:多个工作换着处理,下班后,你屋子收拾一半后,去准备做饭,菜准备好后又回去接着收拾屋子(多个线程轮流使用一个或者多个CPU)
补充:操作系统中,有个任务调度器,将CPU时间片(比如15毫秒)分给不同的程序使用。CPU在线程间的切换速度很快,给人的感觉就是同时运行的。
并发是一个人同时吃三个馒头(一张嘴,第一个馒头吃一口,转身再吃一口第二个馒头),而并行是三个人同时吃三个馒头(三个人各吃各的,好比多核CPU各自执行指令)。
最后,并发在后端层面上,也指同一时刻,多个线程在访问同一个资源,多个线程对一个点,对应的例子: 春运抢票、电商秒杀
3、创建线程的四种方式
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口,Callable的泛型即重写的call方法的返回值类型
- 线程池创建线程
3.1 Runnable和Callable创建线程的区别
- Runnable 接口 run 方法没有返回值
- Callable 接口 call 方法有返回值,是个泛型,和 Future、FutureTask配合可以用来获取异步执行的结果
- Callable 接口的 call 方法允许抛出异常,而 Runnable 接口的run 方法的异常只能在内部try-catch消化,不能继续上抛
3.2 线程的run和start
- start方法是用来启动线程的,通过该线程调用执行run方法,只能被调用一次
- run方法里面封装了要被线程执行的代码,是一个普通方法,可以被调用多次
public class ThreadTest {
public static void main(String[] args){
Thread t1 = new Thread("t1") {
@Override
public void run() {
System.out.println("running...");
}
};
t1.run();
t1.run();
t1.start();
t1.start();
}
调用两次run方法:
调用两次start方法:
4、线程的所有状态与生命周期
JDK源码中的线程状态枚举值:
状态流转:
-
NEW:新建状态,创建出一个Thread对象
-
Runnable:可执行状态,执行start方法,此时如果抢到了CPU时间片,那就去执行run方法,然后线程进入终止状态(死亡)
-
Blocked:没抢不到锁(lock、synchronized),进入阻塞状态,抢到锁后切换为Runnable状态
-
Waiting:调用了wait方法,进入等待状态,其他线程调用notify方法后被唤醒,进入Runnable状态
-
Timed_Waiting:计时等待状态,线程调用了sleep方法,sleep时间过后进入Runnable状态
流转图:
5、新建T1、T2、T3,如何保证线程的执行顺序
thread.join();
以下,t2线程中执行t1.join
,即等待t1线程执行完成后,再执行t2
6、notify和notifyAll方法有什么区别
notify是随机唤醒一个wait的线程,notifyAll是唤醒所有wait的线程。
如下,object对象上wait了两个线程t1、t2
notify时,只随机唤醒了一个object对象上wait的线程
7、wait方法和sleep方法有什么不同
共同点:
- wait和sleep都会让当前线程暂时放弃CPU时间片的使用权,进入阻塞状态
不同点:
1)方法归属不同:
- sleep是Thread类的静态方法
- wait是Object类的静态方法,每个对象都有
2)醒来时机不同:
- 执行sleep(long time) 和 wait(long time) 的线程,在相应毫秒过后会自己醒来进入就绪状态
wait(long time)
和wait()
都可以被 notify 唤醒,且wait() 如果不被唤醒就一直等待- sleep和wait都可以被打断唤醒
3)锁的特性不同:
- 调用wait方法,必须先获取wait方法所在对象的锁,否则
IllegalMonitorStateException
,sleep则不用获取任何锁 - wait对象执行后,会释放其占有的对象锁,其他线程可抢这个锁(放弃CPU时间片、放弃对象锁)
- sleep如果是在synchronized代码块里,sleep并不会释放synchronized抢到的对象锁(放弃CPU时间片,如果有锁也不会放弃)
8、如何停止一个正在运行的线程
8.1 使用退出标志
加一个标记字段,改了标记之后,线程return,结束执行
class MyRun implements Runnable {
boolean flag = false;
public void run() {
for (int i = 0; i < 100; i++) {
if (! this.flag) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
System.out.println(this.run);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println("这是一些终止线程前要做的事");
System.out.println("保存数据中..终止线程成功!");
return;
}
}
}
}
public class ThreadTest2 {
public static void main(String[] args) {
MyRun r = new MyRun();
Thread t = new Thread(r);
t.start();
//sleep主线程三秒
try{
Thread.sleep(3000);
}catch(InterruptedException e){
e.printStackTrace();
}
//终止,改run属性为true
r.run = true;
}
}
8.2 调用stop方法
sleep方法已作废
MyThread myThread = new MyThread();
myThread.start();
//强制终止
myThread.stop();
8.3 调用interrupt方法
- 打断正在阻塞的线程(sleep、wait、join),线程会抛出InterruptedException异常
public class MyInterrupt {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
System.out.println("t1 正在运行...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t1");
t1.start();
Thread.sleep(500);
t1.interrupt();
System.out.println(t1.isInterrupted());
}
}
- 打断正常线程,改变线程的中止标志位,是否退出由线程自己决定,思路上和方式一相似
public class MyInterrupt {
public static void main(String[] args) throws InterruptedException {
Thread t2 = new Thread(()->{
while(true) {
Thread current = Thread.currentThread();
boolean interrupted = current.isInterrupted();
if(interrupted) {
System.out.println("打断状态:"+ interrupted);
break;
}
}
}, "t2");
t2.start();
Thread.sleep(500);
t2.interrupt();
}
}
如上,主线程中调用t2.interrupt(),t2线程的中止标志位变为true,但是否中止执行,看 t2 线程自己,上面t2线程自己判断标志位为true时,就break结束循环