java-并发-保护代码块

  线程经常需要协调其动作,最常用的协调方法就是保护代码块,该代码块以一个条件判断开始,当判断为true时才能开始执行。

  假设一个方法guradedJoy必须等到变量joy由其他线程设定过后才能开始执行,这样的方法,理论上简单的循环直到满足条件就可以了,但是这样的循环是比较浪费资源的,因为等待的时候是在不停的执行的。

public void guardedJoy() {
    // 简单的循环保护,不建议这样做,因为比较浪费处理器时间
    while(!joy) {}
    System.out.println("Joy has been achieved!");
}

  一个更高效的方法就是调用Object类的wait方法来暂停当前的线程,wait方法的调用只有在另外一个线程发出特定的事件已经发生的通知时才会返回,但该特定的事件不一定是这个线程正在等待的事件:

public synchronized void guardedJoy() {
    //  该保护用法只对于特定的事件循环一次,可以不是正在等待的事件
    while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

  注意:在一个测试一个等待条件的循环中请使用wait方法,不要认为中断是针对于你所等待的条件的,或者等待的条件依然为true。
  像其他暂停执行的方法一样,wait方法可以抛出InterruptException,当一个线程调用一个对象的wait方法时,该线程必须拥有该对象的内置锁(如上例中guardedJoy方法为同步方法),否则将抛出错误,在同步方法中调用wait方法是一个请求内置锁的简单途径。

  当调用wait方法时,线程释放锁并暂停执行。在接下来的时间里,另外一个线程将请求同一个锁并调用Object类的notifyAll,通知等待该锁的所有线程一些已经发生的重要事情。当第二个线程释放了锁之后,第一个线程就会请求该锁并从调用的wait方法中返回。

  注意:还有另外一个通知的方法,notify,该方法只唤醒一个线程,因为notify方法无法指定要被唤醒的线程,这个方法只在大规模并行程序中有用,也就是说,程序中有许多线程做相同的事情,这样的程序中,不必在意唤醒的是哪个线程。

  一下利用保护代码块来创建一个简单的生产者-消费者程序,给程序在两个线程之间分享数据,生产者产生数据,而消费者对数据进行处理,两个线程之间通过一个共享的对象进行通信。线程之间的协调是很重要的:消费者在生产者传递数据之间不能检索数据,而生产者在就得数据被消费者检索之前不能传递新的数据。

  以下定义一个类用来创建通信的对象:

public class Drop {
    // 生产者发给消费者的信息
    private String message;
    // 如果消费者需要等待消费者发送信息则为true,如果需要等待消费者接受信息则为false
    private boolean empty = true;

    public synchronized String take() {
        // 等待信息可用
        while (empty) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
        // 转换状态
        empty = true;
        // 通知生产者信息已被检索
        notifyAll();
        return message;
    }

    public synchronized void put(String message) {
        // 等待信息被检索
        while (!empty) {
            try {
                wait();
            } catch (InterruptedException e) {}
        }
        // 转换状态
        empty = false;
        // Store message.
        this.message = message;
        // 通知消费者状态已经改变
        notifyAll();
    }
}

  定义一个Producer类,发送一系列熟悉的信息,字符串“DONE”表明所有的信息已经被发送,为模拟真实的程序,发送信息的间歇Producer类会停顿随机的一段时间。

//导入随机类,以产生一个随机数
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类的创建是为了演示保护代码块的使用,java的集合框架(Java Collections Framework)提供了数据分享的类,提供丰富的功能,因此可以不必定义自己的类。

  

时间: 2024-10-05 04:19:26

java-并发-保护代码块的相关文章

java中静态代码块的用法 static用法详解

(一)java 静态代码块 静态方法区别一般情况下,如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的;需要在项目启动的时候就初始化,在不创建对象的情况下,其他程序来调用的时候,需要使用静态方法,这种代码是被动执行的. 静态方法在类加载的时候 就已经加载 可以用类名直接调用比如main方法就必须是静态的 这是程序入口两者的区别就是:静态代码块是自动执行的;静态方法是被调用的时候才执行的.静态方法(1)在Java里,可以定义一个不需要创建对象的方法,这种方法就是

用亲身经历告诉你,在你的并发程序代码块中,最好最好不要有引用类型

用亲身经历告诉你,在你的并发程序代码块中,最好最好不要有引用类型,必要的string类型还是可以的.目前正在把自己前段时间写的并发程序放到自己的项目中,以提高速度,由于我的项目是与移动对接的,因此,询问了移动的接口能不能响应高并发之后,并且得到了肯定的答复,我就开始着手demo设计了,但是一直出错,一直有问题,后来想到,可能是引用类型的问题,因为我的并发代码需要不断地给一个类实例对象的某个字段赋值,然后去调用移动接口,我就捉摸着,可能是因为并发太快了,然后这个引用类型赋值的之后,没有来的及更换内

Java中动态代码块,构造代码块,静态代码块之间的执行顺序

Java中动态代码块,构造代码块,静态代码块之间的执行顺序 /** * Java中动态代码块,构造代码块,静态代码块之间的执行顺序 * @author Administrator * */ class HelloA { public HelloA() { System.out.println("HelloA"); } { System.out.println("I'm A class"); } static { System.out.println("st

java提高篇-----代码块

自己添加部分 构造代码块在变量初始化之后执行:也符合java编程思想中  ---- 静态构造代码块>变量初始化>普通构造代码块 在编程过程中我们可能会遇到如下这种形式的程序: public class Test { { //// } } 这种形式的程序段我们将其称之为代码块,所谓代码块就是用大括号({})将多行代码封装在一起,形成一个独立的数据体,用于实现特定的算法.一般来说代码块是不能单独运行的,它必须要有运行主体.在Java中代码块主要分为四种: 一. 普通代码块 普通代码块是我们用得最多

java中静态代码块的用法—— static用法详解

(一)java 静态代码块 静态方法区别一般情况下,如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的;需要在项目启动的时候就初始化,在不创建对象的情况下,其他程序来调用的时候,需要使用静态方法,这种代码是被动执行的. 静态方法在类加载的时候 就已经加载 可以用类名直接调用比如main方法就必须是静态的 这是程序入口两者的区别就是:静态代码块是自动执行的;静态方法是被调用的时候才执行的. 静态方法(1)在Java里,可以定义一个不需要创建对象的方法,这种方法就

Java中普通代码块,构造代码块,静态代码块区别及代码示例

//普通代码块:在方法或语句中出现的{}就称为普通代码块.普通代码块和一般的语句执行顺序由他们在代码中出现的次序决定--"先出现先执行"public class CodeBlock01{ public static void main(String[] args){ { int x=3; System.out.println("1,普通代码块内的变量x="+x); } int x=1; System.out.println("主方法内的变量x="

Java误区: 静态代码块,当把类将被载入到自己主动运行?

JAVA静态代码块会在类被载入时自己主动运行? 非常多Java开发人员的思想,被这个思想深深的轮奸了n遍,传播这个错误思想的博客,在网上一堆,越来越多的人被轮奸. 如:http://blog.csdn.net/leeyu35/article/details/7755304 那么我们程序来证明这句话是错误的: class MyClass1 { static {//静态块 System.out.println("static block "); } } public class Main

[转载] java中静态代码块的用法 static用法详解

(一)java 静态代码块 静态方法区别一般情况下,如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的;需要在项目启动的时候就初始化,在不创建对象的情况下,其他程序来调用的时候,需要使用静态方法,这种代码是被动执行的. 静态方法在类加载的时候 就已经加载 可以用类名直接调用比如main方法就必须是静态的 这是程序入口两者的区别就是:静态代码块是自动执行的;静态方法是被调用的时候才执行的. 静态方法(1)在Java里,可以定义一个不需要创建对象的方法,这种方法就

java中静态代码块的用法 static用法详解和static静态导入

(一)java 静态代码块 静态方法区别一般情况下,如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的;需要在项目启动的时候就初始化,在不创建对象的情况下,其他程序来调用的时候,需要使用静态方法,这种代码是被动执行的. 静态方法在类加载的时候 就已经加载 可以用类名直接调用比如main方法就必须是静态的 这是程序入口两者的区别就是:静态代码块是自动执行的;静态方法是被调用的时候才执行的. 静态方法(1)在Java里,可以定义一个不需要创建对象的方法,这种方法就