线程基础

63 阅读18分钟

线程基础

image.png

进程与线程的关系

进程

  • 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须将指令加载至CPU,数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程本质上来说就是用来加载指令、管理内存、管理IO的。
  • 当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。
  • 进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程(例如记事本、画图、浏览器等),也有的程序只能启动一个实例进程(例如网易云音乐、360 安全卫士等)。

线程

  • 一个进程之内可以分为一到多个线程。
  • 一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给 CPU执行。
  • 在Java中,线程作为最小调度单位,进程作为资源分配的最小单位。在windows中进程是不活动的,只是作为线程的容器。

二者对比

  • 进程基本上相互独立的,而线程存在于进程内,是进程的一个子集。
  • 进程拥有共享的资源,如内存空间等,供其内部的线程共享。
  • 进程间通信较为复杂:
    • 同一台计算机的进程通信称为IPC(Inter-process communication)。
    • 不同计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如 HTTP。
  • 线程通信相对简单,因为它们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量。
  • 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低。

线程上下文切换

由于以下一些原因导致cpu不再执行当前的线程,转而执行另一个线程的代码。

  • 线程的CPU时间片用完。
  • 垃圾回收。
  • 有更高优先级的线程需要运行。
  • 线程自己调用 slepp, yield, wait, join, park, synchronized, lock等方法。
  • Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java中对应的概念就是程序计数器,它的作用是记住下一条jvm指令的执行地址,是线程私有的。

并发与并行的关系

并发

  • 单核CPU下,线程实际还是串行执行的。操作系统中有一个组件叫做任务调度器,将CPU的时间片(windows下时间片最小约15毫秒) 分给不同线程使用,只是由于CPU在线程间(时间片很短)的切换非常快。人类感觉是同时运行的。总结一句话就是:微观串行,宏观并行
  • 一般会将这种线程轮流使用CPU的做法称为并发(concurrent)。 image.png

并行

多核CPU下,每个核(core) 都可以调度运行线程,这时候线程可以是并行的。

image.png

并发 + 并行

  • 执行任务的数量恰好等于CPU核心的数量,是一种理想状态。但是在实际场景中,处于运行状态的任务是非常多的,尤其是电脑和手机,开机就几十个任务,而 CPU 往往只有 4 核、8 核或者 16 核,远低于任务的数量,这个时候就会同时存在并发和并行两种情况:所有核心都要并行工作,并且每个核心还要并发工作。
  • 例如一个双核 CPU 要执行四个任务,它的工作状态如下图所示: image.png -每个核心并发执行两个任务,两个核心并行的话就能执行四个任务。当然也可以一个核心执行一个任务,另一个核心并发执行三个任务,这跟操作系统的分配方式,以及每个任务的工作状态有关系。

总结

  • 并发针对单核 CPU 而言,它指的是 CPU 交替执行不同任务的能力;并行针对多核 CPU 而言,它指的是多个核心同时执行多个任务的能力。
  • 单核 CPU 只能并发,无法并行;换句话说,并行只可能发生在多核 CPU 中。
  • 在多核 CPU 中,并发和并行一般都会同时存在,它们都是提高 CPU 处理任务能力的重要手段。

创建运行线程

继承Thread类

//创建线程
Thread thread = new Thread(() -> logger.debug("running..."));
//启动线程
thread.start();

实现Runable接口

//创建任务
Runnable runnable = () -> {
    logger.debug("running...");
}
//任务交给线程
Thread thread = new Thread(runnable);
//启动线程
thread.start();

实现Callable接口

//创建任务
FutureTask<Object> futureTask = new FutureTask<Object>(() -> {
    logger.debug("running...");
    //线程结束后将执行结果返回
    return 100;
});
//创建线程
Thread thread = new Thread(futureTask);
//启动线程
thread.start();
//线程结束后的执行结果
System.out.println(futureTask.get());

线程状态

操作系统

从操作系统方面来说,线程有5种状态:

  • 初始状态:仅在语言层面创建了线程对象,还未与操作系统关联。
  • 可运行状态:指的是线程已被创建(与操作系统相关联),可以有CPU调度并执行。
  • 运行状态:指获取了CPU时间片运行中的状态。但是当CPU时间片用完后,会从运行状态转换至可运行状态,会导致线程上下文的切换。
  • 阻塞状态:如果调用了阻塞API,如BIO读写文件,这时候线程实际上不会用到CPU,会导致线程上下文切换,进入阻塞状态。等BIO操作完毕,会由操作系统唤醒阻塞的线程,切换至可运行状态。与可运行状态的区别是,对于阻塞状态的线程来说只要他们一直不被唤醒,调度器就一直不会考虑调度它们。
  • 终止状态:表示线程已经执行结束,不会再转换为其它状态。

