Java多线程——yield、notify、notifyAll、sleep、join、wait

wait ,notify / notifyAll

源码

public final void wait() throws InterruptedException {
    wait(0);
}

Object.wait(long)要跟Object.notify()/notifyAll()搭配使用。

wait 与 notify/notifyAll 方法必须在synchronized 同步代码块中使用,即要先对调用对象加锁,不放在synchronized 中则会在program runtime时扔出“java.lang.IllegalMonitorStateException”异常。

如果指定了wait的时间,到时间会自动唤醒;否则就需要 notify / notifyAll 去唤醒。

yield

yield()方法作用是放弃当前CPU资源,让给其他线程去使用,但是放弃时间不确定。

Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁,由运行状态变为就绪状态, 但yield()从未导致线程转到等待/睡眠/阻塞状态,让OS再次选择线程。

作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。该方法与sleep()类似,只是不能由用户指定暂停多长时间。

join

thread.join()/thread.join(long millis),假如 当前线程里调用其它线程 t 的join方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁。线程t执行完毕或者millis时间到,当前线程一般情况下进入RUNNABLE状态,也有可能进入BLOCKED状态(因为join是基于wait实现的)。

join的核心源码是这个:

if (millis == 0) {
    while (isAlive()) {
        wait(0);
    }
}

就是说调用join方法的线程,其他线程为其让步,让它先执行完才能执行,阻塞主线程,直到被唤醒。

demo:

public class ThreadMethodTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread1 = new MyThread("Thread 1");
        MyThread myThread2 = new MyThread("Thread 2");
        myThread1.start();
        myThread1.join();

        myThread2.start();
        myThread2.join();


        System.out.println(Thread.currentThread().getName()+ " 线程正在运行");
    }

    static class MyThread extends Thread{
        MyThread(String name){
           super(name);
        }
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" 线程正在运行");
        }
    }
}

输出:

Thread 1 线程正在运行
Thread 2 线程正在运行
main 线程正在运行

myThread1.start() 后 再 myThread1.join() 这样的话,myThread1执行完才会执行 myThread2.start()。

如果改成:

public class ThreadMethodTest {
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread1 = new MyThread("Thread 1");
        MyThread myThread2 = new MyThread("Thread 2");
        myThread1.start();
        myThread2.start();

        myThread2.join();
        myThread1.join();

        System.out.println(Thread.currentThread().getName()+ " 线程正在运行");
    }

    static class MyThread extends Thread{
        MyThread(String name){
           super(name);
        }
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+" 线程正在运行");
        }
    }
}

输出:

Thread 1 线程正在运行
Thread 2 线程正在运行
main 线程正在运行

main 线程必须等待,但是 Thread 1、Thread 2 都已经 start()了,调用join的先后顺序不影响,都是并行的。

sleep

Thread.sleep(long millis),一定是当前线程调用此方法,当前线程进入TIMED_WAITING状态,但不释放对象锁,释放CPU,millis后线程自动苏醒进入就绪状态。

作用:给其它线程执行机会的最佳方式。

有时候会使用sleep(0)使CPU重新发起一次调度。

wait 和 sleep的区别

wait()和sleep()都可以通过interrupt()方法立即 打断线程的暂停状态 ,从而使线程立刻抛出InterruptedException。

wait() 是Object类 的方法,sleep()是 Thread 类的方法。

wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。

都可以指定阻塞指定秒数,并返回。

sleep方法没有释放锁,而wait方法释放了锁。

守护线程

使用多线程的时候,如果有一个子线程没有结束,jvm进程就不会退出,为了确保所有线程都能够结束,就引入了守护线程。

用户线程:Java虚拟机在它所有非守护线程已经离开后自动离开。
守护线程:守护线程则是用来服务用户线程的,如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去。
setDaemon(boolean on)方法可以方便的设置线程的Daemon模式,true为Daemon模式,false为User模式。

demo:

public class DaemonDemo {
    public static void main(String[] args) {
        Thread daemonThread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        System.out.println(Thread.currentThread().getName() + " 子线程执行中....");
                        Thread.sleep(1*1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        System.out.println(Thread.currentThread().getName() + " finally 语句块");
                    }

                }
            }
        }, "daemonThread1");

        Thread daemonThread2 = new Thread(new Runnable() {
            int i = 2;

            @Override
            public void run() {
                while (i-- > 0) {
                    System.out.println(Thread.currentThread().getName() + " 子线程执行中....");
                }
            }
        }, "daemonThread2");

        daemonThread1.setDaemon(true); //daemon要先于start前执行,否则会报  IllegalThreadStateException
        daemonThread1.start();
        daemonThread2.start();

        try {
            Thread.sleep(2 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("daemonThread1 状态"+daemonThread1.isAlive());
        System.out.println("daemonThread2 状态"+daemonThread2.isAlive());

    }
}

输出:

daemonThread1 子线程执行中....
daemonThread2 子线程执行中....
daemonThread2 子线程执行中....
daemonThread1 finally 语句块
daemonThread1 子线程执行中....
daemonThread1 finally 语句块
daemonThread1 子线程执行中....
daemonThread1 状态true
daemonThread2 状态false

上述有2个子线程daemonThread1、daemonThread2,我给daemonThread1设置为守护线程,一直是无限循环,当两个线程执行的时候,随着daemonThread2执行结束、还有main线程结束,之后显示daemonThread1还是alive的,但是因为daemonThread1设置为守护线程,有且只有守护线程的时候,jvm退出。

结论:

当线程只剩下守护线程的时候,JVM就会退出;补充一点如果还有其他的任意一个用户线程还在,JVM就不会退出。.

守护线程结束的时候,不会执行finally的语句块。

setDaemon要先于start()前执行,否则会报 IllegalThreadStateException。

参考:

https://blog.csdn.net/pange1991/article/details/53860651

https://www.cnblogs.com/baizhanshi/p/8289202.html

你可能感兴趣的:(JAVA多线程,yield,sleep,join,wait,多线程)