3-5thread和object类中的重要方法详解

1 问题:

1为什么线程通信的方法wait(),notify(),notifyAll()被定位在object类里边,而sleep定义在thread类里?

2 用3中方式实现生产者模式

3 Java se 8 和Java 1.8 和jdk 8 是什么关系?

4 join和sleep和wait期间线程的状态分别是什么?为什么?

2 thread和object类中的重要方法详解

2.1 方法概览

在这里插入图片描述

2.2 wait,notify,notifyAll方法详解

2.2.1 作用

想要一个线程或多个线程暂时休息一下,等到后续需要的时候或者时机成熟的时候唤醒她。
控制线程休息,唤醒

2.2.1.1 阻塞阶段

使线程进行休息
执行wait的前提,获取monitor 锁

直到以下4种情况之一发生时,才会被唤醒

1 另一个线程调用这个对象的notify()方法且刚好被唤醒的是本线程。
2 另一个线程调用这个对象的notifyAll()方法。
3过了wait(time)规定的超时时间,如果传入0 就是永久。
4 线程自身调用了interrupt()

2.2.1.2唤醒阶段

1notify 只能唤醒单个正在等待monitor 的线程。唤醒的时候,如果有多个线程等待,他只会选取一个,具体选择哪一个,是任意的。
wait,notify都要在synchronize 保护的代码块或者方法种执行,如果在synchronize外边执行会抛出异常。
2notifyAll 所有等待的线程一次性唤醒。

2.2.1.2遇到中断

会抛出异常,并释放获取的monitor

2.2.2代码演示

2.2.2.1普通代码
package threadcoreknowledge.threadobjectclasscommonmethods;

/**
 * 描述:     展示wait和notify的基本用法 1. 研究代码执行顺序 2. 证明wait释放锁
 */
public class Wait {

    public static Object object = new Object();

    static class Thread1 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() + "获取到了锁。");
            }
        }
    }

    static class Thread2 extends Thread {

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

    public static void main(String[] args) throws InterruptedException {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.start();
        Thread.sleep(200);
        thread2.start();
    }
}

Thread.sleep(200); 为了保证顺序执行,让他休息200秒。保证thread1先执行。 thread1 执行,拿到了锁。 走到object.wait() ,吧锁释放掉了。thread2 即拿到了锁。 object.notify() 释放锁后,但不是立即释放锁,synchronized代码块还没有走完。 走出线程代码块以后,thread1 又拿到了锁,接着走下边的代码。
2.2.2.2 notify, notifyAll 代码展示
package threadcoreknowledge.threadobjectclasscommonmethods;

/**
 * 描述:     3个线程,线程1和线程2首先被阻塞,线程3唤醒它们。notify, notifyAll。 start先执行不代表线程先启动。
 */
public class WaitNotifyAll implements Runnable {

    private static final Object resourceA = new Object();


    public static void main(String[] args) throws InterruptedException {
        Runnable r = new WaitNotifyAll();
        Thread threadA = new Thread(r);
        Thread threadB = new Thread(r);
        Thread threadC = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (resourceA) {
                    resourceA.notifyAll();
//                    resourceA.notify();
                    System.out.println("ThreadC notified.");
                }
            }
        });
        threadA.start();
        threadB.start();
//        Thread.sleep(200);
        threadC.start();
    }
    @Override
    public void run() {
        synchronized (resourceA) {
            System.out.println(Thread.currentThread().getName()+" got resourceA lock.");
            try {
                System.out.println(Thread.currentThread().getName()+" waits to start.");
                resourceA.wait();
                System.out.println(Thread.currentThread().getName()+"'s waiting to end.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

线程a进入run 方法, resourceA.wait(); 线程a释放锁
线程b 拿到锁,进入run 方法。 resourceA.wait(); 线程b释放锁
线程c 拿到锁, resourceA.notifyAll(); 两个线程都被唤醒,所以继续进行下边的代码


若换成resourceA.notify()–?只有一个线程被唤醒,执行了下边的代码。
是程序卡在这了

2.3 手写生产者消费者设计模式

2.3.1为什么使用生产者消费者设计模式

有个模式解决相互等待的问题,不至于一个过快,一个过慢 。使消费方和生产方进行了解耦。可以达到顺畅的配合。

package threadcoreknowledge.threadobjectclasscommonmethods;

import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

/**
 * 描述:     用wait/notify来实现生产者消费者模式
 */
public class ProducerConsumerModel {
    public static void main(String[] args) {
        EventStorage eventStorage = new EventStorage();
        Producer producer = new Producer(eventStorage);
        Consumer consumer = new Consumer(eventStorage);
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

class Producer implements Runnable {

    private EventStorage storage;

    public Producer(
            EventStorage storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storage.put();
        }
    }
}

class Consumer implements Runnable {

    private EventStorage storage;

    public Consumer(
            EventStorage storage) {
        this.storage = storage;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storage.take();
        }
    }
}

class EventStorage {

    private int maxSize;
    private LinkedList<Date> storage;

    public EventStorage() {
        maxSize = 10;
        storage = new LinkedList<>();
    }

    public synchronized void put() {
        while (storage.size() == maxSize) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        storage.add(new Date());
        System.out.println("仓库里有了" + storage.size() + "个产品。");
        notify();
    }

    public synchronized void take() {
        while (storage.size() == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("拿到了" + storage.poll() + ",现在仓库还剩下" + storage.size());
        notify();
    }
}



2.4两个线程交替打印0~100 的奇偶数

一个线程全是偶数,一个线程全是奇数

package threadcoreknowledge.threadobjectclasscommonmethods;

/**
 * 描述:     两个线程交替打印0~100的奇偶数,用synchronized关键字实现
 */
public class WaitNotifyPrintOddEvenSyn {

    private static int count;

    private static final Object lock = new Object();

    //新建2个线程
    //1个只处理偶数,第二个只处理奇数(用位运算)
    //用synchronized来通信
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (count < 100) {
                    synchronized (lock) {
                        if ((count & 1) == 0) {
                            System.out.println(Thread.currentThread().getName() + ":" + count++);
                        }
                    }
                }
            }
        }, "偶数").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (count < 100) {
                    synchronized (lock) {
                        if ((count & 1) == 1) {
                            System.out.println(Thread.currentThread().getName() + ":" + count++);
                        }
                    }
                }
            }
        }, "奇数").start();
    }
}