Java:

从java方面来说,线程有6种状态:

public enum State {
    /**
     * 线程刚被创建,但未调用start方法。
     */
    NEW,

    /**
     * runnable当调用start()方法之后,JavaApi层面的Runnable状态涵盖了操作系统层面的可运行状态,运行      * 状态以及阻塞状态,由于BIO导致的线程阻塞,在java里面无法区分,所以仍然认为是可运行的。
     */
    RUNNABLE,

    /**
     * 阻塞状态,表示线程阻塞于锁
     */
    BLOCKED,

    /**
     * WAITING会进入一个无时间限制的等,一般来说,WAITING的线程正式在等待一些特殊的事件,比如,通过      * wait()方法等待的线程在等待notify()方法,而通过join()方法等待的线程则会等待目标线程的终止。一      * 旦等到期望的事件,线程就会再次进入RUNNABLE运行状态。
     */
    WAITING,

    /**
     * 超时等待状态,可以指定等待时间自己返回
     */
    TIMED_WAITING,

    /**
     * 终止状态,表示当前线程已经执行完毕
     */
    TERMINATED;
}

NEW 状态 代码示例

public class ThreadStateNewExample {

    private static Object waiter = new Object();
    
    public static void main(String[] args){
        Runnable waiting = () -> {
            try{
                waiter.wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        };
        Thread whoWillWait = new Thread(waiting);
        System.out.printf(whoWillWait.getState().toString());
    }
}

//运行结果
NEW
Process finished with exit code 0

RUNNABLE 状态 代码示例

public class ThreadStateRunnableExample {
    private static boolean flag = true;

    public static void main(String[] args){
        Runnable waiting = () -> {
            //让程序空转,保持线程是runnable状态
                do { }while (flag);
        };
        Thread thread = new Thread(waiting);
        thread.start();
        try {
            //主线程先睡3秒,让子线程先跑起来,然后输出线程状态
            Thread.sleep(3000);
            System.out.printf(thread.getState().toString());
            //更改标志位,让子线程结束循环
            flag = false;
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.exit(1);
    }
}

//运行结果
RUNNABLE
Process finished with exit code 1

BLOCKED 状态 代码示例

public class ThreadStateBlockExample {

    private static boolean LOCK_FLAG = true;

