Java线程阻塞和唤醒的几种方式详解

 更新时间:2023年10月26日 09:43:38   作者:Ramscy  
这篇文章主要介绍了Java线程阻塞和唤醒的几种方式详解,线程阻塞是指当一个线程无法继续执行时,它会进入阻塞状态,直到某个条件满足后才能继续执行,线程阻塞可以通过多种方式实现,如等待锁、等待IO操作、等待其他线程的完成等,需要的朋友可以参考下

Object类自带的方法

使用wait()方法来阻塞线程,使用notify()和notifyAll()方法来唤醒线程。 调用wait()方法后,线程将被阻塞,wait()方法将会释放当前持有的监视器锁(monitor),直到有线程调用notify/notifyAll()方法后方能继续执行。 notify/notifyAll()方法只是解除了等待线程的阻塞,并不会马上释放监视器锁,而是在相应的被synchronized关键字修饰的同步方法或同步代码块执行结束后才自动释放锁。 默认使用非公平锁,无法修改。

缺点:

  • 使用几个方法时,必须处于被synchronized关键字修饰的同步方法或同步代码块中,否则程序运行时,会抛出IllegalMonitorStateException异常。
  • 线程的唤醒必须在线程阻塞之后,否则,当前线程被阻塞之后,一直没有唤醒,线程将会一直等待下去(对比LockSupport)
public class SynchronizedDemo {

// 三个线程交替打印ABC
    public static void main(String[] args) {

        Print print = new Print();

        new Thread(() -> {
            while (true) {
                print.printA();
            }
        }, "A").start();
        new Thread(() -> {
            while (true) {
                print.printB();
            }
        }, "B").start();
        new Thread(() -> {
            while (true) {
                print.printC();
            }
        }, "C").start();
    }
}

class Print {

    Object object = new Object();
    int num = 1;

