方法 | 说明 |
---|---|
Thread( ) | 创建线程对象 |
Thread(Runnable target) | 使用 Runnable 对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target,String name) | 使用 Runnable 对象创建线程对象,并命名 |
举例:
// Thread(String name)
public class ThreadDemo5 {
public static void main(String[] args) {
Thread t = new Thread("这是一个线程的名字,可以起的很长~"){
@Override
public void run() {
while (true){
}
}
};
t.start();
}
}
执行,在 jconsole 中查看:
属性 | 获取方法 |
---|---|
ID | getId( ) |
名称 | getName( ) |
状态 | getState( ) — JVM 中的线程状态 |
优先级 | getPriority( ) |
是否后台线程 | isDaemon( ) |
是否存活 | isAlive( ) |
是否被中断 | isInterrupted( ) |
演示举例:
public class ThreadDemo6 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread("HUAHua线程"){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
//Thread.currentThread() 获取到当前线程的实例,当前代码中,相当于 this.
System.out.println(Thread.currentThread().getName());
// 效果和上行代码一样
// System.out.println(this.getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// run 方法的执行过程,就代表着系统内线程的生命周期
// run 方法执行中,内核的线程就存在
// run 方法执行完毕,内核中的线程就随之销毁
System.out.println("线程要退出了");
}
};
// t.start();
// 只要线程创建完毕,下面这些属性就不变了,除非显式修改
System.out.println(t.getName());
System.out.println(t.getPriority());
System.out.println(t.isDaemon());
System.out.println(t.getId());
// 这些属性,会随着线程的运行过程而发生改变
System.out.println(t.isAlive());
System.out.println(t.isInterrupted());
System.out.println(t.getState());
t.start();
while (t.isAlive()){
System.out.println("HUAHua线程正在运行.....");
System.out.println(t.getState());
System.out.println(t.isInterrupted());
Thread.sleep(300);
}
}
}
补充:
Thread.currentThread( ),即: 获取到当前线程的实例
在当前代码中,相当于 this.
但,不是所有情况都可以使用this
注意:
若使用继承 Thread 的方式来创建线程,这个操作就和 this 是一样的
若使用 Runnable 的方式或者 lambda 的方式,此时就不能使用 this
此时,运行程序,输出结果:
取前小部分输出结果:
线程对象被创建出来并不意味着线程就开始运行了
调用 start 方法,才真的在操作系统的底层创建出一个线程
创建实例,和重写 run 方法,是告诉线程要做什么,而调用 start 方法,才是真正开始执行
中断,就是让一个线程结束 — 结束,可能有两种情况:
①已经把任务执行完了;即:让线程 run 执行完(比较温和)
②任务执行了一半,被强制结束,即:调用线程的 interrupt 方法(比较激烈)
常见的线程中断有以下两种方式:
private static boolean isQuit = false;
public static void main(String[] args) throws InterruptedException {
// 创建一个线程
Thread t = new Thread(){
@Override
public void run(){
while (!isQuit){
System.out.println("交易继续...");
try {
Thread.sleep(500);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("交易被终止!");
}
};
t.start();
Thread.sleep(5000);
System.out.println("发现内鬼,终止交易!");
isQuit = true;
}
输出结果:
上述方式的结束方式比较温和
当标记位被设置之后,等到当前这次循环执行完了之后,再结束线程
例如: 当线程执行到 sleep 的时候,已经 sleep 100ms 了,此时 isQuit 被设置为 true,当前线程不会立刻退出,而是会继续 sleep,把剩下的 400ms sleep 完,才会结束线程
public static void main(String[] args) throws InterruptedException {
// 创建一个线程
Thread t = new Thread(){
@Override
public void run(){
// 此处直接使用线程内部的标记为来判定
while (!Thread.currentThread().isInterrupted()){
System.out.println("交易继续...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("交易被终止!");
}
};
t.start();
Thread.sleep(1000);
System.out.println("发现内鬼,终止交易!");
t.interrupt();
}
输出结果:
interrupt 本质上是给该线程触发一个异常 InterruptedException,此时,线程内部就会收到这个异常,具体针对这个异常如何处理,这是 catch 内部的事情
例如,上边代码中,catch 中,只是打印了调用栈,并没有真正的结束循环,故应该再加一个 break 结束循环
如果 catch 中没有 break,相当于忽略了异常
如果有 break,则触发异常就会导致循环结束,从而线程也结束
Thread 收到通知的方式有两种:
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.interrupted());
}
}
};
t.start();
t.interrupt();
}
输出结果:
.
Thread.currentThread( ).isInterrupted( )方式:
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().isInterrupted());
}
}
};
t.start();
t.interrupt();
}
输出结果:
.
思考: 如果没有 sleep,新线程能否继续输出?
这个不确定,多线程之间是抢占式执行的,如果主线程没有 sleep,此时接下来 CPU 是执行主线程的 isQuit = true,还是新线程的 while 循环,这个不确定(都有可能)
对于新线程来说,run 方法执行完,线程就结束了
对于主线程来说,main 方法执行完,主线程就结束了
线程和线程之间是并发执行的关系,多个线程之间,谁先执行谁后执行,谁执行到哪里让出 CPU,作为程序员是完全无法感知的,是全权由系统内核负责
例如: 创建一个新线程的时候,此时接下来是主线程继续执行,还是新线程继续执行,这是不好保证的 (这也是 "抢占式执行"的重要特点 )
可以通过下面的代码验证:
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(){
@Override
public void run() {
while (true){
System.out.println("我是新线程!");
try {
Thread.sleep(100);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
while (true){
System.out.println("我是主线程!");
Thread.sleep(100);
}
}
截取部分输出结果:
虽然我们没办法控制哪个线程先走,哪个线程后走,但是我们可以控制,让哪个线程先结束,哪个线程后结束 — 借助线程等待
join 方法,执行 join 方法的线程就会阻塞,一直阻塞到对应线程执行结束之后,才会继续执行
存在的意义:为了控制线程结束的先后顺序
多线程的一个场景:
例如要进行一个复杂运算,主线程把任务分成几份,每个线程计算自己的一份任务
当所有任务都被分别计算完毕后,主线程再来进行汇总(就必须保证主线程是最后执行完的线程)
举例:
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我是线程1");
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
Thread t2 = new Thread(){
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我是线程2");
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start();
t1.join();
t2.start();
t2.join();
}
输出结果:
由于 t1 的 join 放在了 t2 的 strat 之前,意味着此时还没有执行线程2,t1 这里就已经阻塞等待了,一直到 t1 结束,线程2才会继续往下,开始执行
若交换 t1.join( ) 和 t2.start( ); 的位置,输出结果如下:
如果线程已经结束了,才调用 join,此时 join 也会立刻返回
public static Thread currentThread( ) — 返回当前线程对象的引用
代码示例:
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
休眠是让当前线程进入阻塞
关于线程休眠:
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/m0_47988201/article/details/121192572
内容来源于网络,如有侵权,请联系作者删除!