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集合之Set接口及其实现类精解

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

    Java 2023年5月18日
    00
  • SpringBoot注册Filter的两种实现方式

    下面是关于“SpringBoot注册Filter的两种实现方式”的完整攻略。 在SpringBoot中,有两种方式可以注册Filter: 1. 通过@Configuration注解注册Filter 在SpringBoot中,可以通过在@Configuration类中添加@Bean注解的方式注册Filter。示例代码如下: @Configuration pub…

    Java 2023年5月15日
    00
  • Java编程实现多线程TCP服务器完整实例

    Java编程实现多线程TCP服务器完整实例 简介 本文将通过Java代码实现一个多线程的TCP服务器,包含完整的代码以供参考。该服务器能够同时服务多个客户端,每个客户端都在独立的线程中运行。本文将介绍如何实现TCP Socket编程,以及如何使用Java多线程进行并发编程。 实现目标 实现一个多线程TCP服务器,支持多客户端同时连接。 服务器能够接受客户端连…

    Java 2023年5月19日
    00
  • java语言中封装类代码示例

    封装是Java中的一种特性,它将数据和方法作为一个整体封装在一个类中,从而实现了对象的封装和保护。在Java中,我们可以使用封装类来完成一些复杂数据类型的封装。下面是Java语言中封装类的代码示例攻略: 1. 创建封装类 创建一个封装类的关键在于定义一个类,并在类中定义私有变量和公有方法。私有变量可以通过公有方法来获取或修改。以下是一个简单的封装类示例: p…

    Java 2023年5月23日
    00
  • springboot整合jquery和bootstrap框架过程图解

    Spring Boot整合jQuery和Bootstrap框架的过程可以分为以下几个步骤: 引入jQuery和Bootstrap的依赖 配置静态资源路径 创建HTML页面 编写JavaScript代码 下面将详细介绍每个步骤,并提供两个示例。 1. 引入jQuery和Bootstrap的依赖 在Spring Boot应用程序中,可以使用Maven或Gradl…

    Java 2023年5月15日
    00
  • Java 中解决Unsupported major.minor version 51.0的问题

    当我们编写一个Java程序时,可能会遇到“Unsupported major.minor version 51.0”的错误。这是因为Java程序的class文件有不同的版本,如果运行该程序的Java虚拟机版本比程序编译的版本低,则会出现该错误。以下是解决该问题的完整攻略: 问题分析 我们先来了解一下错误信息的含义。在错误信息中,“major.minor ve…

    Java 2023年5月20日
    00
  • kafka topic 权限控制(设置删除权限)

    针对 Kafka topic 权限控制问题,可以采用以下步骤: 1. 启用Kafka权限控制特性 首先需要在Kafka的配置文件 server.properties 中启用权限控制特性。可以找到如下配置项: authorizer.class.name=kafka.security.auth.SimpleAclAuthorizer 该配置项使用 SimpleA…

    Java 2023年5月20日
    00
  • 大公司为什么禁止SpringBoot项目使用Tomcat?

    前言 在SpringBoot框架中,我们使用最多的是Tomcat,这是SpringBoot默认的容器技术,而且是内嵌式的Tomcat。同时,SpringBoot也支持Undertow容器,我们可以很方便的用Undertow替换Tomcat,而Undertow的性能和内存使用方面都优于Tomcat,那我们如何使用Undertow技术呢?本文将为大家细细讲解。 …

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