Day276.Thread和Object类中的重要方法 -Juc

Posted 阿昌喜欢吃黄桃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day276.Thread和Object类中的重要方法 -Juc相关的知识,希望对你有一定的参考价值。

Thread和Object类中的重要方法【二】

一、wait,notify,notifyAll常见面试问题

1、用程序实现两个线程交替打印0-100的奇偶数

  • 未优化版本,有许多的废操作,浪费性能

定义两个线程不同的run()方法,一个打印奇数,一个打印偶数;

/******
 @author 阿昌
 @create 2021-05-23 19:43
 *******
 *  两个线程交替打印0-100的奇偶数,采用Synchronized实现
 */
public class WaitNotifyPrintOddEvenSyn {

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

    public static void main(String[] args) {
        //处理【偶数】线程
        Thread even = new Thread(new Runnable() {
            @Override
            public void run() {
                while (count < 100) {
                    synchronized (lock) {
                        if ((count & 1) == 0) {
                           System.out.println(Thread.currentThread().getName()+","+count);
                           count++;
                        }
                    }
                }
            }
        });
        //处理【奇数】线程
        Thread odd = new Thread(new Runnable() {
            @Override
            public void run() {
                while (count < 100) {
                    synchronized (lock) {
                        if ((count & 1) == 1) {
                           System.out.println(Thread.currentThread().getName()+","+count);
                           count++;
                        }
                    }

                }
            }
        });


        even.start();
        odd.start();
    }
}
  • 结果

image-20210523200436313


  • 优化版本,使用wait、notify

通过只写一个Runnable实现,来让现场交替打印奇偶数

/******
 @author 阿昌
 @create 2021-05-23 20:05
  *******
  *  两个线程交替打印 0 - 100 的奇偶数,用wait和notify
 */
public class WaitNotifyPrintOddEven {

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

    //主函数
    public static void main(String[] args) {
        TurningRounder turningRounder = new TurningRounder();
        Thread even = new Thread(turningRounder);
        Thread odd = new Thread(turningRounder);

        even.start();
        odd.start();
    }

    static class TurningRounder 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();
                        }
                    }
                }
            }
        }
    }
}
  • 结果

image-20210523201658376


2、手写生产者消费者设计模式

/******
 @author 阿昌
 @create 2021-05-22 20:58
 *******
 *  用wait/notify来实现 生产者消费者模式
 */
public class ProducerConsumerModel {
    //主函数
    public static void main(String[] args) {
        EventStorage eventStorage = new EventStorage();
        Consumer1 consumer = new Consumer1(eventStorage);
        Producer1 producer = new Producer1(eventStorage);

        Thread threadConsumer = new Thread(consumer);
        Thread threadProducer = new Thread(producer);

        threadConsumer.start();
        threadProducer.start();
    }
}

//生产者
class Producer1 implements Runnable{
    private EventStorage storage;

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

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

//消费者
class Consumer1 implements Runnable{
    private EventStorage storage;

    public Consumer1(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 storage;

    public EventStorage(){
        this.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();
    }
}

3、为什么wait()需要在同步代码块内使用,而sleep()不需要

为了线程通信可靠,防止死锁发生


4、为什么线程通信的方法wait,notify和notifyAll被定义在Object类里,而sleep定义在Thread类里?

因为wait,notify和notifyAll是锁级别的操作

锁是属于某一个对象的,每一个对象的对象头中含有即为来保留当前锁状态,锁是绑定到某个对象中,而不是线程中


5、wait方法是属于Object对象,那么调用Thread.wait()会怎么样?

首先Thread也是一个对象,他也继承于Object类,但是对于Thread类比较特殊,他在执行完会自动执行notify()方法,这样子可能会使设计的流程受到干扰,所以他Thread类本身不适合做锁对象


6、如何选择用notify和nofityAll

考虑是要唤醒一个线程还是多个线程


7、notifyAll之后所有的线程都会再次抢夺锁,如果某线程抢夺失败怎么办?

他们会陷入到一个等待阻塞的状态,等待那个锁被释放,再去抢夺那把锁


8、用suspend()和resume()来阻塞线程可以吗?为什么?

已经因为线程安全问题被弃用了,推荐使用wait和notify


二、Java相关概念

1、JavaSE、JavaEE、JavaME是什么?

  • JavaSE:标准版
  • JavaEE:企业版
  • JavaME:移动版

2、JRE和JDK和JVM是什么关系?

