Java多线程之Park和Unpark原理

Java多线程中的Park和Unpark是线程同步关键字,常用于线程间等待和通知的操作。在本次攻略中,将深入讲解Park和Unpark的实现原理,并提供两条示例说明。

Park和Unpark的基本概念

Park和Unpark是Java多线程中用于实现线程等待和通知机制的一对关键字。

其中,Park的作用是使线程等待,将其挂起,并将线程的状态设置为WAITING。而Unpark的作用是唤醒被Park挂起的线程。

Park和Unpark的实现原理

Park和Unpark需要借助于Unsafe类实现。Unsafe是Java中一个被隐藏的类,提供了一些跨越JVM的本地方法,可以直接操作内存和线程。因此,使用Unsafe类可以实现Park和Unpark的功能。

具体实现方法如下:

  • Park方法的实现

在使用Park方法时,会使当前线程进入挂起状态,并设置线程状态为WAITING。具体实现代码如下:

public static void park(boolean isAbsolute, long time) {
    Unsafe unsafe = Unsafe.getUnsafe();
    Thread currentThread = Thread.currentThread();
    if(isAbsolute) {
        unsafe.park(true, time);
    }else {
        unsafe.park(false, time * 1000 * 1000);
    }
    currentThread.setParkBlocker(null);
    currentThread.interrupted();
}

在这个代码中,Unsafe类的park方法用于将当前线程挂起,直到其他线程调用Unpark方法唤醒它。isAbsolute用于指定时间是否为绝对时间,time表示等待时间。currentThread则表示当前线程。

  • Unpark方法的实现

Unpark方法是Park方法的“解挂”操作,用于唤醒被Park挂起的线程,使其继续执行。具体实现代码如下:

public static void unpark(Thread thread) {
    Unsafe unsafe = Unsafe.getUnsafe();
    unsafe.unpark(thread);
}

在这个代码中,Unsafe类的unpark方法用于唤醒指定的线程。

示例说明

下面提供两个示例说明,以便更好地理解Park和Unpark的实现原理。

示例一:生产者-消费者模型

这个示例使用Park和Unpark实现了一个简单的生产者-消费者模型,其中Park和Unpark用于线程的等待和唤醒。具体代码如下:

public class ProducerConsumer {

    static Buffer buffer = new Buffer();

    public static class Producer implements Runnable {

        @Override
        public void run() {
            while (true) {
                buffer.produce();
            }
        }
    }

    public static class Consumer implements Runnable {

        @Override
        public void run() {
            while (true) {
                buffer.consume();
            }
        }
    }

    static class Buffer {

        final Lock lock = new ReentrantLock();
        final Condition notFull  = lock.newCondition(); 
        final Condition notEmpty = lock.newCondition(); 

        final int size = 10;
        List<String> data = new ArrayList<>();

