Java线程协作的两种方式小结

Java线程协作是指多个线程之间的相互协作来完成一个任务。在Java中,线程协作有两种方式:wait和notify/notifyAll。

1. wait和notify

当线程需要等待某个条件时,可以调用wait方法。调用wait方法会使线程进入等待状态,直到另一个线程调用notify或notifyAll方法来唤醒它。

示例1:wait和notify的简单使用

下面的代码演示了wait和notify的简单使用方式:

public class WaitNotifyDemo {
    public static void main(String[] args) throws InterruptedException{
        Object lock = new Object();

        Thread t1 = new Thread(new Runnable() {
            public void run() {
                synchronized(lock) {
                    try {
                        System.out.println("Thread 1 is waiting");
                        lock.wait();
                        System.out.println("Thread 1 is awake");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            public void run() {
                synchronized(lock) {
                    System.out.println("Thread 2 is running");
                    lock.notify();
                }
            }
        });

        t1.start();
        Thread.sleep(1000);
        t2.start();
    }
}

这个例子中,有两个线程t1t2,它们共享了一个锁对象lock。线程t1在同步代码块中调用了lock.wait()方法,会使它进入等待状态。线程t2在同步代码块中调用了lock.notify()方法,唤醒了处于等待状态中的线程t1。程序输出结果为:

Thread 1 is waiting
Thread 2 is running
Thread 1 is awake

可以看到,线程t1等待了一段时间后被唤醒,才继续向下执行。

示例2:wait和notify解决生产者消费者问题

下面的代码使用wait和notify解决了生产者消费者问题:

public class ProducerConsumerDemo {
    private static final int MAX_SIZE = 10;
    private List<Integer> list = new ArrayList<Integer>();

    public static void main(String[] args) {
        ProducerConsumerDemo demo = new ProducerConsumerDemo();
        demo.start();
    }

    public void start() {
        Thread producer = new Thread(new Producer());
        Thread consumer = new Thread(new Consumer());

        producer.start();
        consumer.start();
    }

    private class Producer implements Runnable {
        public void run() {
            while (true) {
                synchronized (list) {
                    while (list.size() >= MAX_SIZE) {
                        try {
                            list.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    int val = (int) (Math.random() * 100);
                    list.add(val);
                    System.out.println(Thread.currentThread().getName() + ": produce " + val);
                    list.notifyAll();
                }
            }
        }
    }

    private class Consumer implements Runnable {
        public void run() {
            while (true) {
                synchronized (list) {
                    while (list.size() == 0) {
                        try {
                            list.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    int val = list.remove(0);
                    System.out.println(Thread.currentThread().getName() + ": consume " + val);
                    list.notifyAll();
                }
            }
        }
    }

}

这个例子中,有一个ProducerConsumerDemo类,它有一个共享的list变量,用来存放生产者生产的数据。Producer类实现了生产者的功能,Consumer类实现了消费者的功能。在Producer类中,当list中的元素数量达到最大值MAX_SIZE时,会调用list.wait()方法让该线程进入等待状态,一直到Consumer类消耗掉list中的元素后,唤醒它。在Consumer类中,当list为空时,会调用list.wait()方法让该线程进入等待状态,一直到Producer类生产出新的元素后,唤醒它。程序输出结果为:

Thread-0: produce 81
Thread-1: consume 81
Thread-0: produce 87
Thread-1: consume 87
Thread-0: produce 25
Thread-1: consume 25
...

可以看到,生产者和消费者之间实现了协作,生产者在list中存放数据,消费者从list中取走生产者生产的数据,生产者消费者的数量达到了平衡。

2. park和unpark

JDK1.5新增了Lock对象和Condition接口,其中Condition的await()方法是park的封装,signal()方法是unpark的封装。

示例1:park和unpark的简单使用

下面的代码演示了park和unpark的简单使用方式:

public class ParkUnparkDemo {
    public static void main(String[] args) throws InterruptedException {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        Thread t1 = new Thread(new Runnable() {
            public void run() {
                lock.lock();
                try {
                    System.out.println("Thread 1 is waiting");
                    condition.await();
                    System.out.println("Thread 1 is awake");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            public void run() {
                lock.lock();
                try {
                    System.out.println("Thread 2 is running");
                    condition.signal();
                } finally {
                    lock.unlock();
                }
            }
        });

        t1.start();
        Thread.sleep(1000);
        t2.start();
    }
}

这个例子中,有两个线程t1t2,它们共享了一个Lock对象和一个Condition对象。线程t1在同步代码块中调用了condition.await()方法,会使它进入等待状态。线程t2在同步代码块中调用了condition.signal()方法,唤醒了处于等待状态中的线程t1。程序输出结果为:

Thread 1 is waiting
Thread 2 is running
Thread 1 is awake

可以看到,线程t1等待了一段时间后被唤醒,才继续向下执行。

示例2:park和unpark解决生产者消费者问题

下面的代码使用park和unpark解决了生产者消费者问题:

public class ProducerConsumerDemo1 {
    private static final int MAX_SIZE = 10;

    private Lock lock = new ReentrantLock();

    private Condition notFull = lock.newCondition();

    private Condition notEmpty = lock.newCondition();

    private LinkedList<Integer> list = new LinkedList<Integer>();

    public static void main(String[] args) {
        ProducerConsumerDemo1 demo = new ProducerConsumerDemo1();
        demo.start();
    }

    public void start() {
        Thread producer = new Thread(new Producer());
        Thread consumer = new Thread(new Consumer());

        producer.start();
        consumer.start();
    }

    private class Producer implements Runnable {
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (list.size() >= MAX_SIZE) {
                        notFull.await();
                    }
                    int val = (int) (Math.random() * 100);
                    list.add(val);
                    System.out.println(Thread.currentThread().getName() + ": produce " + val);
                    notEmpty.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    private class Consumer implements Runnable {
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (list.size() == 0) {
                        notEmpty.await();
                    }
                    int val = list.removeFirst();
                    System.out.println(Thread.currentThread().getName() + ": consume " + val);
                    notFull.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

这个例子中,有一个ProducerConsumerDemo1类,它有一个共享的list变量,用来存放生产者生产的数据。Producer类实现了生产者的功能,Consumer类实现了消费者的功能。在Producer类中,当list中的元素数量达到最大值MAX_SIZE时,会调用notFull.await()方法让该线程进入等待状态,一直到Consumer类消耗掉list中的元素后,唤醒它。在Consumer类中,当list为空时,会调用notEmpty.await()方法让该线程进入等待状态,一直到Producer类生产出新的元素后,唤醒它。程序输出结果为:

Thread-0: produce 19
Thread-1: consume 19
Thread-0: produce 87
Thread-1: consume 87
Thread-0: produce 74
Thread-1: consume 74
...

可以看到,生产者和消费者之间实现了协作,生产者在list中存放数据,消费者从list中取走生产者生产的数据,生产者消费者的数量达到了平衡。

以上就是Java线程协作的两种方式的完整攻略,希望能对你有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java线程协作的两种方式小结 - Python技术站

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

相关文章

  • 使用java实现网络爬虫

    使用Java实现网络爬虫可以分为以下步骤: 1. 定义爬虫开始的入口URL 入口URL是爬虫开始爬取网页的地方,可以是指定的网页或是多个网页列表。定义入口URL的方式可以使用字符串形式,也可以使用类似Java URL类的URL对象。比如: String startUrl = "https://example.com"; URL url =…

    Java 2023年5月18日
    00
  • java基础的详细了解第七天

    Java基础的详细了解第七天攻略 在第七天的学习中,我们将了解Java的异常处理机制。异常是指程序运行期间发生的不正常情况,如除数为0,数组越界等等。在Java中,异常处理机制提供了异常的捕获、处理和抛出的操作,可以帮助我们提高程序的健壮性。 异常类的层次结构 在Java中,异常类是按照树形结构进行组织的,最顶层是Throwable类,下面分为两个子类,分别…

    Java 2023年5月23日
    00
  • 解决Jquery下拉框数据动态获取的问题

    当使用 jQuery 实现下拉框时,我们可能需要动态获取数据来填充下拉框选项。如果不处理好动态获取数据的方法,就会导致下拉框无法成功渲染出数据,或渲染出错误的数据。 以下是解决 Jquery 下拉框数据动态获取的问题的完整攻略,包含两个示例: 1. ajax方式获取数据 一种比较常见的方式是使用 ajax 请求来获取数据。我们可以使用 jQuery 的 $.…

    Java 2023年5月20日
    00
  • Spring Jpa多数据源工程配置过程解析

    下面就详细讲解“Spring Jpa多数据源工程配置过程解析”的完整攻略。 一、Spring Jpa多数据源工程配置过程解析 1.1 背景 在实际开发中,有时候我们需要使用多个数据源,分别连接不同数据库进行数据库操作。Spring Jpa框架提供了配置多数据源的方法,本文将详细介绍配置过程。 1.2 配置步骤 添加Maven依赖 <dependency…

    Java 2023年6月3日
    00
  • Jdbc连Sybase数据库的几种方法

    JDBC是Java数据库连接的标准接口,在Java程序中可通过JDBC来访问多种类型的数据库。本文将针对Sybase数据库,介绍几种连接Sybase数据库的方法,以及代码示例。 1. 准备工作 在使用JDBC连接Sybase数据库之前,需要先进行准备工作,包括安装Sybase数据库、Sybase驱动程序。 1.1 安装Sybase数据库 Sybase数据库是…

    Java 2023年6月16日
    00
  • Java实现JDBC连接数据库简单案例

    下面我将详细讲解Java实现JDBC连接数据库简单案例的完整攻略。 第一步:导入JDBC驱动 JDBC驱动包可以从官网下载,下载完成后需要将其导入到项目中。导入方式有两种,分别是将其放入CLASSPATH中或者将其直接加入项目中,本文采用第二种方式。 第二步:建立数据库连接 在Java中使用JDBC驱动连接数据库,需要调用驱动程序提供的DriverManag…

    Java 2023年5月19日
    00
  • java中如何截取字符串最后一位

    在Java中,可以使用String类中的substring()方法来截取字符串。若想截取字符串的最后一位,则可以结合字符串的长度和substring()方法来实现。 具体实现步骤如下: 获取字符串的长度,可以使用String类中的length()方法。 将length()方法返回的结果减1,得到字符串最后一位的下标位置。 使用substring()方法来截取…

    Java 2023年5月27日
    00
  • 详解Java中的流程控制

    下面是“详解Java中的流程控制”的攻略: 一、Java中的流程控制 Java中的流程控制,主要分为三类:选择结构、循环结构和跳转结构。 1. 选择结构 选择结构用于控制程序按照条件执行不同的代码块。Java中的选择结构主要包括if语句和switch语句。 if语句 if语句用来在某种条件下执行一段代码。它的基本语法格式如下: if(条件){ // 执行代码…

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