  • JRE:Java运行环境(包含JVM)
  • JDK:Java开发工具包,用于开发(JDK包含JRE)
  • JVM:Java虚拟机,Java运行的必要东西

3、Java版本升级都包括了哪些东西的升级?

Java类升级、JVM升级


4、Java8和Java1.8和JDK8是什么关系,是同一个东西吗?

JavaSE8 ==Java8 == Java1.8 == JDK8


三、Sleep()方法详解

1、作用

image-20210523210121643


2、sleep()方法不释放锁

  • 不释放synchronized
/******
 @author 阿昌
 @create 2021-05-23 21:05
 *******
 *  展示线程sleep的时候不释放sychronized的monitor,等sleep时间到了后,
 *  正常结束后才会释放锁
 */
public class SleepDontReleaseMonitor implements Runnable {
    //主函数
    public static void main(String[] args) {
        SleepDontReleaseMonitor s = new SleepDontReleaseMonitor();
        Thread thread1 = new Thread(s);
        Thread thread2 = new Thread(s);

        thread1.start();
        thread2.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()+"退出同步代码块");
    }
}

image-20210523211002560


  • 不释放lock
/******
 @author 阿昌
 @create 2021-05-23 21:13
 *******
 *  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);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    //主函数
    public static void main(String[] args) {
        SleepDontReleaseLock s = new SleepDontReleaseLock();
        Thread thread1 = new Thread(s);
        Thread thread2 = new Thread(s);

        thread1.start();
        thread2.start();
    }
}

image-20210523211840801


  • 和wait()不同,wait()会释放锁,sleep()不会

3、sleep方法响应中断

  • 抛出InterruptedException
/******
 @author 阿昌
 @create 2021-05-23 21:22
 *******
 *  每隔一秒钟输出当前时间,但是中途被中断,观察
 *  两种写法:1、Thread.sleep()
 *          2、TimeUnit.SECONDS.sleep()
 */
public class SleepInterrupted implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(new Date());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                System.out.println("我被中断了");
                e.printStackTrace();
            }
        }
    }

    //主函数
    public static void main(String[] args) throws InterruptedException {
        SleepInterrupted sleepInterrupted = new SleepInterrupted();
        Thread thread = new Thread(sleepInterrupted);

        thread.start();
        Thread.sleep(5000);
        //中断
        thread.interrupt();
    }
}

image-20210523212919741


4、总结

image-20210523213250113


5、sleep常见面试题

wait/notify、sleep的异同(方法属于哪个对象?线程状态怎么切换?)

  • 相同点
  • 阻塞
  • 可以响应interrupted中断
  • 不同点
  • wait/notify
    • 使用在同步方法中
    • 释放锁
    • 可选指定时间
    • 属于Object类
  • sleep
    • 可不使用在同步方法中
    • 不释放锁
    • 可指定时间
    • 属于Thread类

四、Join()方法详解

1、作用、用法

作用因为新的线程加入了我们,所有我们需要等待他们执行完成再出发

用法主线程等待子线程执行完毕,注意谁等谁


2、代码演示

/******
 @author 阿昌
 @create 2021-05-23 21:44
 *******
 *  演示Join用法,注意语句的输出顺序,会变化。
 */
public class Join {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = 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() + ":线程执行完毕");
            }
        });

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

两个子线程都睡1s,所以主线程先打印出开始等待子线程运行完毕然后在执行子线程

image-20210523215314095


  • 如果注掉
        thread1.start();
        thread2.start();
        System.out.println("开始等待子线程运行完毕");
//        thread1.join();
//        thread2.join();
        System.out.println("所有子线程执行完毕");
  • 结果

主线程直接在1s内执行完开始等待子线程运行完毕所有子线程执行完毕语句

然后子线程才执行完

image-20210523215433496


3、join遇到中断

/******
 @author 阿昌
 @create 2021-05-23 21:56
 *******
 *  Join的中断演示
 */
public class JoinInterrupted {
    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 执行完毕");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("子线程运行完毕");
            }
        });

        thread1.start();
        System.out.println("等待子线程运行完毕");
        try {
            thread1.join();
        } catch (InterruptedException e) {
            System.out.以上是关于Day276.Thread和Object类中的重要方法   -Juc的主要内容,如果未能解决你的问题,请参考以下文章

5.线程的八大核心基础知识之Thread和Object类中的重要方法详解

java.long.object类中的重要方法

JAVA零基础小白学习教程之day10-API&Object&String

JAVA零基础小白学习教程之day10-API&Object&String

继承中子类构造函数相关问题

字符串类中的Object的equals方法和自定义类中的Object的equal方法之间有什么区别