    public static void main(String[] args){
        Runnable locker = ThreadStateBlockExample::locker;

        Thread whoWillLockOthers = new Thread(locker);
        /**
         * 启动whoWillLockOthers线程,主线程睡2秒让子线程先运行
         * 此时whoWillLockOthers获得锁,这时候其他线程需要等待
         */
        whoWillLockOthers.start();
        try {
            Thread.sleep(2000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        Thread whoWillBeLocked = new Thread(locker);
        /**
         * 启动whoWillBeLocked线程,主线程睡2秒让子线程先运行
         * 因为locker方法是个死循环,所以whoWillBeLocked线程永远拿不到锁,就会进入BLOCKED状态
         */
        whoWillBeLocked.start();
        try {
            Thread.sleep(2000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        System.out.printf("whoWillBeLocked 当前状态="+whoWillBeLocked.getState().toString()+"");
        System.exit(1);
    }


    private static synchronized void locker(){
        do {

        }while (LOCK_FLAG);
    }
}

//执行结果
whoWillBeLocked 当前状态为=BLOCKED
Process finished with exit code -1

WAITING 状态 代码示例

//代表线程正在等待中。一个线程如果调用下列方法,会导致线程状态变为`WAITING`:
//-   `Object.wait with no timeout`
//-   `Thread.join with no timeout`
//-   `LockSupport.park`
//举个栗子:`ThreadA`调用了`Object.wait()`方法,此时`ThreadA`状态为`WAITING`。`ThreadA`会等待其他//的线程调用 `Object.notify()`或`Object.notifyAll`才会被唤醒,继续执行后面的逻辑。
//注意!!!调用`wait()`和`notify()`此类方法必须先获得`Object`的锁。

public class ThreadStateWaitingExample {
    private static final Object LOCKER = new Object();

    public static void main(String[] args) {

        Runnable waiting = () -> {
            System.out.println("whoWillWait 开始等待 whoWillNotify");
            waiting();
            System.out.println("whoWillWait 等到了 whoWillNotify 的通知");
        };
        //创建一个线程调用waiter.wait()方法,让whoWillWait线程进入waiting状态
        Thread whoWillWait = new Thread(waiting);
        whoWillWait.start();
        //主线程先睡2秒,让whoWillWait先执行
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("whoWillWait当前的线程状态=" + whoWillWait.getState().toString());


        Runnable notify = () -> {
            System.out.println("whoWillNotify 准备通知 whoWillWait");
            notifying();
        };
        //创建一个线程调用waiter.notify()方法,唤醒whoWillWait
        Thread whoWillNotify = new Thread(notify);
        whoWillNotify.start();
        //主线程先睡2秒,让whoWillNotify先执行
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("唤醒后,whoWillWait当前的线程状态=" + whoWillWait.getState().toString());
        System.exit(1);
    }

    private static void waiting() {
        synchronized (LOCKER) {
            try {
                LOCKER.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    private static void notifying() {
        synchronized (LOCKER) {
            LOCKER.notify();
            System.out.println("whoWillNotify 已经通知,即将离开同步代码块");
        }
    }
}

//执行结果
whoWillWait 开始等待 whoWillNotify
whoWillWait当前的线程状态=WAITING
whoWillNotify 准备通知 whoWillWait
whoWillNotify 已经通知,即将离开同步代码块
whoWillWait 等到了 whoWillNotify 的通知
唤醒后,whoWillWait当前的线程状态=TERMINATED
Process finished with exit code 1

TIMED_WAITING 状态 代码示例

//线程正在等待其他线程的操作,直到超过指定的超时时间。线程在调用以下方法是会将状态改变为TIMED_WAITING状态:
//-   Thread.sleep
//-   Object.wait with timeout
//-   Thread.join with timeout
//-   LockSupport.parkNanos
//-   LockSupport.parkUntil
public class ThreadStateTimedWaitingExample {
    private static final Object LOCKER = new Object();

    public static void main(String[] args) {

        Runnable waiting = () -> {
            System.out.println("whoWillWait 开始等待 2秒钟");
            waiting();
            System.out.println("whoWillWait 等待结束");
        };
        //创建一个线程调用waiter.wait()方法,让whoWillWait线程进入waiting状态
        Thread whoWillWait = new Thread(waiting);
        whoWillWait.start();
        //主线程先睡1秒,让whoWillWait先执行
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("whoWillWait当前的线程状态=" + whoWillWait.getState().toString());


        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("whoWillWait当前的线程状态=" + whoWillWait.getState().toString());
        System.exit(1);
    }

    private static void waiting() {
        synchronized (LOCKER) {
            try {
                LOCKER.wait(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
    
}

//执行结果
whoWillWait 开始等待 2秒钟
whoWillWait当前的线程状态=TIMED_WAITING
whoWillWait 等待结束
whoWillWait当前的线程状态=TERMINATED

Process finished with exit code 1

TERMINATED 状态 代码示例

public class ThreadStateTerminatedExample {

    public static void main(String[] args) {

        Runnable waiting = () -> {
            System.out.println("随便执行一下,然后线程就会变为Terminated");
        };
        Thread terminate = new Thread(waiting);
        terminate.start();
        //主线程先睡1秒,让terminate先执行,一秒钟足够terminate执行完毕,然后线程就结束了
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("terminate当前的线程状态=" + terminate.getState().toString());

        System.exit(1);
    }

}

//执行结果
随便执行一下,然后线程就会变为Terminated
terminate当前的线程状态=TERMINATED
Process finished with exit code 1

线程在某个时间点只会拥有一种状态,这些状态都是虚拟机的线程状态,并不是操作系统的线程状态,这是两个概念,不要混淆。

常用方法

currentThread方法

Thread.currentThread()方法可以返回的是当前正在被执行的线程

run方法

run方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行> >路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达> >到写线程的目的。

案例:

@Slf4j
public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread("线程一"){
            @Override
            public void run() {
                log.info("running...");
            }
        };
        log.info("main线程 running...");
        thread.run();
    }
}

//执行结果:
//18:04:08.933 [main] INFO com.dqxh.controller.Test - main线程 running...
//18:04:08.935 [main] INFO com.dqxh.controller.Test - running...

start方法

用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行run方法,这里方法run称为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随即终止。

getState方法

获取指定线程的当前状态。

案例:

@Slf4j
public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread("线程一"){
            @Override
            public void run() {
                log.info("running...");
            }
        };
        System.out.println(thread.getState());
        thread.start();
        System.out.println(thread.getState());
    }
}

//执行结果:
//18:15:02.820 [main] INFO com.dqxh.controller.Test - main线程 running...
//NEW
//RUNNABLE
//18:15:02.822 [线程一] INFO com.dqxh.controller.Test - running...

sleep方法

让当前线程休眠,使其放弃CPU时间片的使用,调用sleep方法会让当前线程从Running进入Timed Waiting(阻塞)状态,其它线程可以使用interrupt方法打断正在休眠的线程,这时sleep方法会抛出InterruptedException,睡眠之后的线程未必会立刻执行,建议使用TimeUnit的sleep代替Thread的sleep来获得更好的可读性。

案例:

@Slf4j
public class Test {
    public static void main(String[] args) throws InterruptedException {

        Thread thread = new Thread("线程一") {
            @Override
            public void run() {
                try {
                    //Thread.sleep(2000);
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    log.info(e.getMessage());
                }
            }
        };
        thread.start();
        //让主线程休眠
        //Thread.sleep(1000);
        TimeUnit.SECONDS.sleep(2);
        System.out.println(thread.getName() +"状态:"+ thread.getState());
    }
}
//执行结果:
//线程一状态:TIMED_WAITING

yield方法

调用yield会让当前线程从Running进入Runnable(就绪)状态,然后调度执行其它线程,具体实现比较依赖操作系统的任务调度器。线程执行sleep方法后会转入阻塞状态,所以执行sleep()方法的线程在指定的时间内肯定不会被执行,而yield方法只是使当前线程重新回到就绪状态,所以执行yield()方法的线程有可能在进入到可执行状态后马上又被执行。

案例:

@Slf4j
public class Test {
    public static void main(String[] args) throws InterruptedException {
        Runnable task1 = ()->{
            int count = 0;
            for (;;){
                log.info("线程一" + count++);
            }
        };

        Runnable task2 = ()->{
            int count = 0;
            for (;;){
                Thread.yield();
                log.info("-------------->" + count++);
            }
        };
        Thread thread1 = new Thread(task1 ,"线程一");
        Thread thread2 = new Thread(task2 , "线程二");
        thread1.start();
        thread2.start();
    }
}

//执行结果:
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381879
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381880
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381881
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381882
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381883
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381884
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381885
//20:29:13.660 [线程二] INFO com.dqxh.controller.Test - =========>222094
//20:29:13.660 [线程二] INFO com.dqxh.controller.Test - =========>222095
//20:29:13.660 [线程二] INFO com.dqxh.controller.Test - =========>222096
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381886
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381887
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381888
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381889
//20:29:13.660 [线程一] INFO com.dqxh.controller.Test - --->381890

setPriority方法

调用setPriority方法,根据参数的不同可以设置不同的线程优先级,但是它仅仅只是提示哪一个线程该优先执行,调度器是完全可以忽略它。如果CPU比较忙,那么优先级高的线程会获得更多的时间片,但是CPU空闲时,优先级几乎没用。

案例:

@Slf4j
public class Test {
    public static void main(String[] args) throws InterruptedException {
        Runnable task1 = ()->{
            int count = 0;
            for (;;){
                log.info("--->"+count++);
            }
        };

        Runnable task2 = ()->{
            int count = 0;
            for (;;){
                log.info("=========>" + count++);
            }
        };
        Thread thread1 = new Thread(task1 ,"线程一");
        Thread thread2 = new Thread(task2 , "线程二");
        //线程一设置最小优先级
        thread1.setPriority(Thread.MIN_PRIORITY);
        //线程二设置最大优先级
        thread2.setPriority(Thread.MAX_PRIORITY);
        thread1.start();
        thread2.start();
    }
}

join方法

join方法就是通过wait方法实现的,让主线程处于WAITING(等待)状态,主要作用就是同步,它可以使得线程之间的并行执行变为串行执行。

案例:

@Slf4j
public class Test {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                log.info("线程一执行完毕!");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, "线程一");
        thread1.start();
        //程序在main线程中调用thread1线程的join方法,则main线程放弃cpu控制权
        //并返回t1线程继续执行直到线程t1执行完毕后main线程才有执行的机会。
        thread1.join();
        log.info("main线程执行完毕!");
    }
}
//执行结果:
//20:54:50.110 [线程一] INFO com.dqxh.controller.Test - 线程一执行完毕!
//20:54:50.112 [main] INFO com.dqxh.controller.Test - main线程执行完毕!

interrupt方法

在一个线程中调用另一个线程的interrupt()方法,即会设置那个线程的中断状态。该方法不能中断在运行中的线程,它只能改变中断状态而已。但是该方法打断sleep,wait,join状态下的线程后,会以异常InterruptedException的形式标识,线程的中断标志位会由true重置为false,因为线程为了处理异常已经重新处于就绪状态。

isInterrupted方法

该方法用于标记线程是否被打断。

interrupted方法

该方法用于标记线程是否被打断,并且会清除打断标记

案例:

//打断正常情况下的线程
@Slf4j
public class Test {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    log.info("thread1线程已经被打断");
                    break;
                }
                log.info("thread1线程正在运行中");
            }
        });
        thread1.start();
        //让main线程睡眠1s是因为让thread1线程进入休眠状态后,再进行打断,此时打断标记变为false。
        TimeUnit.SECONDS.sleep(1);
        thread1.interrupt();
        log.info("打断标记:{}", thread1.isInterrupted());
    }
}

//执行结果:
//23:25:41.244 [Thread-0] INFO com.wuke.controller.Test - thread1线程正在运行中
//23:25:41.244 [Thread-0] INFO com.wuke.controller.Test - thread1线程正在运行中
//23:25:41.244 [Thread-0] INFO com.wuke.controller.Test - thread1线程正在运行中
//23:25:41.244 [Thread-0] INFO com.wuke.controller.Test - thread1线程已经被打断
//23:25:41.244 [main] INFO com.wuke.controller.Test - 打断标记:true
//打断sleep/wait/join状态下的线程,其该线程状态依然为false
@Slf4j
public class Test {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        thread1.start();
        //让main线程睡眠1s是因为让thread1线程进入休眠状态后,再进行打断,此时打断标记变为false。
        TimeUnit.SECONDS.sleep(1);
        //main线程打断thread1线程
        thread1.interrupt();
        log.info("打断标记:{}" ,thread1.isInterrupted());
    }
}

//执行结果:
//Caused by: java.lang.InterruptedException: sleep interrupted
//	at java.lang.Thread.sleep(Native Method)
//	at java.lang.Thread.sleep(Thread.java:340)
//	at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
//	at com.wuke.controller.Test.lambda$main$0(Test.java:13)
//	... 1 more
//23:07:14.650 [main] INFO com.wuke.controller.Test - 打断标记:false

setDaemon

默认情况下Java进程需要等待所有线程运行结束,Java进程才会结束。但是有一种特殊的线程只要其它非守护线程运行结束了,它才会结束,即使守护线程的代码并没有执行完毕,也会被强制结束。 垃圾回收器就是一个守护线程 Tomcat中的Acceptor和Poller线程都是守护线程,所以Tomcat再收到shutdown命令后,不会等待它们处理完当前请求。

@Slf4j
public class Test {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(()->{
            while (true){
                log.info("守护线程执行中");
            }
        },"守护线程");
        //将thread1设置为守护线程
        thread1.setDaemon(true);
        thread1.start();
        log.info("main线程结束");
    }
}