        void produce() {
            lock.lock();
            try {
                while (data.size() == size)
                    notFull.await(); 
                String message = "msg-" + Math.random();
                data.add(message);
                System.out.println("Produce " + message);
                notEmpty.signalAll(); 
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        void consume() {
            lock.lock();
            try {
                while (data.size() == 0)
                    notEmpty.await();
                String message = data.remove(0);
                System.out.println("Consume " + message);
                notFull.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

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

        Thread t1 = new Thread(new Producer());
        t1.start();

        Thread t2 = new Thread(new Consumer());
        t2.start();

        Thread.sleep(5000L);
        t1.interrupt(); 
        t2.interrupt(); 
    }
}

在这个示例中,Buffer实现了对数据的添加和移除,Producer和Consumer分别为生产者和消费者线程,使用Lock和Condition实现等待和通知的机制。在produce和consume方法中,使用了Park和Unpark方法实现线程等待和唤醒的操作。

示例二:线程池

这个示例使用Park和Unpark实现了一个简单的线程池,其中Park和Unpark用于实现线程的等待和唤醒。具体代码如下:

public class MyThreadPool {

    private final BlockingQueue<Runnable> queue;

    public static final int DEFAULT_SIZE = 10;

    private Set<MyThread> threadSet = new HashSet<>();

    public MyThreadPool(int size) {
        queue = new LinkedBlockingQueue<Runnable>(size);
        for (int i = 0; i < size; i++) {
            MyThread task = new MyThread(queue);
            threadSet.add(task);
            task.start();
        }
    }

    public void execute(Runnable command) throws InterruptedException {
        if (command != null) {
            queue.put(command);
        }
    }

    public void destroy() {
        for (MyThread thread : threadSet) {
            thread.stopThread();
        }
    }

    static class MyThread extends Thread {
        private volatile boolean running = true;
        private BlockingQueue<Runnable> queue;

        public MyThread(BlockingQueue<Runnable> queue) {
            this.queue = queue;
        }

        public void run() {
            while (running) {
                try {
                    if (queue.isEmpty()) {
                        LockSupport.park(thread);
                    }
                    Runnable task = queue.poll();
                    if (task != null) {
                        task.run();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        public void stopThread() {
            running = false;
            LockSupport.unpark(this);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThreadPool threadPool = new MyThreadPool(DEFAULT_SIZE);
        for (int i = 0; i < 20; i++) {
            threadPool.execute(new Runnable() {
                public void run() {
                    System.out.println(Thread.currentThread().getName() + " is running.");
                    try {
                        Thread.sleep(30000L);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                    System.out.println(Thread.currentThread().getName() + " is finished.");
                }
            });
        }
        threadPool.destroy();
    }
}

在这个示例中,MyThreadPool实现了一个简单的线程池,使用BlockingQueue作为任务队列,MyThread则实现了线程的执行和停止。在MyThread中,如果任务队列为空,使用LockSupport的Park方法暂停当前线程。当线程被调用stopThread方法时,使用LockSupport的Unpark方法唤醒正在等待的线程继续执行。

总结

本次攻略详细讲解了Java多线程中的Park和Unpark的实现原理,并提供了两个示例。通过理解Park和Unpark的实现原理,我们可以更好地理解线程的等待和唤醒机制,并能够将其应用到实际的多线程编程中。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java多线程之Park和Unpark原理 - Python技术站

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

相关文章

  • Java 如何将网络资源url转化为File文件

    将网络资源URL转换为File文件需要借助Java中的IO流和网络操作类。下面将会详细介绍Java如何将网络资源URL转化为File文件的完整攻略。 步骤一:获取URL 首先要获取网络资源的URL,可以使用Java中的URL类。以下示例演示如何获取指定URL的网络资源: import java.net.*; public class GetUrlConten…

    Java 2023年5月19日
    00
  • Mybatis实现SQL存储流程详解

    下面是对“Mybatis实现SQL存储流程详解”的完整攻略: Mybatis 实现 SQL 存储流程 配置 Mybatis 使用 Mybatis,首先需要配置 Mybatis。Mybatis 的配置可以是 XML 文件形式,也可以是 Java 类形式。 以下是一个示例 Mybatis 配置文件,“config.xml”: <?xml version=&…

    Java 2023年5月19日
    00
  • Springboot启动后执行方法小结

    SpringBoot启动后执行方法是开发SpringBoot应用程序时经常涉及到的一个知识点。本文将详细讲解SpringBoot启动后执行方法的完整攻略,包括执行方式、参数解析和应用场景。 一、执行方式 SpringBoot启动后执行方法有两种执行方式,分别为实现CommandLineRunner接口和使用ApplicationRunner接口。 1.1 实…

    Java 2023年5月31日
    00
  • Spring Security实现自定义访问策略

    Spring Security是一个开源的安全框架,提供了许多安全方案,其中自定义访问策略是Spring Security的核心之一。下面将详细讲解在Spring Security中实现自定义访问策略的完整攻略,包括以下内容: Spring Security的基本概念 自定义访问策略的原理 实现自定义访问策略的步骤 示例说明 1. Spring Securi…

    Java 2023年6月3日
    00
  • 学习Java之如何正确地跳出循环结构

    学习Java,循环结构是非常重要的知识点。而在使用循环的过程中,我们有时候需要跳出循环,以停止或跳过一些迭代,这时候就需要使用跳出循环的语句。本文将详细讲解如何正确地跳出循环结构。 标准循环结构 Java 中常见的循环结构包括 for、while 和 do-while 循环结构。它们的语法分别为: for (初始化; 布尔表达式; 更新) { // 代码块 …

    Java 2023年5月26日
    00
  • JavaWeb实体类转为json对象的实现方法

    下面是详细讲解“JavaWeb实体类转为json对象的实现方法”的完整攻略: 一、所需工具 在实现JavaWeb实体类转为json对象的过程中,需要使用以下工具: Java开发工具(如Eclipse、Intellij IDEA等) fastjson开源库 二、fastjson简介 fastjson是阿里巴巴公司开源的一个JSON格式数据处理工具。具有体积小、…

    Java 2023年5月26日
    00
  • SpringBoot快速入门及起步依赖解析(实例详解)

    SpringBoot快速入门及起步依赖解析 SpringBoot是一个快速构建基于Spring的应用程序的框架。在本文中,我们将为您介绍如何快速入门以及如何使用起步依赖项。 快速入门 在使用SpringBoot之前,我们需要首先配置Maven或者Gradle来构建我们的应用程序。这里我们以Maven为例。 创建一个maven项目 使用Maven创建一个新项目…

    Java 2023年5月15日
    00
  • Spring Web MVC框架学习之配置Spring Web MVC

    下面是关于“Spring Web MVC框架学习之配置Spring Web MVC”的完整攻略,包含两个示例说明。 Spring Web MVC框架学习之配置Spring Web MVC Spring Web MVC是一个基于MVC模式的Web框架,可以帮助我们快速开发Web应用程序。本文将介绍如何配置Spring Web MVC框架。 添加依赖 首先,我们…

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