7.线程常用方法

本文详细介绍了Java中wait、notify、notifyAll方法用于线程同步的基本原理和用法,展示了它们如何控制线程的执行顺序和状态转换。同时,探讨了sleep方法在保持锁的同时让线程休眠以及join方法如何使得线程等待其他线程执行完毕后再继续执行的机制。通过示例代码,解释了这些方法在实际编程中的应用。
摘要由CSDN通过智能技术生成

1.wait、notify和notifyAll
(1).wait
作用:让线程让出CPU执行权,进入等待状态(释放锁),在满足的特定条件下再被唤醒。
前提:想要调用wait( )方法进行线程等待,必须要取得这个锁对象的控制权。
唤醒条件:另一个线程调用了这个对象的notify()方法且刚好被唤醒的是本线程,或者另一个线程调用了这个对象的notifyAll()方法。
参数:如果传入的时间参数值为0就是永久等待,不为0时,过了wait()方法设置的超时时间,该线程会自动苏醒进入RUNNABLE状态。
注意:wait释放锁只会释放当前这把锁,即哪个对象调用了wait,就释放那个对象的锁。

(2).notify
作用:随机唤醒一个处于等待状态的线程。

(3).notifyAll
作用:唤醒所有处于等待状态的线程。
注意:wait、notify、notifyAll必须在synchronized关键字修饰的方法或者代码块中,否则会抛出异常。

(4).展示wait和notify的基本用法

/**
 * 描述:展示wait和notify的基本用法
 * 1.研究代码执行顺序
 * 2.证明wait的作用是让线程进入等待状态,释放锁
 */
public class WaitAndNotify {
    public static Object object = new Object();

    static class Thread0 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                System.out.println(Thread.currentThread().getName() + "开始执行");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程" + Thread.currentThread().getName() + "获得了synchronized锁");
            }
        }
    }

    static class Thread1 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                object.notify();
                System.out.println("线程" + Thread.currentThread().getName() + "调用了notify()方法");
            }
        }
    }


    public static void main(String[] args) {
        Thread0 thread0 = new Thread0();
        Thread1 thread1 = new Thread1();
        thread0.start();
        //保证thread0的wait()方法先于thread1的notify()方法执行(thread0先于thread1启动)
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread1.start();
    }
}

代码运行结果如下所示。

线程Thread-0开始执行
线程Thread-1调用了notify()方法
线程Thread-0获得了synchronized

(5).展示notify,notifyAll两种方式唤醒

/**
 * 描述:展示notify, notifyAll两种方式唤醒。
 * 该案列中有3个线程,线程1和线程2首先被阻塞,线程3唤醒它们。
 */
public class WaitNotifyAll implements Runnable {
    private static final Object object = new Object();

    @Override
    public void run() {
        synchronized (object) {
            System.out.println("线程" + Thread.currentThread().getName() + "获得了synchronized锁");
            try {
                object.wait();
                System.out.println("线程" + Thread.currentThread().getName() + "运行结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Runnable r = new WaitNotifyAll();
        Thread thread0 = new Thread(r);
        Thread thread1 = new Thread(r);
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (object) {
                    object.notifyAll();
                    System.out.println("线程2唤醒线程0和1");
                }
            }
        });
        thread0.start();
        thread1.start();
        Thread.sleep(200);
        thread2.start();
    }
}

代码运行结果如下所示,其中,第一行和第二行输出顺序可以互换,第四行和第五行输出顺序也可以互换,原因是Thread-0和Thread-1公平竞争CPU资源,哪一个先被分配到CPU执行权,哪一个就先运行。

线程Thread-0获得了synchronized锁
线程Thread-1获得了synchronized锁
线程2唤醒线程01
线程Thread-1运行结束
线程Thread-0运行结束

(6).证明wait只释放当前的那把锁

/**
 * 描述:证明wait只释放当前的那把锁。
 */
public class WaitNotifyReleaseOwnMonitor {
    private static volatile Object resourceA = new Object();

    private static volatile Object resourceB = new Object();

    public static void main(String[] args) {
        Thread thread0 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resourceA) {
                    System.out.println("线程Thread0获得resourceA这把锁");
                    synchronized (resourceB) {
                        System.out.println("线程Thread0获取resourceB这把锁");
                        try {
                            System.out.println("线程Thread0释放resourceA这把锁");
                            resourceA.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (resourceA) {
                    System.out.println("线程Thread1获得resourceA这把锁");
                    System.out.println("线程Thread1尝试获得resourceB这把锁");
                    synchronized (resourceB) {
                        System.out.println("线程Thread1获得resourceB这把锁");
                    }
                }
            }
        });

        thread0.start();
        thread1.start();
    }
}

代码运行结果如下所示。

线程Thread0获得resourceA这把锁
线程Thread0获取resourceB这把锁
线程Thread0释放resourceA这把锁
线程Thread1获得resourceA这把锁
线程Thread1尝试获得resourceB这把锁

(7).两个线程交替打印0~100的奇偶数

public class PrintOddEven implements Runnable {
    private int number = 1;

    private boolean flag = true;

    private Object object = new Object();

