Java多线程-Thread的Object类介绍【wait】【notify】【sleep】

news2024/9/21 2:48:13

Thread和Object类详解

方法概览

Thread

在这里插入图片描述

wait、notify、notifyAll方法详解

作用

阻塞阶段

使用了wait方法之后,线程就会进入阻塞阶段,只有发生以下四种情况中的其中一个,线程才会被唤醒

  1. 另一个线程调用了这个线程的notify方法,刚好唤醒的是本线程
  2. 另一个线程调用了这个对象的notifyAll方法
  3. 过了wait规定的超时时间
  4. 线程调用了interrupt

唤醒阶段

notify会唤醒单个处于阻塞状态的线程,唤醒的线程是随机的

notify和wait都需要写在synchronized代码块里,不然会抛出异常

notifyAll会唤醒所有等待的线程

遇到中断

执行wait方法之后,被中断,会抛出InterruptedException这个异常

代码展示

  • 展示wait和notify的基本用法
  • 该代码执行wait方法之后会释放锁,然后thread2执行notify方法
  • notify方法执行完毕之后,并没有立即释放锁,而是接着执行之后的代码,也就是打印“Thread2调用notify”这句话
  • thread2执行完毕之后,会进行释放锁,thread1才会继续执行
  • 在此期间,thread1虽然被唤醒,但是一直在等待thread2同步代码块里面的代码执行完毕
public class Wait {

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

    public static Object object = new Object();

    static class Thread1 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                System.out.println("Thread1执行");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread1获取锁");
            }
        }
    }

    static class Thread2 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                object.notify();
                System.out.println("Thread2调用notify");
            }
        }
    }
}

/*
Thread1执行
Thread2调用notify
Thread1获取锁
* */
  • notify和notifyAll的展示
  • 第一个输出:threadc调用notifyAll
  • 第二个输出:threadc调用notify
  • 调用notify的时候,程序并没有结束,threadb陷入等待
public class notifyOrAll implements Runnable{

    private static final Object a = new Object();

