一文吃透 Java 线程:4 种创建方式对比 + Thread 中断机制完整教程

一.线程创建

方法1:继承Thread类

继承Thread来创建一个线程类,重写run()方法

class MyThread extends Thread{

    @Override
    public void run() {
        // super.run();
        System.out.println("MyThread run");
    }
    
}
public class Demo18 {
    public static void main(String[] args) {
        MyThread t=new MyThread();
        t.start();
        System.out.println("main run");
    }
}

执行结果如下:

方法2:实现Runnable接口

1.实现Runnable接口

  1. 创建一个类并实现java.lang.Runnable接口
  2. 必须实现run()方法,在其中编写线程要执行的业务逻辑
class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 线程执行的具体任务
        for(int i=0; i<5; i++){
            System.out.println("线程运行中: "+i);
        }
    }
}

2.创建Thread实例

  • 实例化实现Runnable接口的类对象
  • 通过Thread类的构造方法创建线程对象
  • MyRunnable myRunnable = new MyRunnable();
    Thread thread = new Thread(myRunnable);
    // 或者带线程名称
    Thread namedThread = new Thread(myRunnable, "MyThread-1");
    

3.调用start方法

  • 调用Thread实例的start()方法启动线程
class MyThread implements Runnable{
    @Override
    public void run() {
        System.out.println("MyThread run");
    }
}
public class Demo19 {
    public static void main(String[] args) {
        MyThread myRunnable = new MyThread();
        Thread t = new Thread(myRunnable);
        t.start();
        System.out.println("main run");
    }
}

方法3:匿名内部类创建Thread子类对象


    public static void main(String[] args) {
        Thread t=new Thread(){
            @Override
            public void run() {
                System.out.println("MyThread run");
            }
        };
    }

方法4:匿名内部类创建Runnable子类对象

    public static void main(String[] args) {
        Thread t=new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("MyThread run");
            }
        });
    }

方法5:lambda表达式创建Runnable子类对象(最常用)

 public static void main(String[] args) {
        Thread t=new Thread(()->{
            System.out.println("MyThread run");
        });
        t.start();
    }

二.Thread常见的方法

1.常见的构造方法

Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程
Thread(Runnable target,String name)使用Runnable对象创建线程并给创建的线程命名

2.常见的属性

IDgetId()
名称getName()
状态getState()
优先级getDaemon()
是否是后台线程isDaemon()
是否存活isAlive()

  • ID是线程的唯一标识,不同线程不会重复
  • 名称是各种调试工具用到的
  • 状态表示线程当前所处的一个情况
  • 优先级高的线程更容易被调度到
  • 后台线程:负责维持进程的正常运行,但其生命周期不会直接影响进程的终止。即使后台线程仍在执行,进程仍可正常退出。
  • 前台线程:作为进程终止的关键因素,其执行状态直接影响进程的生命周期。若存在多个前台线程,进程必须等待所有前台线程执行完毕才会结束。
  • 是否存活,可以理解为run()方法运行完就结束了

3.启动线程-start()

  • 调用start动作非常快

     一旦执行start,代码会立刻向下执行,不会产生任何阻塞。

  • 一个对象的start方法只能执行一次
start和run的区别:

start() 方法是线程类的核心方法,调用后会触发以下行为:

  • 创建一个新的操作系统级线程,由 JVM 和操作系统共同调度。
  • 在新创建的线程中自动调用 run() 方法,实现异步执行。

run() 方法仅仅是线程类的普通方法:

  • 直接调用 run() 不会创建新线程,代码仍在当前线程中同步执行。
  • 其逻辑与普通方法调用无异,无法实现多线程并发效果。

三.线程的中断

线程的中断可以理解为打断这个线程,线程打断有两种方法。

方法一:用标志位

public class Demo24 {
    public static boolean flag=true;
    public static void main(String[] args) {
        Thread t=new Thread(()->{
            while(flag){
                try {
                    System.out.println("MyThread run");
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
            }
        });
        t.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        flag=false;
    }
}

方法二:使用thread对象的interrupt()方法通知线程结束

public static void main(String[] args) {
        Thread t=new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                try {
                    Thread.sleep(1000);
                     System.out.println("MyThread run");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    // break;
                }
            }
        });
        t.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        t.interrupt();
        System.out.println("MyThread interrupt");
    }
注意:

当一个线程处于sleep状态时,若其他线程调用该线程的interrupt()方法,会触发以下行为:

  • sleep状态的线程会被立即唤醒,并抛出InterruptedException异常。
  • 线程的isInterrupted标志会被自动重置为false,这是Java中断机制的设计特性。

线程中断与sleep的交互机制

调用 interrupt() 方法会中断处于 sleep 状态的线程,触发 InterruptedException 异常。该异常的处理逻辑会清除线程的中断标志位(即 isInterrupted 返回 false

恢复中断标志的方法

在catch块中加break(推荐),这种方式让线程决定自己是否要终止。


    public static void main(String[] args) {
        Thread t=new Thread(()->{
            while(!Thread.currentThread().isInterrupted()){
                try {
                    Thread.sleep(1000);
                     System.out.println("MyThread run");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    // e.printStackTrace();
                    break;
                }
            }
        });
        t.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        t.interrupt();
        System.out.println("MyThread interrupt");
    }

线程中断的总结:

使用Interrupt方法的时候,

1.如果线程没有进行sleep等阻塞操作,线程的isInterrupted()方法返回true,可以通过循环条件结束线程。

2.如果线程进行sleep等阻塞操作,线程的isInterrupted()返回的是true,但是sleep如果被提前唤醒,抛出InterruptException异常,同时会把isInterrupted()的返回结果设置为false,此时就需要手动决定是否要结束线程了。

四.等待线程

有的时候,我们需要等待一个线程完成它的工作后,才能进行下一步工作。

示例一:

 public static void main(String[] args) {
        Thread t=new Thread(()->{
            for(int i=0;i<10;i++){
                System.out.println("MyThread run"+i);
            }
        });
        t.start();
        try {
            t.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("MyThread join");
    }

示例二:


    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) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                // System.out.println("MyThread run"+i);
            }
        };
        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去掉会出现:

注意:哪个线程调用join,哪个线程就是"等待的一方"

public void join()等待线程结束
public void join(long millis)等待线程结束,最多等待millis毫秒
public void join(long millis,int nanos)同理,但可以更高的精度

五.获取当前线程引用

public static Thread currentThread();返回当前线程对象的引用
 public static void main(String[] args) {
        Thread thread=Thread.currentThread();
        System.out.println(thread);
        System.out.println(thread.getName());
    }

运行结果:

六.休眠当前进程

通过Thread.sleep()控制进程休眠:

       Thread.sleep()本质就是让线程的状态变成"阻塞"状态,此时线程就不参与cpu调度了。直到时间到,这个线程状态恢复成"就绪"状态才能参与cpu调度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IKUN家族

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值