    @Override
    public void run() {
        if (flag) {
            synchronized (object) {
                while (number <= 100) {
                    if (number % 2 == 1) {
                        System.out.println(Thread.currentThread().getName() + "输出" + number++);
                    }
                    try {
                        object.wait(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        } else {
            synchronized (object) {
                while (number <= 100) {
                    if (number % 2 == 0) {
                        System.out.println(Thread.currentThread().getName() + "输出" + number++);
                    }
                    try {
                        object.wait(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        PrintOddEven printOddEven = new PrintOddEven();
        Thread thread0 = new Thread(printOddEven);
        thread0.start();
        //保证线程thread0先打印奇数
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        printOddEven.flag = false;
        Thread thread1 = new Thread(printOddEven);
        thread1.start();
    }
}

代码运行结果如下所示。

Thread-0输出1
Thread-1输出2
Thread-0输出3
......
Thread-1输出98
Thread-0输出99
Thread-1输出100

2.sleep
(1).简介
作用是让出CPU执行权,进入等待状态(不释放锁),等sleep休眠结束后,才释放锁。休眠期间线程如果被中断,则会抛出异常并清除中断状态。

(2).展示线程sleep时,不释放synchronized锁

/**
 * 描述:展示线程sleep的时,不释放synchronized锁。
 */
public class NotReleaseSynchronized implements Runnable {
    public static void main(String[] args) {
        NotReleaseSynchronized notReleaseSynchronized = new NotReleaseSynchronized();
        Thread thread0 = new Thread(notReleaseSynchronized);
        Thread thread1 = new Thread(notReleaseSynchronized);
        thread0.start();
        thread1.start();
    }

    @Override
    public void run() {
        syn();
    }

    private synchronized void syn() {
        System.out.println("线程" + Thread.currentThread().getName() + "获得了锁");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程" + Thread.currentThread().getName() + "退出了同步方法");
    }
}

代码运行结果如下所示,其中第一、二行输出顺序可以和第三、四行输出顺序整体互换,原因是thread0和thread1公平竞争CPU资源,哪一个先被分配到CPU执行权,哪一个就先运行。

线程Thread-0获得了锁
线程Thread-0退出了同步方法
线程Thread-1获得了锁
线程Thread-1退出了同步方法

(3).演示sleep不释放lock锁

/**
 * 描述:演示sleep不释放lock锁(lock本身就需要手动释放)。
 */
public class NotReleaseLock implements Runnable {
    private static final Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        NotReleaseLock notReleaseLock = new NotReleaseLock();
        Thread thread0 = new Thread(notReleaseLock);
        Thread thread1 = new Thread(notReleaseLock);
        thread0.start();
        thread1.start();
    }

    @Override
    public void run() {
        lock.lock();
        System.out.println("线程" + Thread.currentThread().getName() + "获取了lock锁");
        try {
            Thread.sleep(1000);
            System.out.println("线程" + Thread.currentThread().getName() + "已被唤醒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

代码运行结果如下所示,其中第一、二行输出顺序可以和第三、四行输出顺序整体互换,原因是thread0和thread1公平竞争CPU资源,哪一个先被分配到CPU执行权,哪一个就先运行。

线程Thread-0获取了lock锁
线程Thread-0已被唤醒
线程Thread-1获取了lock锁
线程Thread-1已被唤醒

3.join
(1).简介
当正在运行的线程调用了某个线程的join方法时,调用者进入等待状态,当该线程运行完毕后调用者进入RUNNABLE状态。当某个线程调用了join方法后,该线程会抢占到CPU资源,不会再释放,直到线程执行完毕。

(2).演示join可以改变语句执行顺序的作用

/**
 * 描述:演示join可以改变语句执行顺序的作用。
 */
public class Join {
    public static void main(String[] args) throws InterruptedException {
        Thread thread0 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                	//保证线程任务在线程启动后不会快速执行完毕,使join()方法奏效
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "执行完毕");
            }
        });

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                	//保证线程任务在线程启动后不会快速执行完毕,使join()方法奏效
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "执行完毕");
            }
        });

        thread0.start();
        thread1.start();
        System.out.println("开始等待子线程运行完毕");
        thread0.join();
        thread1.join();
        System.out.println("所有子线程执行完毕");
    }
}

代码运行结果如下所示,其中第二行和第三行语句可以相互调换顺序,原因是thread0和thread1公平竞争CPU资源,哪一个先被分配到CPU执行权,哪一个就先运行。

开始等待子线程运行完毕
Thread-0执行完毕
Thread-1执行完毕
所有子线程执行完毕

(3).观察线程join前后状态的对比

/**
 * 描述:观察线程join前后状态的对比。
 */
public class JoinThreadState {
    public static void main(String[] args) throws InterruptedException {
        Thread mainThread = Thread.currentThread();
        Thread thread0 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                    System.out.println(mainThread.getState());
                    System.out.println("子线程运行结束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread0.start();
        System.out.println("等待子线程运行完毕");
        thread0.join();
        System.out.println("子线程运行完毕");
    }
}

代码运行结果如下所示。

等待子线程运行完毕
WAITING
子线程运行结束
子线程运行完毕

(4).join源码解析

public class Thread implements Runnable {
	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;
            }
        }
    }
}

在A线程中调用B线程的join()方法,也就是B线程充当了这把锁,调用者A线程被挂起(谁调用wait谁休眠),当B线程还活着,就一直wait,只有当B线程执行完了,A才会被唤醒。因此易推测出当B线程执行完毕后会使用notify方法唤醒A线程,不然A线程就会一直处于等待状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值