代码分析Java中线程的等待与唤醒

下面是“代码分析Java中线程的等待与唤醒”的完整攻略:

1. 什么是线程等待和唤醒

在Java中,线程等待和唤醒是多线程编程中重要的概念之一。线程等待和唤醒通常发生在一个共享对象上,例如一个锁或者是一个共享的变量。简单来说,线程等待和唤醒的作用是让线程在满足某些条件之前暂停或者执行某段代码之前等待某些条件达成。

具体而言,线程等待通常与线程同步机制(如synchronized关键字或者lock)一起使用,用于暂停线程,直到某些条件得到满足。线程等待会将当前线程放入等待队列中,这里需要注意,线程只能等待持有相同对象的其他线程,也就是说在某个共享对象上只能存在一个等待队列。

相应的,线程唤醒则是让等待队列中的一个线程从等待状态变为就绪状态,因此线程唤醒实际上就是通知正在等待的线程可以继续执行,一般与notify和notifyAll方法相关联,通常也需要在同步块中使用。

2. 线程等待和唤醒的实现方式

Java中实现线程等待和唤醒的方式通常有两种,wait()和notify()方法和Lock和Condition接口。

2.1 wait()和notify()方法

wait()和notify()方法是Java中实现程序的线程等待和唤醒的较为常用的方法。 一个线程可以通过wait()方法进入等待状态,使其它线程能够获得锁并执行一些任务。而另一个线程通过notify()方法来通知某个线程已经完成了特定任务,以便让该线程离开等待状态。

wait()方法的语法如下:

public final native void wait(long timeout) throws InterruptedException;
public final void wait() throws InterruptedException;
public final native void notify();
public final native void notifyAll();

wait()方法会将调用线程挂起,直到另外的线程调用notify()或notifyAll()方法。wait()方法会在Object对象的监视器中等待,并且只能被在同步块中(即synchronized)调用。同时,wait()方法还支持提供超时时间,即到达指定的超时时间后wait方法会自动返回,并且线程状态变为就绪态。

notify()方法则是唤醒等待中的一个线程。如果有多个线程在等待,那么唤醒其中任意一个线程即可,具体唤醒哪一个线程由JVM底层决定。notifyAll()方法则是唤醒所有在等待中的线程,使得它们都进入就绪状态,等待被再次调度。

下面给出一段代码示例,通过wait和notify来实现一次精准唤醒,即只唤醒指定的某一个线程:

public class WaitAndNotifyDemo implements Runnable {
    final int MAX_LIMIT = 3;
    int current = 0;

    public static void main(String[] args) {
        WaitAndNotifyDemo wad = new WaitAndNotifyDemo();
        for (int i = 0; i < wad.MAX_LIMIT; i++) {
            new Thread(wad).start();
        }
    }

    @Override
    public void run() {
        synchronized (this) {
            // 当前线程的序号
            int num = current++;
            // 等待指定序号的线程
            while (current % MAX_LIMIT != num) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 输出当前线程的序号
            System.out.println("Thread-" + num);
            // 唤醒下一个线程
            notifyAll();
        }
    }
}

上述代码中,我们设定了一个最大次数为3的计数器,当计数器的数值不等于当前线程的编号时,我们进入等待,否则输出当前线程编号,并唤醒下一条线程。

2.2 Lock和Condition接口

Java5之后,JDK中又增加了Lock和Condition接口,相比wait和notify方式,Lock和Condition方式用起来更自由,同时能够更方便地解决那些很容易出现的死锁问题。

Lock接口的使用需要进行显示加锁和释放锁,Condition提供了await()等待和signal()唤醒方法。下面是一个使用Lock和Condition的示例代码:

public class LockAndConditionDemo {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    private volatile boolean isPing = true;