    public void printA() {
        synchronized (object) {
            try {
                while (num != 1) {
                    object.wait();
                }
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + "==>A");
                }
                num = 2;
                object.notifyAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void printB() {
        synchronized (object) {
            try {
                while (num != 2) {
                    object.wait();
                }
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + "==>B");
                }
                num = 3;
                object.notifyAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void printC() {
        synchronized (object) {
            try {
                while (num != 3) {
                    object.wait();
                }
                for (int i = 0; i < 15; i++) {
                    System.out.println(Thread.currentThread().getName() + "==>C");
                }
                num = 1;
                object.notifyAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Condition接口

使用await()方法来阻塞线程,signal()/singnalAll()方法来唤醒线程。

需要使用lock对象的newCondition()方法获得Condition条件对象(可有多个)。

可实现公平锁,默认是非公平锁

缺点:

  • 必须被Lock包裹,否则会在运行时抛出IllegalMonitorStateException异常。
  • 线程的唤醒必须在线程阻塞之后
  • Lock的实现是基于AQS,效率稍高于synchronized
public class ConditionDemo {

// 三个线程交替打印ABC
    public static void main(String[] args) {
        Print print = new Print();
        new Thread(() -> {
            while (true) {
                print.printA();
            }
        }, "A").start();

        new Thread(() -> {
            while (true) {
                print.printB();
            }
        }, "B").start();

        new Thread(() -> {
            while (true) {
                print.printC();
            }
        }, "C").start();
    }
}

class Print {

    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int num = 1;

    public void printA() {

        lock.lock();
        try {
            while (num != 1) {
                condition1.await();
            }
            for (int i = 0; i < 5; ++i) {
                System.out.println(Thread.currentThread().getName() + "==>A");
            }
            num = 2;
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB() {

        lock.lock();
        try {
            while (num != 2) {
                condition2.await();
            }
            for (int i = 0; i < 10; ++i) {
                System.out.println(Thread.currentThread().getName() + "==>B");
            }
            num = 3;
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC() {

        lock.lock();
        try {
            while (num != 3) {
                condition3.await();
            }
            for (int i = 0; i < 15; ++i) {
                System.out.println(Thread.currentThread().getName() + "==>C");
            }
            num = 1;
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

LockSupport

使用park()来阻塞线程,用unpark()方法来唤醒线程。 这里有一个许可证的概念,许可不能累积,并且最多只能有一个许可,只有1和0的区别。

特点:

  • 使用灵活,可以直接使用
  • 线程唤醒可在线程阻塞之前,因为调用unpark()方法后,线程已经获得了一个许可证(但也只能有一个许可证),之后阻塞时,可以直接使用这个许可证来通行。
  • 效率高
public class LockSupportDemo {

// 三个线程交替打印ABC
    public static void main(String[] args) throws Exception {

        Print print = new Print();

        Thread threadA = new Thread(() -> {
            while (true) {
                print.printA();
            }
        }, "A");

        Thread threadB = new Thread(() -> {
            while (true) {
                print.printB();
            }
        }, "B");

        Thread threadC = new Thread(() -> {
            while (true) {
                print.printC();
            }
        }, "C");

        threadA.start();
        threadB.start();
        threadC.start();

        while (true) {
            LockSupport.unpark(threadA);
            LockSupport.unpark(threadB);
            LockSupport.unpark(threadC);
        }

    }
}

class Print {

    private int num = 1;

    public void printA() {
        while (num != 1) {
            LockSupport.park();
        }
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "==>A");
        }
        num = 2;
    }

    public void printB() {
        while (num != 2) {
            LockSupport.park();
        }
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "==>B");
        }
        num = 3;
    }

    public void printC() {
        while (num != 3) {
            LockSupport.park();
        }
        for (int i = 0; i < 15; i++) {
            System.out.println(Thread.currentThread().getName() + "==>C");
        }
        num = 1;
    }
}

到此这篇关于Java线程阻塞和唤醒的几种方式详解的文章就介绍到这了,更多相关Java线程阻塞和唤醒内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot实现PDF转图片的代码示例

    SpringBoot实现PDF转图片的代码示例

    在本文中,我们使用SpringBoot演示了如何将PDF文件转换为一张或多张图片,这些示例演示了如何使用Java编程语言与其他开源技术集成,以实现各种文件格式之间的转换,感兴趣的小伙伴跟着小编一起来看看吧
    2024-08-08
  • mybatis条件语句中带数组参数的处理

    mybatis条件语句中带数组参数的处理

    这篇文章主要介绍了mybatis条件语句中带数组参数的处理方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • SpringCloud 2020-Ribbon负载均衡服务调用的实现

    SpringCloud 2020-Ribbon负载均衡服务调用的实现

    这篇文章主要介绍了SpringCloud 2020-Ribbon负载均衡服务调用的实现,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • Maven pom.xml scope属性的使用

    Maven pom.xml scope属性的使用

    在Maven中,scope属性用于定义依赖关系在不同生命周期阶段的行为,影响依赖在构建过程中的下载和使用,以及是否传递给其他项目,常见的scope值包括compile、provided、runtime和test等
    2025-01-01
  • SpringSecurity角色权限控制(SpringBoot+SpringSecurity+JWT)

    SpringSecurity角色权限控制(SpringBoot+SpringSecurity+JWT)

    本文主要介绍了SpringSecurity角色权限控制(SpringBoot+SpringSecurity+JWT),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-05-05
  • Java中使用正则检查有效日期的实现

    Java中使用正则检查有效日期的实现

    要判断一个字符串是否符合时间格式,可以使用正则表达式,本文主要介绍了Java中使用正则检查有效日期的实现,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • Java对时间的简单操作实例

    Java对时间的简单操作实例

    这篇文章主要介绍了Java对时间的简单操作,实例分析了针对java.util.Date的各类常见操作,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-01-01
  • java  Lock接口详解及实例代码

    java Lock接口详解及实例代码

    这篇文章主要介绍了java Lock接口详解及实例代码的相关资料,需要的朋友可以参考下
    2017-01-01
  • idea创建springboot项目,Application.java不能运行问题及解决

    idea创建springboot项目,Application.java不能运行问题及解决

    这篇文章主要介绍了idea创建springboot项目,Application.java不能运行问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • 详解JVM的内存对象介绍[创建和访问]

    详解JVM的内存对象介绍[创建和访问]

    这篇文章主要介绍了JVM的内存对象介绍[创建和访问],文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03

最新评论