当前位置:网站首页>(必须掌握的多线程知识点)认识线程,创建线程,使用Thread的常见方法及属性,以及线程的状态和状态转移的意义
(必须掌握的多线程知识点)认识线程,创建线程,使用Thread的常见方法及属性,以及线程的状态和状态转移的意义
2022-06-26 18:01:00 【一只懐坏旭】
学习多线程
一.认识线程
1.1 概念
我们设想如下场景: 一家公司要去银行办理业务,既要进行财务转账,又要进行福利发放,还得进行缴社保。如果只有张三一个会计就会
忙不过来,耗费的时间特别长。为了让业务更快的办理好,张三又找来两位同事李四、王五一起来帮助他,三个人分
别负责一个事情,分别申请一个号码进行排队,自此就有了三个执行流共同完成任务,但本质上他们都是为了办理一
家公司的业务。此时,我们就把这种情况称为多线程,将一个大任务分解成不同小任务,交给不同执行流就分别排队
执行。其中李四、王五都是张三叫来的,所以张三一般被称为主线程(Main Thread)。
那这个和多进程又有什么区别的,最大的区别就是这些执行流之间是否有资源的共享。比如之前的多进程例子中,每
个客户来银行办理各自的业务,但他们之间的票据肯定是不想让别人知道的,否则钱不就被其他人取走了么。而上面
我们的公司业务中,张三、李四、王五虽然是不同的执行流,但因为办理的都是一家公司的业务,所以票据是共享着的。这个就是多线程和多进程的最大区别。
进程是系统分配资源的最小单位,
线程是系统调度的最小单位。
一个进程内的线程之间是可以共享资源的。
每个进程至少有一个线程存在,即主线程(此处主线程为C语言的入口函数级别的线程)。
我们以前都是写完代码然后直接运行得到结果,那么当你run或者debug时,是启动了什么程序?你知道真实的java进程的运行过程吗?
|
|
.首先运行main方法(本质是调用java包名.类名 来启动java程序——》启动之后就相当于java进程运行起来了)那么接下来进程直接执行代码行输出结果吗??
当然不是!!
比如此时java程序是在windows上运行,这个java.exe是用C语言编写的,此时java命令代码执行是启动系统main方法,一般是C语言的main入口函数(系统级别的main线程,也就是系统级别的主线程),
执行过程:

1.2 创建线程
方法1: 继承 Thread 类
可以通过继承 Thread 来创建一个线程类,该方法的好处是 this 代表的就是当前线程,不需要通过
Thread.currentThread() 来获取当前线程的引用。
class MyThread extends Thread {
@Override
public void run() {
System.out.println("这里是线程运行的代码");
}
}
MyThread t = new MyThread();
t.start(); // 线程开始运行 方法二:实现 Runnable 接口
通过实现 Runnable 接口,并且调用 Thread 的构造方法时将 Runnable 对象作为 target 参数传入来创建线程对象。
该方法的好处是可以规避类的单继承的限制;但需要通过 Thread.currentThread() 来获取当前线程的引用
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "这里是线程运行的代码");
}
} Thread t = new Thread(new MyRunnable());
t.start(); // 线程开始运行 方法三:其他变形 方法
// 使用匿名类创建 Thread 子类对象
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("使用匿名类创建 Thread 子类对象");
}
};
// 使用匿名类创建 Runnable 子类对象
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("使用匿名类创建 Runnable 子类对象");
}
});
// 使用 lambda 表达式创建 Runnable 子类对象
Thread t3 = new Thread(() -> System.out.println("使用匿名类创建 Thread 子类对象"));
Thread t4 = new Thread(() -> {
System.out.println("使用匿名类创建 Thread 子类对象");
});
二.Thread 类及常见方法
Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
用我们上面的例子来看,每个执行流,也需要有一个对象来描述,类似下图所示,而 Thread 类的对象就是用来描述
一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理
2.1 Thread 的常见构造方法
| 方法 | 说明 |
|---|---|
| Thread() | 创建线程对象 |
| Thread(Runnable target) | 使用 Runnable 对象创建线程对象 |
| Thread(String name) | 创建线程对象,并命名 |
| Thread(Runnable target, String name | 使用 Runnable 对象创建线程对象,并命名 |
| 【了解】Thread(ThreadGroup group,Runnable target | 线程可以被用来分组管理,分好的组即使线程组,这个目前我们了解即可 |
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");2.2 Thread 的几个常见属性
| 属性 | 获取方法 |
|---|---|
| ID | getId |
| 名称 | getName |
| 状态 | getState |
| 优先级 | getPriority |
| 是否后台线程 | isDaemon |
| 是否存活 | isAlive |
| 是否被中断 | isInterrupted |
- ID 是线程的唯一标识,不同线程不会重复
- 名称是各种调试工具用到
- 状态表示线程当前所处的一个情况,下面我们会进一步说明
- 优先级高的线程理论上来说更容易被调度到
- 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
- 是否存活,即简单的理解,为 run 方法是否运行结束了
- 线程的中断问题,下面我们进一步说明
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName() + ": 我还活着");
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ": 我即将死去");
});
System.out.println(Thread.currentThread().getName()
+ ": ID: " + thread.getId());
System.out.println(Thread.currentThread().getName()
+ ": 名称: " + thread.getName());
System.out.println(Thread.currentThread().getName()
+ ": 状态: " + thread.getState());
System.out.println(Thread.currentThread().getName()
+ ": 优先级: " + thread.getPriority());
System.out.println(Thread.currentThread().getName()
+ ": 后台线程: " + thread.isDaemon());
System.out.println(Thread.currentThread().getName()
+ ": 活着: " + thread.isAlive());
System.out.println(Thread.currentThread().getName()
+ ": 被中断: " + thread.isInterrupted());
thread.start();
while (thread.isAlive()) {
System.out.println(Thread.currentThread().getName()
+ ": 状态: " + thread.getState());
}
}
}
2.3 启动一个线程
之前我们已经看到了如何通过覆写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行
了。
覆写 run 方法是提供给线程要做的事情的指令清单
线程对象可以认为是把 李四、王五叫过来了
而调用 start() 方法,就是喊一声:”行动起来!“,线程才真正独立去执行了
面试题: 一定要注意 run 方法和 start 方法是不同的,启动线程必须要调用 start 方法。!!此处要认识到start是启动某个线程,run是执行线程内部的代码(即线程要做的事情),线程没有启动(start),run方法不会执行到,因为线程只有start才能进入就绪态,等待系统调度,调度到时线程进入运行态,才能执行run方法。
2.4 中断一个线程
例如老板突然来电话了,说转账的对方是个骗子,需要赶紧停止转账,那张三该如何通知李四停止呢?这就涉
及到我们的停止线程的方式了。
目前常见的有以下两种方式:
1. 通过共享的标记来进行沟通
2. 调用 interrupt() 方法来通知
实例1:
此处使用共享标记来实现中断线程(也就是设置布尔变量)
public class ThreadDemo {
private static class MyRunnable implements Runnable {
public volatile boolean isQuit = false;
@Override
public void run() {
while (!isQuit) {
System.out.println(Thread.currentThread().getName()
+ ": 别管我,我忙着转账呢!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()
+ ": 啊!险些误了大事");
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable target = new MyRunnable();
Thread thread = new Thread(target, "李四");
System.out.println(Thread.currentThread().getName()
+ ": 让李四开始转账。");
thread.start();
Thread.sleep(10 * 1000);
System.out.println(Thread.currentThread().getName()
+ ": 老板来电话了,得赶紧通知李四对方是个骗子!");
target.isQuit = true;
}
} 这种方法虽然可以实现中断线程,但是当线程调用了sleep方法,并且休眠时间较长,就无法及时中断线程了。所以不太提倡。
实例2:
public class Thread2 {
private static class MyRunnable implements Runnable {
@Override
public void run() {
// 两种方法均可以
while (!Thread.interrupted()) {
//while (!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName()
+ ": 别管我,我忙着转账呢!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName()
+ ": 有内鬼,终止交易!");
break;
}
}
System.out.println(Thread.currentThread().getName()
+ ": 啊!险些误了大事");
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable target = new MyRunnable();
Thread thread = new Thread(target, "李四");
System.out.println(Thread.currentThread().getName()
+ ": 让李四开始转账。");
thread.start();
Thread.sleep(10 * 1000);
System.out.println(Thread.currentThread().getName()
+ ": 老板来电话了,得赶紧通知李四对方是个骗子!");
thread.interrupt();
}
} 重点说明下第二种方法:
通过 thread 对象调用 interrupt() 方法通知该线程停止运行
thread 收到通知的方式有两种:
2.1. 如果线程调用了 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除
中断标志
2.2. 否则,只是内部的一个中断标志被设置,thread 可以通过2.2.1. Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志
2.2.2. Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志
第二种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到
2.5等待一个线程-join()
有时,我们需要等待一个线程完成它的工作后,才能进行自己的下一步工作。例如,张三只有等李四转账成功,才决
定是否存钱,这时我们需要一个方法明确等待线程的结束。
public class Thread2 {
public static void main(String[] args) throws InterruptedException {
Runnable target = () -> {
for (int i = 0; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName()
+ ": 我还在工作!");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ": 我结束了!");
};
Thread thread1 = new Thread(target, "李四");
Thread thread2 = new Thread(target, "王五");
System.out.println("先让李四开始工作");
thread1.start();
thread1.join();
System.out.println("李四工作结束了,让王五开始工作");
thread2.start();
thread2.join();
System.out.println("王五工作结束了");
}
}
附:
| 方法 | 说明 |
|---|---|
| public void join() | 等待线程结束 |
| public void join(long millis) | 等待线程结束,最多等 millis 毫秒 |
| public void join(long millis, int nanos) | 同理,但可以更高精度 |
2.6 获取当前线程引用
这个方法之前已经使用过了,相信大家一定有了一定的认识了吧
| 方法 | 说明 |
|---|---|
| public static Thread currentThread(); | 返回当前线程对象的引用 |
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
} 2.7 休眠当前线程
有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证休眠时间是大于等于休眠时间的
| 方法 | 说明 |
|---|---|
| public static void sleep(long millis) throws InterruptedException | 休眠当前线程 millis 毫秒 |
| public static void sleep(long millis, int nanos) throws InterruptedException | 可以更高精度的休眠 |
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println(System.currentTimeMillis());
Thread.sleep(3 * 1000);
System.out.println(System.currentTimeMillis());
}
} 3. 线程的状态
3.1观察线程的所有状态
线程的状态是一个枚举类型 Thread.State
public class ThreadState {
public static void main(String[] args) {
for (Thread.State state : Thread.State.values()) {
System.out.println(state);
}
}
} N
EW
RUNNABLE
BLOCKED
WAITING
TIMED_WAITING
TERMINATED3.2线程状态和状态转移的意义