//10:15:40.490 [main] INFO com.dqxh.controller.Test - main线程结束
//10:15:40.490 [守护线程] INFO com.dqxh.controller.Test - 守护线程执行中
//10:15:40.492 [守护线程] INFO com.dqxh.controller.Test - 守护线程执行中
//10:15:40.492 [守护线程] INFO com.dqxh.controller.Test - 守护线程执行中

过时方法 stop(),suspend(),resume()

stop():停止线程运行。
suspend():挂起(暂停线程)。
resume():恢复线程运行。

不推荐使用,这些方法已经过时,容易破坏同步代码块,造成线程死锁。

总结:

yield() 和 sleep() 的异同

  • 相同点:yield()方法和sleep()方法类似,也不会释放“锁”。
  • 不同点:yield()方法只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。 yield()方法只能使同优先级或者高优先级的线程得到执行机会,这也和sleep()方法不同。

join() 和 sleep() 的异同

  • 相同点:都可以实现等待
  • 不同点:由于join的内部实现是wait(),所以使用join()方法时会释放锁,那么其他线程就可以调用此线程的同步方法了。sleep()方法不释放锁,因此线程会一直等待下去,直到任务完成,才会释放锁。

sleep() 与 wait() 的异同

  • 相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
  • 不同点:
    1. 两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()
    2. 调用的要求不同:sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中
    3. 关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。
    4. 当调用某一对象的wait()方法后,会使当前线程暂停执行,并将当前线程放入对象等待池中,直到调用了 notify()方法后,将从对象等待池中移出任意一个线程并放入锁标志等待池中,只有锁标志等待池中的线程可以获取锁标志,它们随时准备争夺锁的拥有权。当调用了某个对象的notifyAll()方法,会将对象等待池中的所有线程都移动到该对象的锁标志等待池。