package threadcoreknowledge.threadobjectclasscommonmethods;


/**
 * 描述:     两个线程交替打印0~100的奇偶数,用wait和notify
 */
public class WaitNotifyPrintOddEveWait {

    private static int count = 0;
    private static final Object lock = new Object();


    public static void main(String[] args) {
        new Thread(new TurningRunner(), "偶数").start();
        new Thread(new TurningRunner(), "奇数").start();
    }

    //1. 拿到锁,我们就打印
    //2. 打印完,唤醒其他线程,自己就休眠
    static class TurningRunner implements Runnable {

        @Override
        public void run() {
            while (count <= 100) {
                synchronized (lock) {
                    //拿到锁就打印
                    System.out.println(Thread.currentThread().getName() + ":" + count++);
                    lock.notify();
                    if (count <= 100) {
                        try {
                            //如果任务还没结束,就让出当前的锁,并休眠
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

2.5 sleep

2.5.1展示线程sleep的时候不释放synchronized的monitor,等sleep时间到了以后,正常结束后才释放锁

线程1 等5秒,退出。第二秒等5秒退出

/**
 * 展示线程sleep的时候不释放synchronized的monitor,等sleep时间到了以后,正常结束后才释放锁
 */
public class SleepDontReleaseMonitor implements Runnable {

    public static void main(String[] args) {
        SleepDontReleaseMonitor sleepDontReleaseMonitor = new SleepDontReleaseMonitor();
        new Thread(sleepDontReleaseMonitor).start();
        new Thread(sleepDontReleaseMonitor).start();
    }

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

    private synchronized void syn() {
        System.out.println("线程" + Thread.currentThread().getName() + "获取到了monitor。");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程" + Thread.currentThread().getName() + "退出了同步代码块");
    }
}

2.5.2演示sleep不释放lock(lock需要手动释放)

public class SleepDontReleaseLock implements Runnable {

    private static final Lock lock = new ReentrantLock();

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

    public static void main(String[] args) {
        SleepDontReleaseLock sleepDontReleaseLock = new SleepDontReleaseLock();
        new Thread(sleepDontReleaseLock).start();
        new Thread(sleepDontReleaseLock).start();
    }
}

2.5.3sleep响应中断

1 抛出InterruptedException
2 清除中断状态

/**
 * 描述:     每个1秒钟输出当前时间,被中断,观察。
 * Thread.sleep()
 * TimeUnit.SECONDS.sleep()
 */
public class SleepInterrupted implements Runnable{

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new SleepInterrupted());
        thread.start();
        Thread.sleep(6500);
        thread.interrupt();
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(new Date());
            try {
                TimeUnit.HOURS.sleep(3);
                TimeUnit.MINUTES.sleep(25);
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                System.out.println("我被中断了!");
                e.printStackTrace();
            }
        }
    }
}

2.5.4总结

2.5.5面试:wait/notify,sleep异同

1 相同点:
都会让线程进入阻塞状态
他们都可以响应中断,可以抛出异常

2 不同点:
2.1 wait/notify 必须在同步代码快中执行,而sleep不需要
会线程更安全,防止死锁,活着永久等待
2.2 wait/notify会释放锁,而sleep不会
2.3 sleep需要传参,wait不传
2.4 所属类不同,wait/notify属于object类,sleep属于thread类
wait/notify属于object类:Java设计中每个类都是一把锁,他的对象头中都有几位保存monitor的预留

2.6join 方法

2.6.1 作用和用法

作用:因为新的线程加入我们,所以我们要等他执行完在出发。
用法: main等待子线程执行完毕
在join期间,线程到底是什么状态?waiting

/**
 * 描述:     演示join,注意语句输出顺序,会变化。
 */
public class Join {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "执行完毕");
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "执行完毕");
            }
        });

        thread.start();
        thread2.start();
        System.out.println("开始等待子线程运行完毕");
        thread.join();
        thread2.join();
        System.out.println("所有子线程执行完毕");
    }
}
子线程还在休眠,所以主线程往下执行类代码 而有join的情况下,主线程的代码,需要等待子线程执行完毕才继续执行下边的代码

2.6.2 遇到中断进行演示

/**
 * 描述:     演示join期间被中断的效果
 */
public class JoinInterrupt {
    public static void main(String[] args) {
        Thread mainThread = Thread.currentThread();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    mainThread.interrupt();
                    Thread.sleep(5000);
                    System.out.println("Thread1 finished.");
                } catch (InterruptedException e) {
                    System.out.println("子线程中断");
                }
            }
        });
        thread1.start();
        System.out.println("等待子线程运行完毕");
        try {
            thread1.join();
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName()+"主线程中断了");
            thread1.interrupt();
        }
        System.out.println("子线程已运行完毕");
    }

}

2.7yield

作用:释放我的CPU时间片

定位:JVM不保证遵循,所以一般不自己使用

2.8 CurrentThread

public class CurrentThread implements Runnable {

    public static void main(String[] args) {
        new CurrentThread().run();
        new Thread(new CurrentThread()).start();
        new Thread(new CurrentThread()).start();
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值