    public static void main(String[] args) {
        LockAndConditionDemo lockAndConditionDemo = new LockAndConditionDemo();
        //线程A
        Thread threadA = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                lockAndConditionDemo.printPing(i);
            }
        });
        //线程B
        Thread threadB = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                lockAndConditionDemo.printPong(i);
            }
        });
        threadA.start();
        threadB.start();
    }

    public void printPing(int i) {
        lock.lock();
        try {
            while (!isPing) {
                condition.await();
            }
            System.out.println("ping:(" + i + ")");
            isPing = false;
            condition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printPong(int i) {
        lock.lock();
        try {
            while (isPing) {
                condition.await();
            }
            System.out.println("pong:{" + i + "}");
            isPing = true;
            condition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

输出:

ping:(0)
pong:{0}
ping:(1)
pong:{1}
ping:(2)
.....

我们使用了一个Lock对象和两个Condition对象实现了线程的等待和唤醒,不同的线程会在不同的Condition对象上等待,并且使用volatile型变量isPing来控制打印Pong和Ping。从输出结果中我们可以看到Ping和Pong会依次打印,符合预期。

综上所述,以上两种方法都是实现Java多线程中的线程等待和唤醒的方式。在使用的时候根据实际情况判断使用哪一种方法,需要注意的是,线程等待和唤醒最好放在同一个同步块或方法中,否则会存在线程不安全的情况。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:代码分析Java中线程的等待与唤醒 - Python技术站

(0)
上一篇 2023年5月18日
下一篇 2023年5月18日

相关文章

  • Tomcat使用Log4j输出catalina.out日志

    介绍 在使用Tomcat服务器时,通常需要对服务器的运行状态进行特定的记录,例如:访问日志、错误日志、调试日志等。而catalina.out则是Tomcat服务中的一种重要的日志文件,其中一般会记录Tomcat服务器的所有日志,包括控制台的输出信息、访问日志、错误日志等。在Tomcat服务器中,默认的日志实现是JUL(Java Util Logging),但…

    Java 2023年5月19日
    00
  • SpringMVC+Mybatis二维码实现多平台付款(附源码)

    下面我将详细讲解“SpringMVC+Mybatis二维码实现多平台付款(附源码)”的完整攻略。 1. 概述 本文介绍如何使用SpringMVC和Mybatis实现多平台付款系统,主要包括以下几个方面。 使用SpringMVC实现Web应用程序的开发; 使用Mybatis对数据库进行访问; 使用二维码实现多平台付款。 2. 开发环境和技术栈 JDK 1.8 …

    Java 2023年5月26日
    00
  • SpringBoot打印启动时异常堆栈信息详解

    讲解SpringBoot打印启动时异常堆栈信息的完整攻略,具体步骤如下: 1. 开启Debug模式 在SpringBoot启动类中,添加以下代码: @SpringBootApplication public class DemoApplication { public static void main(String[] args) { // 开启Debug模…

    Java 2023年5月27日
    00
  • 完美解决java.lang.OutOfMemoryError处理错误的问题

    下面我将详细讲解如何完美解决 java.lang.OutOfMemoryError 错误的处理问题。 什么是 java.lang.OutOfMemoryError 错误? java.lang.OutOfMemoryError 错误是指 Java 应用程序在运行时申请的内存超过了 Java 虚拟机所能分配的最大内存限制,导致 Java 虚拟机耗尽了可用内存造成…

    Java 2023年5月27日
    00
  • java json不生成null或者空字符串属性(详解)

    Java JSON不生成null或者空字符串属性(详解) 在开发过程中,我们经常需要将Java对象序列化成JSON格式,然而默认情况下,在Java对象中含有null或者空字符串的属性时,JSON序列化会将这些属性也序列化出来,这样可能会导致一些问题。此时,我们需要在生成JSON时控制输出项,使其不包含null或空字符串的属性。 生成JSON时控制输出项 我们…

    Java 2023年5月26日
    00
  • spring Data jpa简介_动力节点Java学院整理

    Spring Data JPA简介 什么是Spring Data JPA Spring Data JPA是Spring基于ORM框架JPA的基础上封装的一套JPA应用框架。它简化了基于JPA的数据访问层开发工作,使得我们可以更加专注于业务逻辑的实现。Spring Data JPA提供了一套自动生成JPA API实现代码的机制,这样我们就不用手动编写大量的JP…

    Java 2023年5月20日
    00
  • Java集合之Set接口及其实现类精解

    Java集合之Set接口及其实现类精解 Set接口是Java集合框架中的一种无序集合,它只能包含不重复的元素。本文将会详细讲解Set接口及其实现类的特点和使用方法。 Set接口 Set接口是Java集合框架中的一个接口,它继承了Collection接口,表示一个不允许重复元素的无序集合。Set接口中定义了以下常用的方法: add(E e):添加指定元素到集合…

    Java 2023年5月18日
    00
  • Java如何利用Mybatis进行数据权限控制详解

    Java如何利用Mybatis进行数据权限控制详解 什么是数据权限控制 数据权限控制是指通过安全管理机制,对不同用户或用户组授权不同的数据操作权限,从而控制这些用户或用户组在访问企业数据资源时的范围和强度。 Mybatis数据权限控制的实现过程 首先,在Mybatis中配置Interceptor拦截器来实现数据权限控制,Interceptor是用来拦截SQL…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部