    public static void main(String[] args) throws InterruptedException {

        Runnable r = new notifyOrAll();
        Thread threada = new Thread(r);
        Thread threadb = new Thread(r);
        Thread threadc = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (a) {
//                    a.notifyAll();
                    a.notify();
                    System.out.println(Thread.currentThread().getName() + "notify");
                }
            }
        });
        threada.start();
        Thread.sleep(200);
        threadb.start();
        Thread.sleep(200);
        threadc.start();

    }

    @Override
    public void run() {
        synchronized (a) {
            System.out.println(Thread.currentThread().getName() + "得到锁");
            try {
                System.out.println(Thread.currentThread().getName() + "wait");
                a.wait();
                System.out.println(Thread.currentThread().getName() + "wait结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

/*
Thread-0得到锁
Thread-0wait
Thread-1得到锁
Thread-1wait
Thread-2notifyAll
Thread-1wait结束
Thread-0wait结束
* */

/*
Thread-0得到锁
Thread-0wait
Thread-1得到锁
Thread-1wait
Thread-2notify
Thread-0wait结束
* */

  • 只释放当前monitor
  • 证明wait只释放当前的那把锁
public class OwnMonitor {

    private static volatile Object a = new Object();
    private static volatile Object b = new Object();

    public static void main(String[] args) throws InterruptedException {

        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (a) {
                    System.out.println("threadA得到a");
                    synchronized (b) {
                        System.out.println("threadA得到锁b");

                        try {
                            System.out.println("threadA释放a");
                            a.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (a) {
                    System.out.println("threadB得到a");
                    System.out.println("threadB要获取b");
                    synchronized (b) {
                        System.out.println("threadB得到b");
                    }
                }
            }
        });

        threadA.start();
        Thread.sleep(1000);
        threadB.start();
    }

}

/*
threadA得到a
threadA得到锁b
threadA释放a
threadB得到a
threadB要获取b
* */

特点

  • 执行这些方法必须先获取锁
  • notify只能换取一个,而且是随机的
  • 都属于Object。任何对象都可以调用
  • 都是native final修饰的

当线程从wait状态刚被唤醒时,通常不能直接得到锁,那就会从waiting状态转换到blocked状态,抢到锁之后状态转变为runnable

如果发生异常,则直接跳到Terminated状态

通过wait、notify方法实现生产者和消费者

  • 将storge当作生产者和消费者进行工作的仓库
  • 如果storge中没有数据,生产者就开始wait
  • 如果storge中数据满了,消费者就开始wait
  • 生产者和消费者每进行一次生产和消费,就执行notify
public class ProducerConsumer {

    public static void main(String[] args) {

        Storge storge = new Storge();
        Producer producer = new Producer(storge);
        Consumer consumer = new Consumer(storge);
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

class Producer implements Runnable {

    private Storge storge;

    public Producer(Storge storge) {
        this.storge = storge;
    }

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

class Consumer implements Runnable {

    private Storge storge;

    public Consumer(Storge storge) {
        this.storge = storge;
    }

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

class Storge {

    private int maxSize;
    private LinkedList<Date> storge;

    public Storge() {
        maxSize = 10;
        storge = new LinkedList<>();
    }

    public synchronized void put() {
        while (storge.size() == maxSize) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        storge.add(new Date());
        System.out.println("已经有了" + storge.size());
        notify();
    }

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

sleep方法详解

作用:让线程在预期的时间执行,其他时间不占用CPU资源

特点:和wait不一样,sleep不释放锁

sleep不会释放锁

  • 证明sleep不会释放 synchronized锁
public class SleepSyn implements Runnable{

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

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

    private synchronized void syn() {
        System.out.println(Thread.currentThread().getName() + "获取锁");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "释放锁");
    }
}

/*
* Thread-0获取锁
Thread-0释放锁
Thread-1获取锁
Thread-1释放锁
* */
  • 证明sleep不释放Lock锁

    public class sleepLock implements Runnable{
    
        private static final Lock LOCK = new ReentrantLock();
    
        @Override
        public void run() {
            LOCK.lock();
            System.out.println(Thread.currentThread().getName() + "获取锁");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                LOCK.unlock();
            }
            System.out.println(Thread.currentThread().getName() + "释放锁");
        }
    
        public static void main(String[] args) {
            sleepLock sleepLock = new sleepLock();
            new Thread(sleepLock).start();
            new Thread(sleepLock).start();
        }
    }
    
    /*
    * Thread-0获取锁
    Thread-0释放锁
    Thread-1获取锁
    Thread-1释放锁
    * */
    

sleep响应中断

  • 抛出InterruptedException
  • 会清除中断状态
  • 中断之后,抛出异常继续执行
public class sleepInterrupted implements Runnable{

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new sleepInterrupted());
        thread.start();
        Thread.sleep(2000);
        thread.interrupt();

    }

    @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();
            }
        }
    }
}

/*
* Fri Jan 27 21:11:57 CST 2023
Fri Jan 27 21:11:58 CST 2023
中断
Fri Jan 27 21:11:59 CST 2023
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.jx.JavaTest.ThreadObjectMethod.sleepInterrupted.run(sleepInterrupted.java:21)
   at java.lang.Thread.run(Thread.java:748)
Fri Jan 27 21:12:00 CST 2023
Fri Jan 27 21:12:01 CST 2023
Fri Jan 27 21:12:02 CST 2023
Fri Jan 27 21:12:03 CST 2023
Fri Jan 27 21:12:04 CST 2023
Fri Jan 27 21:12:05 CST 2023
Fri Jan 27 21:12:06 CST 2023

Process finished with exit code 0

* */

总结

sleep方法可以让线程进入waiting状态,不占用CPU资源,但是不释放锁,规定时间之后再运行

休眠期间如果被打断,会抛出异常并清除中断状态

join方法详解

新线程加入,主线程等子线程执行完毕

代码展示

  • 前一个结果是使用join
  • 后一个结果是没使用join
  • 可知使用join之后,主线程会等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("所有线程执行完毕");
    }
}

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

/*
* 开始等待子线程运行
所有线程执行完毕
Thread-1执行完毕
Thread-0执行完毕
* */
  • 遇到中断
  • 第一个的运行结果是主线程没中断的打印结果
  • 第二个的运行结果是join期间进行中断的打印结果,可知在打印了“子线程运行完毕”之后,依然打印了“启动”两个字,可知会造成运行混乱
  • 可以在捕获异常的代码块中,将join的线程也中断,可以解决上面的问题
public class joinInterrupt {
    public static void main(String[] args) {
        Thread main1 = Thread.currentThread();
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    main1.interrupt();
                    Thread.sleep(2000);
                    System.out.println("启动");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread1.start();
        System.out.println("join");
        try {
            thread1.join();
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + "中断");
            // thread1.interrupt();
            e.printStackTrace();
        }
        System.out.println("子线程运行完毕");
    }
}


/*
* join
启动
子线程运行完毕
* */

/*
* join
main中断
子线程运行完毕
java.lang.InterruptedException
   at java.lang.Object.wait(Native Method)
   at java.lang.Thread.join(Thread.java:1252)
   at java.lang.Thread.join(Thread.java:1326)
   at com.jx.JavaTest.ThreadObjectMethod.joinInterrupt.main(joinInterrupt.java:23)
启动

Process finished with exit code 0

* */

