1、Thread 类及常见方法
1.1 Thread 的常见构造方法
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");
1.2 Thread 的几个常见属性
- ID 是线程的唯⼀标识,不同线程不会重复,是JVM自动分配的身份标识
- 名称是各种调试工具用到
- 状态表示线程当前所处的⼀个情况,下面我们会进⼀步说明
- 优先级高的线程理论上来说更容易被调度到
- 关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。
补充:
前台线程的运行会阻止进程的结束;后台线程的运行不会阻止进程的结束。
咱们代码创建的线程,默认就是前台线程,会阻止进程的结束,只要前台线程没执行完,进程就不会结束,即使main已经执行完毕了。
代码举例:
public class ThreadDemo { public static void main(String[] args) { Thread t= new Thread(()->{ for (int i = 0;i < 10;i++) { System.out.println("线程工作"); try{ Thread.sleep(3000); }catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); } }
分析这个代码,当执行到t.start()时,会创建一个新的线程,新的线程去执行循环,而main线程继续自己的后续代码的执行,此时后面已没有代码,则main线程执行完毕,可以通过jonsole工具进行查看,如图:
按照我们之前的理解,main执行完毕,进程就应该结束,但是很明显,该进程依然继续执行,我们可以根据上述代码的运行结果来看:
设为后台线程的代码:
public class ThreadDemo { public static void main(String[] args) { Thread t= new Thread(()->{ for (int i = 0;i < 10;i++) { System.out.println("线程工作"); try{ Thread.sleep(1000); }catch (InterruptedException e) { e.printStackTrace(); } } }); t.setDaemon(true); t.start(); } }
补充:
isAlive()该方法表示了内核中的线程(PCB)是否还存在。
java代码中定义的线程对象(Thread)实例,虽然表示一个线程,但这个对象本身的生命周期与内核中的线程PCB生命周期是不完全一样的。
下面是代码示例:
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());
}
}
运行结果:
1.3 启动⼀个线程 – start()
调用 start 方法, 才真的在操作系统的底层创建出⼀个线程. 对于同一个Thread对象来说,start只能调用一次。
class MyThread8 extends Thread { @Override public void run() { while (true) { System.out.println("hello"); try { Thread.sleep(1000); }catch (InterruptedException e) { e.printStackTrace(); } } } } public class ThreadDemo8 { public static void main(String[] args) { Thread t = new MyThread8(); //t.run(); //此时是main方法调用run,没有创建新线程,后续的循环执行不到 t.start(); //会创建新的线程,新线程执行run的循环,主线程main继续后续代码 while (true) { System.out.println("hello main"); try { Thread.sleep(1000); }catch (InterruptedException e) { e.printStackTrace(); } } } }
当创建一个Thread类对象 t 时:
由 t 调用run方法时( t.run() ),并没有创建出一个新的线程,这个操作还是在主线程main中进行的,循环打印hello,此时代码就只能停留在run的循环中了,下方main中的循环执行不到。
若由 t 调用start,这时会创建出一个新的线程,去执行run循环;main线程则继续执行自己的后续循环。
总结:
运行结果不同:
1.4 中断⼀个线程
接着上面图片张三、李四的例子,李四⼀旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我们需要增加⼀些机制,例如老板突然来电话了,说转账的对方是个骗子,需要赶紧停⽌转账,那张三该如何通知李四停止呢?这就涉及到我们的停⽌线程的方式了。
public class ThreadDemo9 {
private static boolean isQuit = false;
public static void main(String[] args) {
Thread t = new Thread(()->{
while (!isQuit) {
System.out.println("hello");
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t结束");
});
t.start();
try {
Thread.sleep(3000);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("让t线程结束");
isQuit = true;
}
}
运行结果:
我们可以看到代码中,将自定义的变量标志位写成了类的静态成员变量,那是否可以写为main方法中的局部变量?
这是因为我们使用了创建线程的lambda表达式方法,lambda表达式有一个语法:变量捕获。
lambda表达式的变量捕获,本质上就是,把外面的变量当作参数传进来(参数是隐藏的)。这个捕获的变量得是 final 修饰的或者“事实final”(虽然没有写final,但是没有修改)。因为此处isQuit是确实要修改的,不能写成final,它也不是事实final,因此将标志位变量isQuit定义为局部变量是行不通的!
lambda表达式,本质上是“函数式接口”,匿名内部类。对于内部类,访问外部类的成员是完全可以的,这个事情不受到变量捕获的影响。
示例代码:
public static void main(String[] args) {
Thread t = new Thread(()->{
while (!Thread.currentThread().isInterrupted()) {
System.out.println("我是一个线程,正在工作");
//interrupt会影响sleep,线程不会结束
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程结束");
});
t.start();
try {
Thread.sleep(3000);
}catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("让t线程结束");
t.interrupt();
}
}
运行结果:
可以看到抛出异常,并且线程不会结束,继续往下循环输出。这是因为什么呢?
这是因为存在sleep,在执行sleep的过程中,调用interrupt。大概率sleep休眠时间还没到,被提前唤醒了。
sleep被提前唤醒,会做两件事:
对于线程不会结束,就是标志位被清除了。我们通过interrupt方法,已经把标志位设为true了,但是sleep提前唤醒操作,又把标志位清除,设为原来的false,所以线程不会结束。
要想线程结束,只需要在catch中加上break即可。但此时仍会抛异常,不想抛,就不写输出e.printStackTrace()。
Thread t = new Thread(()->{
while (!Thread.currentThread().isInterrupted()) {
System.out.println("我是一个线程,正在工作");
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
// e.printStackTrace();
break; //加上break,仍会抛异常(不想抛,就不写上面输出e.printStackTrace()),线程会结束
}
}
System.out.println("线程执行完毕");
});
这时的结果:
1.5 等待⼀个线程 – join()
代码示例:
public class ThreadDemo {
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("王五⼯作结束了");
}
}
这里是几个join方法:
1.6 获取当前线程引用
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
1.7 休眠当前线程
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());
}
}
2、线程的状态
2.1 观察线程的所有状态
public class ThreadState {
public static void main(String[] args) {
for (Thread.State state : Thread.State.values()) {
System.out.println(state);
}
}
}
- NEW: 安排了工作, 还未开始行动
- RUNNABLE: 可工作的,又可以分成正在⼯作中和即将开始⼯作.
- BLOCKED: 这几个都表示排队等着其他事情
- WAITING: 这几个都表示排队等着其他事情
- TIMED_WAITING: 这几个都表示排队等着其他事情
- TERMINATED: 工作完成了.
2.2 线程状态和状态转移的意义
大家不要被这个状态转移图吓到,我们重点是要理解状态的意义以及各个状态的具体意思。
TERMINATED
状态。
2.3 观察线程的状态和转移
观察 1: 关注 NEW 、 RUNNABLE 、 TERMINATED 状态的转换
public class ThreadStateTransfer {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
for (int i = 0; i < 1000_0000; i++) {}
}, "李四");
System.out.println(t.getName() + ": " + t.getState());;
t.start();
while (t.isAlive()) {
System.out.println(t.getName() + ": " + t.getState());;
}
System.out.println(t.getName() + ": " + t.getState());;
}
}
观察 2: 关注 WAITING 、 BLOCKED 、 TIMED_WAITING 状态的转换
public static void main(String[] args) {
final Object object = new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (object) {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}, "t1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (object) {
System.out.println("hehe");
}
}}, "t2");
t2.start();
}
public static void main(String[] args) {
final Object object = new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (object) {
try {
// [修改这⾥就可以了!!!!!]
// Thread.sleep(1000);
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "t1");
...
}
使用户jconsole 可以看到 t1 的状态是 WAITING。
- BLOCKED 表示等待获取锁,WAITING 和 TIMED_WAITING 表示等待其他线程发来通知.
- TIMED_WAITING 线程在等待唤醒,但设置了时限; WAITING 线程在无限等待唤醒
原文地址:https://blog.csdn.net/m0_61876562/article/details/134583641
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_30392.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!