大家不要被这个状态转移图吓到,我们重点是要理解状态的意义以及各个状态的具体意思。

刚把李四、王五找来,还是给他们在安排任务,没让他们行动起来,就是 NEW 状态;
当李四、王五开始去窗口排队,等待服务,就进入到 RUNNABLE 状态。该状态并不表示已经被银行工作人员开始接
待,排在队伍中也是属于该状态,即可被服务的状态,是否开始服务,则看调度器的调度;
当李四、王五因为一些事情需要去忙,例如需要填写信息、回家取证件、发呆一会等等时,进入 BLOCKED 、 WATING 、 TIMED_WAITING 状态,
如果李四、王五已经忙完,为 TERMINATED 状态。
所以,之前我们学过的 isAlive() 方法,可以认为是处于不是 NEW 和 TERMINATED 的状态都是活着的。

后序继续更新奥!!!
边栏推荐
- Uncover the secret of Agora lipsync Technology: driving portraits to simulate human speech through real-time voice
- halcon之区域:多种区域(Region)特征(5)
- MySQL exports all table indexes in the database
- idea中文插件chinese(simplified) language pack
- 在国金证券开户怎么样?开户安全吗?
- 同花顺开户怎么样安全吗?怎么炒股开户
- KDD 2022 | how to use comparative learning in cross domain recommendation?
- 数字签名论述及生成与优点分析
- ZCMU--1367: Data Structure
- vutils. make_ A little experience of grid () in relation to black and white images
猜你喜欢
![[buuctf.reverse] 126-130](/img/df/e35633d85caeff1dece62a66cb7804.png)
[buuctf.reverse] 126-130

The king of Internet of things protocol: mqtt

10 cloud security best practices that enterprises need to know

Niuke network: Design LRU cache structure design LFU cache structure

MySQL add column failed because there was data before, not null by default

Row lock analysis and deadlock

#26class中get和set设置

Knapsack problem with dependency

14 MySQL tutorial insert insert data

有依赖的背包问题
随机推荐
LM06丨仅用成交量构造抄底摸顶策略的奥秘
Concurrent thread safety
[buuctf.reverse] 126-130
Chinese (Simplified) language pack
Tsinghua & Shangtang & Shanghai AI & CUHK proposed Siamese image modeling, which has both linear probing and intensive prediction performance!
Hello, is it safe to open an online stock account and buy stocks now?
JS 常用正则表达式
Row lock analysis and deadlock
Vue--vuerouter cache routing component
并发之线程安全
[dynamic planning] Jianzhi offer II 091 Paint the house
17.13 补充知识、线程池浅谈、数量谈、总结
RSA概念详解及工具推荐大全 - lmn
sql中ROUND和TRUNCATE的区别(四舍五入还是截取小数点后几位)
sparksql如何通过日期返回具体周几-dayofweek函数
RSA concept explanation and tool recommendation - LMN
#26class中get和set设置
Which low code platform is more friendly to Xiaobai? Here comes the professional evaluation!
14《MySQL 教程》INSERT 插入数据
[QNX] Command