/*
* join
main中断
子线程运行完毕
java.lang.InterruptedException: sleep interrupted
   at java.lang.Thread.sleep(Native Method)
   at com.jx.JavaTest.ThreadObjectMethod.joinInterrupt$1.run(joinInterrupt.java:13)
   at java.lang.Thread.run(Thread.java:748)
java.lang.InterruptedException
   at java.lang.Object.wait(Native Method)
   at java.lang.Thread.join(Thread.java:1252)
   at java.lang.Thread.join(Thread.java:1326)
   at com.jx.JavaTest.ThreadObjectMethod.joinInterrupt.main(joinInterrupt.java:23)

Process finished with exit code 0

* */
  • join期间,线程处于WAITING状态

    public class joinStates {
    
        public static void main(String[] args) throws InterruptedException {
            Thread main1 = Thread.currentThread();
    
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(3000);
                        System.out.println(main1.getState());
                        System.out.println("子线程运行结束");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
    
            thread.start();
            System.out.println("join");
            thread.join();
            System.out.println("运行完毕");
    
        }
    }
    
    /*
    * join
    WAITING
    子线程运行结束
    运行完毕
    * */
    

yield方法

用来释放CPU时间片,但是不一定能达到预期的效果,因为有时CPU资源不紧张,无需yield

和sleep的区别是:sleep期间不会被再次调度但是yield会立刻处于竞争状态,还会随时再次被调度

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/182605.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python数据可视化之直方图和密度图

Python数据可视化之直方图和密度图 提示&#xff1a;前言 Python数据可视化之直方图和密度图 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录Python数据可视化之直方图和密度图前言一、导入包二、选择数据集三、直…

k8s之平滑升级

写在前面 通过POD 应用就有了存在的形式&#xff0c;通过deployment 保证了POD在一定的数量&#xff0c;通过service 可以实现一定数量的POD以负载均衡的方式对外提供服务。但&#xff0c;如果是程序开发了新功能&#xff0c;需要上线&#xff0c;该怎么办呢&#xff1f;对此k…

jvm相关,jvm内存模型,java程序运行流程及jvm各个分区的作用、对象的组成(针对hotspot虚拟机)--学习笔记

java程序运行时的运行模型 在jdk1.8之前的元空间&#xff0c;称为永久代并将元空间挪到了堆直接使用本地内存&#xff0c;不再占用堆空间 jvm内存结构划分 堆&#xff08;方法区&#xff09;和元空间是线程共有的&#xff0c;其他部分是线程私有的 每创建一个线程都会创建一个…

MYSQL中常见的知识问题(二)

1、B树和B树的区别&#xff0c;MYSQL为啥使用B树。 1.1、B树 目的&#xff1a;为了存储设备或者磁盘设计的一种平衡查找树。 定义&#xff08;M阶B树&#xff09;&#xff1a;a、树中的每个节点最多有m个孩子。 b、除了根节点和叶子节点外&#xff0c;其他节点最少含有m/2(取上…

08-网络管理-iptables基础(四表五链、禁止ping、防火墙规则添加/删除、自建链使用、SNAT\DNAT模式、FTP服务器防火墙规则)待发布

文章目录1. 概述1.1 四表1.2 五链1.3 四表五链的关系1.4 使用流程2. 语法和操作1.1 语法1.2 常用操作命令1.3 基本匹配条件1.4 基本动作1.5 常用命令示例- 设置默认值- 禁止80端口访问- 查看防火墙规则- 保存规则- 允许ssh- 禁止ping- 删除规则- 清除规则&#xff08;不包括默认…

HR软件如何识别保留优秀员工

在企业信息化的时代&#xff0c;越来越多的年轻员工开始追求他们的激情&#xff0c;辞掉那些乏味的工作&#xff0c;而选择加入重视员工生活质量的企业。他们不再追随那些以牺牲员工福利为代价追求利润的公司。 员工认可度有助于加强组织中的团队合作关系&#xff0c;反过来&a…

木马程序(病毒)

木马的由来 "特洛伊木马"&#xff08;trojan horse&#xff09;简称"木马"&#xff0c;据说这个名称来源于希腊神话《木马屠城记》。古希腊有大军围攻特洛伊城&#xff0c;久久无法攻下。于是有人献计制造一只高二丈的大木马&#xff0c;假装作战马神&…

实用技巧盘点:Python和Excel交互的常用操作

大家好&#xff0c;在以前&#xff0c;商业分析对应的英文单词是Business Analysis&#xff0c;大家用的分析工具是Excel&#xff0c;后来数据量大了&#xff0c;Excel应付不过来了&#xff08;Excel最大支持行数为1048576行&#xff09;&#xff0c;人们开始转向python和R这样…

