`
623deyingxiong
  • 浏览: 190195 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

保护块(Guarded Blocks)(Concurrency Tutorial 5)

 
阅读更多
保护块(Guarded Blocks)

线程间经常要协调并行操作,而最常见的协调方法就是保护块。保护块即:一块在执行前必须检查是否满足某一条件的代码。要做到这一点,要走这几步。

举个例子guardedJoy 是一个共享变量joy被置为true时才能执行的方法。理论上讲,guardedJoy可以写成一直循环判断joy知道joy为true。但循环很浪费资源,因为在等待执行期间它一直占用着CPU时间。
public void guardedJoy() {
    // Simple loop guard. Wastes
    // processor time. Don't do this!
    while(!joy) {}
    System.out.println("Joy has been achieved!");
}


更加高效的做法是使用object.wait()挂起当前线程。wait调用会一直挂起当前线程直到另一线程通知挂起线程有些特殊事件发生了——即使这个事件并不是挂起线程期待的:(译者注:所以你不能用if只判定一次条件而是用while确认当前条件确实满足了执行的条件,因为很可能唤醒你的线程并不清楚你在等待什么)
public synchronized guardedJoy() {
    // This guard only loops once for each special event, which may not
    // be the event we're waiting for.
    while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

注意:永远把守护卫士做成一个while循环而不是if判断。不要假设唤醒你的线程将你的执行条件置为true,或者执行条件会在唤醒后一直是true。

像很多挂起线程的方法一样,wait会抛出InterruptedException。在本例中,我们只是简单的忽略该异常——我们只关心joy的值。

为什么guardeJoy方法是同步的?假设d是wait所属的对象,当一个线程调用d.wait时,它必须拥有了d的内含锁(intrinsic lock)——否则将会抛出运行时异常。同步方法是获取内含锁的简单方法。

当wait被调用后,线程会释放锁并挂起。在后来某一时间,另一个线程会获取相同的锁,并调用notifyAll,通知所有等待该锁的线程有重要事情发生了:
public synchronized notifyJoy() {
    joy = true;
    notifyAll();
}

再后来,第二个线程释放了该锁,第一个线程再次获取该锁从wait中恢复。

注意:有第二个通知方法,notify,只能唤醒一个线程。因为notify不能指定哪个线程被唤醒,所以它只在大规模并发程序(massively parallel applications)中使用——有大量线程做同样的操作。在这种程序中,你不关心哪个线程被唤醒。

我们使用保护块来创建一个生产者消费者程序(Producer-Consumer)程序。这种程序在两个线程间共享数据:生产者生产数据,消费者消费数据。两个线程使用共享对象通信。协调是很必要的:生产者线程在生产者生产数据之前不能试图得到数据。生产者不能在旧数据未被消费之前交付新数据。

在本例中,共享数据是一系列的文本消息,通过一个叫Drop类型的对象传递:

public class Drop {
    // Message sent from producer
    // to consumer.
    private String message;
    // True if consumer should wait
    // for producer to send message,
    // false if producer should wait for
    // consumer to retrieve message.
    private boolean empty = true;

    public synchronized String take() {
        // Wait until message is
        // available.
        while (empty) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
        // Toggle status.
        empty = true;
        // Notify producer that
        // status has changed.
        notifyAll();
        return message;
    }

    public synchronized void put(String message) {
        // Wait until message has
        // been retrieved.
        while (!empty) {
            try { 
                wait();
            } catch (InterruptedException e) {}
        }
        // Toggle status.
        empty = false;
        // Store message.
        this.message = message;
        // Notify consumer that status
        // has changed.
        notifyAll();
    }
}

生产者线程,在Producer中定义。发送了一系列相似的消息。“DONE”表明所有的消息都已经发送完毕。为了模拟不能预期的真实世界的程序,生产者在生产消息之后暂停随机时间。
import java.util.Random;

public class Producer implements Runnable {
    private Drop drop;

    public Producer(Drop drop) {
        this.drop = drop;
    }

    public void run() {
        String importantInfo[] = {
            "Mares eat oats",
            "Does eat oats",
            "Little lambs eat ivy",
            "A kid will eat ivy too"
        };
        Random random = new Random();

        for (int i = 0;
             i < importantInfo.length;
             i++) {
            drop.put(importantInfo[i]);
            try {
                Thread.sleep(random.nextInt(5000));
            } catch (InterruptedException e) {}
        }
        drop.put("DONE");
    }
}


消费者线程定义在Consumer中,简单地得到消息并打印出来,直到得到“DONE”字符串。这个线程也暂停随机的时间间隔。
import java.util.Random;

public class Consumer implements Runnable {
    private Drop drop;

    public Consumer(Drop drop) {
        this.drop = drop;
    }

    public void run() {
        Random random = new Random();
        for (String message = drop.take();
             ! message.equals("DONE");
             message = drop.take()) {
            System.out.format("MESSAGE RECEIVED: %s%n", message);
            try {
                Thread.sleep(random.nextInt(5000));
            } catch (InterruptedException e) {}
        }
    }
}

最后,这里有一个主线程,定义在ProducerConsumerExample,加载了生产者和消费者线程。
public class ProducerConsumerExample {
    public static void main(String[] args) {
        Drop drop = new Drop();
        (new Thread(new Producer(drop))).start();
        (new Thread(new Consumer(drop))).start();
    }
}


注意Drop类是为了描述保护块而写的。为了避免重复造轮子(re-inventing the wheel),在你写自己的数据共享对象之前尝试一下使用Java Collections Framework中已经定义好的数据结构。欲知详情,请看Quesitons and Exercises 一节。
0
0
分享到:
评论

相关推荐

    scalac-guardedblocks-plugin:简单的Scala编译器插件

    `scalac-guardedblocks-plugin`是一个针对Scala编译器的轻量级插件,它的主要目标是提供一种机制,允许开发者定义"受保护的块"。这种块类似于Java中的try-catch结构,但更简洁且具有更广泛的用途。通过这个插件,...

    Go_Concurrency_Patterns.pdf

    Rob Pike 在演讲中提到,Go 的并发思想可以追溯到 1978 年 Tony Hoare 提出的 CSP 模型(Communicating Sequential Processes)以及 Edsger Dijkstra 的受保护命令(Guarded Commands)。在此之后,许多其他编程语言...

    《Java Concurrency in Practice》代码示例

    《Java Concurrency in Practice》是Java并发编程领域的一本经典著作,由Brian Goetz、Tim Peierls、Joshua Bloch、David Holmes和Doug Lea合著。这本书深入浅出地探讨了Java平台上的多线程和并发编程,提供了丰富的...

    laravel中的fillable和guarded属性详解

    选择使用哪一个取决于你的具体需求,白名单 `fillable` 更倾向于开放指定的属性,而黑名单 `guarded` 更侧重于保护敏感字段不被意外更新。正确地使用这两个属性能帮助你构建更安全、更可控的 Laravel 应用。

    java oracle并发官方教程

    Guarded Blocks是一种同步构造,它用于等待某个条件变为真。它通过检查条件和等待条件满足的循环来实现,通常利用wait/notify机制来控制线程的执行。 ### 不可变对象(Immutable Objects) 在并发编程中,不可变...

    guarded-array:边界检查数组

    保护阵列 用于帮助调试的慢速边界检查数组。 例子 var guard = require ( 'guarded-array' ) //First create any old array var array = [ 0 , 1 , 2 , 3 , 4 , 5 ] //Then we protect it using guard! var ...

    逻辑编程语言:GHC (Guarded Horn Clauses).zip

    史上最全编程语言全套教程,共99门编程语言,包括: 函数式编程语言 壳编程语言 常见编程语言 并行编程语言 数据分析编程语言 数据库查询语言 系统编程语言 脚本编程语言 逻辑编程语言 面向对象编程语言 ...

    guarded-string:防止在应用程序中意外地在字符串中引入XSSKong

    yarn add guarded-string 用法 重要的! 应该将其用于防止XSS攻击之类的东西,而不是用于隐藏敏感信息。 import guardedString from 'guarded-string' ; const myString = guardedString `My very important (but ...

    jcip-annotations:来自 Brian Goetz 等人的“Java Concurrency In Practice”一书中的注释代码

    5. **@GuardedBy** - 用来标记某个字段或方法应由哪个锁进行保护。如果一个字段被@GuardedBy("this")注解,那么访问这个字段的代码必须持有当前对象的监视器锁。 6. **@Volatile** - 通常在并发编程中,volatile...

    java并发编程经典书籍(英文版)

    - **并发模式**:介绍了如Producer-Consumer、Builder、Guarded Suspension等经典并发模式,并提供了Java实现。 - **并发集合**:详述了JUC(Java Util Concurrency)库,包括ArrayList、LinkedList、HashMap等...

    guarded-bayou-7383

    JAX-RS 模板应用程序这是使用 JAX-RS 的轻量级 RESTful API 的模板。 示例代码是获取当前时间的调用。在本地运行应用程序首先构建: $mvn clean install然后运行它: $ java -cp target/classes:target/dependency/*...

    纳米ASIC 的ESD保护电路基础-综合文档

    I/O端口通常设置有T形网络、GDT( Guarded Diode Transistor)或TVS(Transient Voltage Suppressor)等结构,以迅速分流ESD电流,避免进入核心电路。同时,内部电路的保护通常包括多晶硅栅极氧化层增强、源/漏掺杂...

    程序语言设计原理习题解答

    8.5 Guarded Commands 367 8.6 Conclusions 371 Summary • Review Questions • Problem Set •Programming Exercises 372 Chapter 9 Subprograms 377 9.1 Introduction 378 9.2 Fundamentals of ...

    周玉川-2017221302006-第三次作业1

    3. GS (Guarded Stack) 保护机制: GS 提供了一种栈保护机制,通过动态生成的canary值来检测栈溢出。在函数调用前后,canary值会被计算和验证,异常则会导致程序终止。为了增强安全性,还会额外加入cookie和栈中...

    GuardedCommands:用于模拟防护命令语言的Python程序

    防护命令语言六月7,2018 CS522正式语言和自动机20183111金李涵概括 2018年Spring正式语言和自动机理论项目1〜2 防护命令语言(以下称为GCL)解析和执行功能的实现语句语法说明中止论文说“做任何事情”,但实际上并...

    finally分析

    // Guarded body } __finally { // Termination handler } ``` 在这个语法结构中,不论`__try`块中的代码如何退出,`__finally`块中的代码都将被执行,除非使用了`ExitProcess`, `ExitThread`, `TerminateProcess...

    essential-java

    - **Guarded Blocks**:一种等待条件满足后再继续执行的线程同步机制。 - **不可变对象**:一旦创建后,其状态就不能被改变的对象,有助于简化并发编程。 - **高级并发对象**:如CountDownLatch、Semaphore等,...

    aqs-并发编程笔记.pdf

    并发编程笔记中的知识点涵盖了保护性暂停模式(Guarded Suspension Design Pattern)的定义、实现与分析,以及在Java中如何通过GuardedObject对象来实现多线程间的结果传递和超时处理。以下是详细的知识点梳理: 1....

    Laravel开发-attribute-purging

    在Laravel框架中,开发过程中有时需要控制模型的属性,以确保某些数据不会被保存到数据库中。这就是“attribute purging”(属性清除)的概念,它...在实践中,一定要考虑数据保护和隐私政策,确保符合相关法规和标准。

Global site tag (gtag.js) - Google Analytics