Java多线程之Park和Unpark原理

yizhihongxing

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日

相关文章

  • Spring boot创建自定义starter的完整步骤

    Spring Boot创建自定义starter的完整步骤 在Spring Boot中,我们可以创建自定义starter来封装一些常用的功能,以便于在其他项目中重复使用。本文将详细讲解Spring Boot创建自定义starter的完整步骤,并提供两个示例。 1. 创建starter项目 以下是创建starter项目的基本流程: 在IDEA中创建一个Maven…

    Java 2023年5月15日
    00
  • Android Java crash 处理流程详解

    下面我来为你详细讲解“Android Java crash 处理流程详解”的完整攻略。 Android Java crash 处理流程详解 在Android开发中,我们经常会遇到应用程序由于各种原因而崩溃的情况。此时,我们需要进行相应的处理操作,才能有效减少应用程序的异常崩溃情况,提高用户体验。本文将详细介绍Android Java crash的处理流程,帮…

    Java 2023年5月25日
    00
  • 浅谈Maven包冲突的原理及解决方法

    下面我来详细讲解 “浅谈Maven包冲突的原理及解决方法” 这个话题。首先,我们需要了解一些基础概念。 什么是 Maven? Maven 是一个基于项目对象模型(Project Object Model,POM)的构建工具,可以用来管理项目依赖、构建项目、运行测试等。Maven 使用 jar 归档文件作为项目打包和分发的标准方式,同时支持多模块项目的构建。 …

    Java 2023年6月2日
    00
  • java实现时间与字符串之间转换

    下面是详细的讲解: 1. Java中时间字符串的格式化 Java中有一个比较强大的时间格式化类——SimpleDateFormat。使用它可以很方便地将时间字符串按照指定的格式进行格式化,也可以将时间转换为指定格式的字符串。 使用SimpleDateFormat时,需要先定义好时间字符串的格式。常用的格式符有: 格式符 说明 yyyy 年份,如:2019 M…

    Java 2023年5月20日
    00
  • 浅谈StringEntity 和 UrlEncodedFormEntity之间的区别

    十分感谢您对本网站的关注,下面是关于 “浅谈StringEntity 和 UrlEncodedFormEntity之间的区别” 的详细讲解。 StringEntity 和 UrlEncodedFormEntity 介绍 StringEntity 和 UrlEncodedFormEntity 是 Apache HttpClient 中两种常见的 HttpEnt…

    Java 2023年5月20日
    00
  • Java数组归纳总结

    Java数组归纳总结 在Java语言中,数组是一种非常常用的数据结构,可以用来存储同一类型的数据。本文将对Java数组进行归纳总结,包括数组的定义、初始化、遍历、复制、排序等常用操作,以及一些常见问题和解决方案。 数组的定义 Java数组是一种固定长度的数据结构,可以存储同一类型的数据。数组定义时需要指定数组的长度和类型。 声明一个长度为10,类型为int的…

    Java 2023年5月26日
    00
  • SpringBoot集成阿里巴巴Druid监控的示例代码

    下面是关于SpringBoot集成阿里巴巴Druid监控的示例代码的完整攻略。本文中包含以下内容: 什么是阿里巴巴Druid监控。 阿里巴巴Druid监控的优势与特点。 SpringBoot集成阿里巴巴Druid监控的步骤。 两个示例代码。 什么是阿里巴巴Druid监控 阿里巴巴Druid监控是一款对数据库进行监控的工具。它提供了丰富的监控数据和可视化界面,…

    Java 2023年5月20日
    00
  • 详解springboot解决CORS跨域的三种方式

    详解Spring Boot解决CORS跨域的三种方式 在Web应用程序中,我们经常需要解决CORS(跨域资源共享)问题。CORS是一种安全机制,用于限制跨域访问。本文将详细讲解Spring Boot解决CORS跨域的三种方式,并提供两个示例。 1. 添加依赖 在pom.xml文件中添加以下依赖: <dependency> <groupId&…

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