【通信原理(含matlab程序)】实验六:模拟信号的数字化

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; 本人持续分享更多关于电子通信专业内容以及嵌入式和单片机的知识&#xff0c;如果大家喜欢&#xff0c;别忘点个赞加个关注哦&#xff0c;让我们一起共同进步~ &#x…

一文理解JVM虚拟机

一. JVM内存区域的划分 1.1 java虚拟机运行时数据区 java虚拟机运行时数据区分布图&#xff1a; JVM栈&#xff08;Java Virtual Machine Stacks&#xff09;&#xff1a; Java中一个线程就会相应有一个线程栈与之对应&#xff0c;因为不同的线程执行逻辑有所不同&#xff…

【JavaGuide面试总结】Java IO篇

【JavaGuide面试总结】Java IO篇1.有哪些常见的 IO 模型?2.Java 中 3 种常见 IO 模型BIO (Blocking I/O)NIO (Non-blocking/New I/O)AIO (Asynchronous I/O)1.有哪些常见的 IO 模型? UNIX 系统下&#xff0c; IO 模型一共有 5 种&#xff1a; 同步阻塞 I/O、同步非阻塞 I/O、…

浏览器兼容性 问题产生原因 厂商前缀 滚动条 css hack 渐近增强 和 优雅降级 caniuse

目录浏览器兼容性问题产生原因厂商前缀滚动条css hack渐近增强 和 优雅降级caniuse浏览器兼容性 问题产生原因 市场竞争标准版本的变化 厂商前缀 比如&#xff1a;box-sizing&#xff0c; 谷歌旧版本浏览器中使用-webkit-box-sizing:border-box 市场竞争&#xff0c;标准没有…

Java多线程案例之线程池

前言&#xff1a;在讲解线程池的概念之前&#xff0c;我们先来谈谈线程和进程&#xff0c;我们知道线程诞生的目的其实是因为进程太过重量了&#xff0c;导致系统在 销毁/创建 进程时比较低效&#xff08;具体指 内存资源的申请和释放&#xff09;。 而线程&#xff0c;其实做…

14岁初中生将免去四考,保送清华本硕博连读,乡亲们敲锣打鼓祝贺

导语&#xff1a; 很多学生在很小的时候&#xff0c;都曾有豪言壮语&#xff1a;“将来一定要考上清华北大”。可是真正接受教育&#xff0c;开始学习之后&#xff0c;学生们才能发现&#xff0c;原来学习这么难。不要说真的走进清华北大&#xff0c;即使是进入“985”大学&am…

C++ 智能指针(一) auto_ptr

文章目录前言 - 什么是智能指针&#xff1f;std::auto_ptrauto_ptr的使用常用成员方法&#xff1a;1. get()方法2. release()方法3. reset()方法4. operator()5. operator*() & operator->()auto_ptr的局限性前言 - 什么是智能指针&#xff1f; 在全文开始之前&#xf…

Redis事务的概述、设计与实现

1 Redis事务概述事务提供了一种“将多个命令打包&#xff0c; 然后一次性、按顺序地执行”的机制&#xff0c; 并且事务在执行的期间不会主动中断 —— 服务器在执行完事务中的所有命令之后&#xff0c; 才会继续处理其他客户端的其他命令。以下是一个事务的例子&#xff0c; 它…

mysql-事务以及锁原理讲解(二)

1、前言 众所周知&#xff0c;事务和锁是mysql中非常重要功能&#xff0c;同时也是面试的重点和难点。本文会详细介绍事务和锁的相关概念及其实现原理&#xff0c;相信大家看完之后&#xff0c;一定会对事务和锁有更加深入的理解。 2、什么是事务 在维基百科中&#xff0c;对事…

7 处理多维特征的输入

文章目录课程前提知识问题引入模型改进修改神经层的增加学习能力与超参数课本代码课程来源: 链接课程文本来源借鉴&#xff1a; 链接以及&#xff08;强烈推荐&#xff09;Birandaの课程前提知识 BCELoss - Binary CrossEntropyLoss BCELoss 是CrossEntropyLoss的一个特例&am…

JavaEE day7 初识JavaScript2

函数小结 1.可以赋值给变量(其实就是被变量所指向) 2.装入容器中作为元素存在 3.在函数调用的过程中&#xff0c;函数类型作为实参 4.函数作为另一个函数的返回值 可以直接return一个函数 5.和java不同&#xff0c;JS中允许在一个函数中定义另一个函数&#xff0c;也就是嵌…

介绍一个令强迫症讨厌的小红点组件

前言 在 App 的运营中,活跃度是一个重要的指标,日活/月活……为了提高活跃度,就发明了小红点,然后让强迫症用户“没法活”。 小红点虽然很讨厌,但是为了 KPI,程序员也不得不屈从运营同学的逼迫(讨好),得想办法实现。这一篇,来介绍一个徽标(Badge)组件,能够快速搞…