Java 多线程常用方法

406 阅读2分钟

多线程常见方法

sleep()

  1. 线程调用sleep()方法会使线程变为TIMED_WAITING阻塞状态。
  2. 其它线程可以调用interrupt()方法会打断正在睡眠线程,这时会抛出InterruptedException异常。
  3. 睡眠结束后的线程会进入RUNNABLE状态,不一定会立刻执行。

yield()

使线程从Running变为Runnable就绪状态,让出当前运行状态。此时调度器仍有可能调用此线程。

给调度程序的提示,表明当前线程愿意放弃其当前对处理器的使用。调度程序可以忽略这个提示。

setPriority(int newPriority)

设置线程的优先级(1~10),优先级高的被调度的可能性就越高.

/**
 * The minimum priority that a thread can have.
 */
public static final int MIN_PRIORITY = 1;

/**
 * The default priority that is assigned to a thread.
 */
public static final int NORM_PRIORITY = 5;

/**
 * The maximum priority that a thread can have.
 */
public static final int MAX_PRIORITY = 10;

wait()

  1. 线程进入WAITING状态,进入wait set等待,并让出获得的锁。
  2. 进入阻塞状态,不会占用CPU时间片。
  3. 需要利用synchronized获得线程的锁后才可以调用该方法,如不获得锁就调用该方法会throw IllegalMonitorStateException

示例代码

public class Main {
    private static final Object object = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            synchronized (object) {
                try {
                    System.out.println("进入wait!");
                    object.wait();
                    System.out.println("结束wait!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();

        Thread.sleep(1000);
        // 需要利用 synchronize 获得锁!!!
        synchronized (object) {
            object.notifyAll();
        }
    }
}

notify() notifyAll()

该方法会唤醒 wait set 中的线程,notify()会随机唤醒一个,notifyAll()会唤醒wait set中的所有线程。

join()

当线程A执行thread.join()后线程A进入TIMED_WAITTING状态。

线程A会等待thread线程终止之后才从超时方法中返回,继续运行。

同时还有join(long millis)join(long millis, int nanos)两个带超时方法,该方法会使线程A进入TIMED_WAITING状态,如果在给定的时间内thread线程没有终止,那么线程A将会从超时方法中返回,继续运行。

根据源码我们可以知道,join()的底层就是wait()

  1. 当传入millis为 0 时,join()方法会一直调用isAlive()方法判断线程是否存活,如果存活就一直等待。
  2. 当传入millis不为 0 时,当超出给出的超时时间时会break跳出循环。
// native method
public final native boolean isAlive();

public final synchronized void join(long millis) throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

interrupt()

中断可以理解为线程的一个标识属性,相当于其它线程通知该线程。有两种方法可以判断当前线程的打断状态:

  1. Thread.interrupted()当使用该方法后,会清楚打断标志,此时再获取打断标志会得到的false
  2. Thread.currentThread().isInterrupted()使用后不会改变打断标志

注意事项:

  1. 当打断处于sleep wait join状态的线程时会throw异常,并且会使打断状态false,此时可以在catch块中再次调用interrupted()方法设置打断标记并自行打断。
  2. 打断处于运行状态的线程或自行打断,此时并不会直接暂停被打断的线程。被打断的线程可以通过isInterrupted()方法获取线程的打断状态,再来决定接下来的操作。想比于被废弃的stop()方法,interrupt()可以在接下来中释放锁等资源,更加安全。

打断正常运行的线程

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("结束运行!");
                    break;
                }
            }
        });
        t.start();

        // 5 秒后打断 t 线程
        Thread.sleep(5000);
        t.interrupt();
    }
}

打断处于sleep wait join状态的线程

需注意在异常块中再次调用了interrup()方法。

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (true) {
                try {
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("结束运行!");
                        break;
                    }
                    System.out.println("运行!");
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt(); // 再次调用,使得打断标志为true
                    e.printStackTrace();
                }
            }
        });
        t.start();

        // 5 秒后打断 t 线程
        Thread.sleep(5000);
        t.interrupt();
    }
}

LockSupport.park() & LockSupport.unpark()

unpark()不会释放锁,可以在线程暂停前调用,也可以在暂停后调用。

如果在park()前使用,则运行到park()时会直接跳过。