likes
comments
collection
share

【一文通关】Java多线程基础(3)- 常用方法

作者站长头像
站长
· 阅读数 14

线程的常用方法

start()方法

start()方法是线程类中用于启动线程的方法。在线程对象创建之后,调用 start() 方法会自动调用线程的 run() 方法(start里面去调用run方法的逻辑是使用C写的),使线程进入可执行状态(从新建状态转化为就绪状态)。线程一旦进入可执行状态,就有可能被CPU调度执行。start() 方法只能被调用一次,多次调用会抛出异常IllegalThreadStateExcellent。

run()方法

在Java中,线程的执行代码通常是通过实现Runnable接口来完成的。Runnable接口中有一个run()方法,该方法定义了线程要执行的代码。当一个线程被创建并启动后,它的run()方法会被自动调用。当run()方法被执行完毕,线程会变为死亡状态。

所以要再调用start()方法,需要等run()方法执行完毕,否则会抛出异常IllegalThreadStateExcellent

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("MyRunnable is running...");
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建线程对象
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        
        // 启动线程
        thread.start();
    }
}

在本例中,我们定义了一个实现了Runnable接口的MyRunnable类,并覆写了run()方法。在主线程中,我们创建了一个MyRunnable对象myRunnable,并将其作为参数传递给Thread构造函数,创建一个Thread对象thread。最后,我们调用thread的start()方法启动线程。这将导致线程进入可执行状态,并自动调用run()方法。在本例中,线程的执行结果将在控制台上打印一条消息"MyRunnable is running..."。

需要注意的是,Java中的线程可以通过继承Thread类来创建,但这种方式并不推荐,因为继承Thread类会限制代码的灵活性,而实现Runnable接口则可以更好地实现代码的复用。

sleep(int millsecond)

线程的执行是按照优先级来进行的, 当高级别的线程未死亡时,低级别的线程是不可能获得CPU资源的。

但有时我们需要低级别的线程先做一些工作来配合高级别的线程,因此,需要高级别的线程让出CPU资源, 此时,我们就可以使用sleep()方法, 让它休眠一会儿。

休眠的时间由你的参数决定, millsecond是以毫秒为单位的休眠时间。如果线程在休眠时被打断,JVM就抛出InterruptedException, 所以,sleep方法必须在try-catch语句中。

isAlive()

线程处于新建状态,即分配了空间,但没有调用start()之前, isAlive()返回false;

线程调用了start()方法并且占用了CPU的资源, 将调用run()方法,在run()方法结束之前,线程调用isAlive() 返回true。

当线程处于死亡状态,即run()方法执行完毕,这时isAlive()方法返回false。

常见问题

一个正在运行的线程在死亡状态之前, 是不能再给它分配实体的。 原因: 线程只能引用最后分配的实体,之前的实体就会成为垃圾, 并且不会被垃圾收集器收集。

Thread thread = new Thread(target);
thread.start();

这时候如果再次分配实体

thread = new Thread(target)

那么,先前的实体就会成为垃圾, JVM认为它是一个垃圾, 但是不敢将其回收, 原因: 如果垃圾正在运行,突然释放可能会引起错误或者设备毁坏。

currentThread()

currentThread()方法是Thread类中的类方法, 可以使用类名调用,该方法返回当前正在使用CPU资源的线程

interrupt方法

当一个线程调用interrupt()方法时,该线程的中断标志位将被设置为true,但并不会立即停止线程的执行。在线程执行代码中,可以通过检查中断标志位来判断线程是否被中断,并根据需要进行相应的处理。

当一个线程处于阻塞状态时(如等待、睡眠或者等待锁)如果它的中断标志位被设置为true,则该线程将抛出一个InterruptedException异常,从而提前结束阻塞状态回到可执行状态。但是,如果线程没有处于阻塞状态,那么设置中断标志位将不会产生任何效果,线程仍然会继续执行。

举一个例子:

有三个线程, 小明,小红和老师,他们都在一个教室里, 小明正在睡觉,而小红一直在处于听课状态;当老师叫上课后,小明被惊醒。

具体代码如下:

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Classroom room = new Classroom();
        room.ming.start();
        room.hong.start();
        room.teacher.start();
    }
}
class Classroom implements Runnable{
    Thread ming, hong, teacher;

    Classroom(){
        this.ming = new Thread(this);
        this.hong = new Thread(this);
        this.teacher = new Thread(this);
        ming.setName("小明");
        hong.setName("小红");
    }
    @Override
    public void run() {
        Thread thread = Thread.currentThread();
        if(thread == ming){
            System.out.println("老子在睡觉");
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                System.out.println("我被吵醒了");
            }
            System.out.println("假装听课");
        } else if (thread == hong) {
            System.out.println("我一直在听课");
//            while (!hong.isInterrupted()){
//                System.out.println("我还没被打断");
//            }
//            System.out.println("被打断了");
        } else if (thread == teacher) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("上课了");
            ming.interrupt();
            System.out.println("hong前的interrupted值:" + hong.isInterrupted());
            hong.interrupt();
            System.out.println("hong后的interrupted值:" + hong.isInterrupted());
        }
    }
}

输出: 老子在睡觉 我一直在听课 上课了 我被吵醒了 假装听课 hong前的interrupted值:false hong后的interrupted值:true

可以看出, 当处于sleep状态时,调用interrupt()方法会抛出InterruptedException。

当不处于阻塞状态时,对其调用interrupt方法只会改变它里面的interrupted属性的值。