作者归档

并发集合(五)使用线程安全的、带有延迟元素的列表

声明:本文是《 Java 7 Concurrency Cookbook 》的第六章,作者: Javier Fernández González     译者:许巧辉 校对:方腾飞

使用线程安全的、带有延迟元素的列表

DelayedQueue类是Java API提供的一种有趣的数据结构,并且你可以用在并发应用程序中。在这个类中,你可以存储带有激活日期的元素。方法返回或抽取队列的元素将忽略未到期的数据元素。它们对这些方法来说是看不见的。

为了获取这种行为,你想要存储到DelayedQueue类中的元素必须实现Delayed接口。这个接口允许你处理延迟对象,所以你将实现存储在DelayedQueue对象的激活日期,这个激活时期将作为对象的剩余时间,直到激活日期到来。这个接口强制实现以下两种方法:

  • compareTo(Delayed o):Delayed接口继承Comparable接口。如果执行这个方法的对象的延期小于作为参数传入的对象时,该方法返回一个小于0的值。如果执行这个方法的对象的延期大于作为参数传入的对象时,该方法返回一个大于0的值。如果这两个对象有相同的延期,该方法返回0。
  • getDelay(TimeUnit unit):该方法返回与此对象相关的剩余延迟时间,以给定的时间单位表示。TimeUnit类是一个枚举类,有以下常量:DAYS、HOURS、 MICROSECONDS、MILLISECONDS、 MINUTES、 NANOSECONDS 和 SECONDS。

阅读全文

并发集合(四)用优先级对使用阻塞线程安全的列表排序

声明:本文是《 Java 7 Concurrency Cookbook 》的第六章,作者: Javier Fernández González     译者:许巧辉 校对:方腾飞

用优先级对使用阻塞线程安全的列表排序

一个典型的需求是,当你需要使用一个有序列表的数据结构时,Java提供的PriorityBlockingQueue类就拥有这种功能。

你想要添加到PriorityBlockingQueue中的所有元素必须实现Comparable接口。这个接口有一个compareTo()方法,它接收同样类型的对象,你有两个比较的对象:一个是执行这个方法的对象,另一个是作为参数接收的对象。如果本地对象小于参数,则该方法返回小于0的数值。如果本地对象大于参数,则该方法返回大于0的数值。如果本地对象等于参数,则该方法返回等于0的数值。 阅读全文

并发集合(三)使用阻塞线程安全的列表

声明:本文是《 Java 7 Concurrency Cookbook 》的第六章,作者: Javier Fernández González  译者:许巧辉

使用阻塞线程安全的列表

列表(list)是最基本的集合。一个列表中的元素数量是不确定的,并且你可以添加、读取和删除任意位置上的元素。并发列表允许不同的线程在同一时刻对列表里的元素进行添加或删除,而不会产生任何数据不一致的问题。

在这个指南中,你将学习如何在你的并发应用程序中使用阻塞的列表。阻塞列表与非阻塞列表的主要区别是,阻塞列表有添加和删除元素的方法,如果由于列表已满或为空而导致这些操作不能立即进行,它们将阻塞调用的线程,直到这些操作可以进行。Java包含实现阻塞列表的LinkedBlockingDeque类。

你将使用以下两种任务来实现例子:

  • 添加大量数据到列表。
  • 从同一个列表中删除大量的数据。

阅读全文

并发集合(二)使用非阻塞线程安全的列表

声明:本文是《 Java 7 Concurrency Cookbook 》的第六章,作者: Javier Fernández González     译者:许巧辉

使用非阻塞线程安全的列表

列表(list)是最基本的集合。一个列表有不确定的元素数量,并且你可以添加、读取和删除任意位置上的元素。并发列表允许不同的线程在同一时刻对列表的元素进行添加或删除,而不会产生任何数据不一致(问题)。

在这个指南中,你将学习如何在你的并发应用程序中使用非阻塞列表。非阻塞列表提供这些操作:如果操作不能立即完成(比如,你想要获取列表的元素而列表却是空的),它将根据这个操作抛出异常或返回null值。Java 7引进实现了非阻塞并发列表的ConcurrentLinkedDeque类。

我们将使用以下两种不同任务来实现一个例子:

  • 大量添加数据到列表
  • 在同个列表中,大量删除数据

阅读全文

并发集合(一)引言

声明:本文是《 Java 7 Concurrency Cookbook 》的第六章,作者: Javier Fernández González     译者:许巧辉 校对:方腾飞

在本章中,我们将包含:

引言

在编程中,数据结构是一种基本的元素。几乎每个程序都使用一个或多个数据结构类型来存储和管理它们的数据。Java API提供了Java集合框架(Java Collections framework),它包括可以用来实现许多不同的数据结构的接口、类和算法,你可以在程序中使用它们。

当你需要在并发程序中使用数据集合时,你必须十分小心的选择实现。大多数集合数并不适合用在并发应用程序中,因为它们没有控制并发访问数据。如果一些并发任务共享一个数据结构,而这个数据结构并不适合用在并发任务中,你将会有数据不一致的错误,这将影响到程序的正确运行。ArrayList类就是这种数据结构的一个例子。
阅读全文

基本线程同步(八)在Lock中使用多个条件

声明:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González     译者:许巧辉 校对:方腾飞

在Lock中使用多个条件

一个锁可能伴随着多个条件。这些条件声明在Condition接口中。 这些条件的目的是允许线程拥有锁的控制并且检查条件是否为true,如果是false,那么线程将被阻塞,直到其他线程唤醒它们。Condition接口提供一种机制,阻塞一个线程和唤醒一个被阻塞的线程。

在并发编程中,生产者与消费者是经典的问题。我们有一个数据缓冲区,一个或多个数据生产者往缓冲区存储数据,一个或多个数据消费者从缓冲区中取出数据,正如在这一章中前面所解释的一样。

在这个指南中,你将学习如何通过使用锁和条件来实现生产者与消费者问题。 阅读全文

基本线程同步(七)修改Lock的公平性

声明:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González  译者:许巧辉 校对:方腾飞

修改Lock的公平性

在ReentrantLock类和 ReentrantReadWriteLock类的构造器中,允许一个名为fair的boolean类型参数,它允许你来控制这些类的行为。默认值为 false,这将启用非公平模式。在这个模式中,当有多个线程正在等待一把锁(ReentrantLock或者 ReentrantReadWriteLock),这个锁必须选择它们中间的一个来获得进入临界区,选择任意一个是没有任何标准的。true值将开启公平 模式。在这个模式中,当有多个线程正在等待一把锁(ReentrantLock或者ReentrantReadWriteLock),这个锁必须选择它们 中间的一个来获得进入临界区,它将选择等待时间最长的线程。考虑到之前解释的行为只是使用lock()和unlock()方法。由于tryLock()方 法并不会使线程进入睡眠,即使Lock接口正在被使用,这个公平属性并不会影响它的功能。

在这个指南中,我们将修改使用Lock同步代码块食谱示例来使用这个属性,并且观察公平与非公平模式之间的差别。 阅读全文

基本线程同步(六)使用读/写锁同步数据访问

声明:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González     译者:许巧辉

使用读/写锁同步数据访问

锁所提供的最重要的改进之一就是ReadWriteLock接口和唯一 一个实现它的ReentrantReadWriteLock类。这个类提供两把锁,一把用于读操作和一把用于写操作。同时可以有多个线程执行读操作,但只有一个线程可以执行写操作。当一个线程正在执行一个写操作,不可能有任何线程执行读操作。

在这个指南中,你将会学习如何使用ReadWriteLock接口实现一个程序,使用它来控制访问一个存储两个产品价格的对象。 阅读全文

基本线程同步(五)使用Lock同步代码块

声明:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González     译者:许巧辉 校对:方腾飞

使用Lock同步代码块

Java提供另外的机制用来同步代码块。它比synchronized关键字更加强大、灵活。它是基于Lock接口和实现它的类(如ReentrantLock)。这种机制有如下优势:

  • 它允许以一种更灵活的方式来构建synchronized块。使用synchronized关键字,你必须以结构化方式得到释放synchronized代码块的控制权。Lock接口允许你获得更复杂的结构来实现你的临界区。
  • Lock 接口比synchronized关键字提供更多额外的功能。新功能之一是实现的tryLock()方法。这种方法试图获取锁的控制权并且如果它不能获取该锁,是因为其他线程在使用这个锁,它将返回这个锁。使用synchronized关键字,当线程A试图执行synchronized代码块,如果线程B正在执行它,那么线程A将阻塞直到线程B执行完synchronized代码块。使用锁,你可以执行tryLock()方法,这个方法返回一个 Boolean值表示,是否有其他线程正在运行这个锁所保护的代码。
  • 当有多个读者和一个写者时,Lock接口允许读写操作分离。
  • Lock接口比synchronized关键字提供更好的性能。

在这个指南中,你将学习如何通过锁来同步代码块和通过Lock接口及其实现者ReentrantLock类来创建临界区,实现一个程序来模拟打印队列。
阅读全文

基本线程同步(四)在同步代码中使用条件

声明:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González     译者:许巧辉 校对:方腾飞

在同步代码中使用条件

在并发编程中的一个经典问题是生产者与消费者问题,我们有一个数据缓冲区,一个或多个数据的生产者在缓冲区存储数据,而一个或多个数据的消费者,把数据从缓冲区取出。

由于缓冲区是一个共享的数据结构,我们必须采用同步机制,比如synchronized关键字来控制对它的访问。但是我们有更多的限制因素,如果缓冲区是满的,生产者不能存储数据,如果缓冲区是空的,消费者不能取出数据。

对于这些类型的情况,Java在Object对象中提供wait(),notify(),和notifyAll() 方法的实现。一个线程可以在synchronized代码块中调用wait()方法。如果在synchronized代码块外部调用wait()方法,JVM会抛出IllegalMonitorStateException异常。当线程调用wait()方法,JVM让这个线程睡眠,并且释放控制 synchronized代码块的对象,这样,虽然它正在执行但允许其他线程执行由该对象保护的其他synchronized代码块。为了唤醒线程,你必 须在由相同对象保护的synchronized代码块中调用notify()或notifyAll()方法。

在这个指南中,你将学习如何通过使用synchronized关键字和wait()和notify(),notifyAll()方法实现生产者消费者问题。 阅读全文

基本线程同步(三)在同步的类里安排独立属性

声明:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González     译者:许巧辉  校对:方腾飞

在同步的类里安排独立属性

当你使用synchronized关键字来保护代码块时,你必须通过一个对象的引用作为参数。通常,你将会使用this关键字来引用执行该方法的对象,但是你也可以使用其他对象引用。通常情况下,这些对象被创建只有这个目的。比如,你在一个类中有被多个线程共享的两个独立属性。你必须同步访问每个变量,如果有一个线程访问一个属性和另一个线程在同一时刻访问另一个属性,这是没有问题的。

在这个指南中,你将学习如何解决这种情况的一个例子,编程模拟一家电影院有两个屏幕和两个售票处。当一个售票处出售门票,它们用于两个电影院的其中一个,但不能用于两个,所以在每个电影院的免费席位的数量是独立的属性。 阅读全文

基本线程同步(二)同步方法

声明:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González     译者:许巧辉 校对:方腾飞

同步方法

在这个指南中,我们将学习在Java中如何使用一个最基本的同步方法,即使用 synchronized关键字来控制并发访问方法。只有一个执行线程将会访问一个对象中被synchronized关键字声明的方法。如果另一个线程试图访问同一个对象中任何被synchronized关键字声明的方法,它将被暂停,直到第一个线程结束方法的执行。

换句话说,每个方法声明为synchronized关键字是一个临界区,Java只允许一个对象执行其中的一个临界区。

静态方法有不同的行为。只有一个执行线程访问被synchronized关键字声明的静态方法,但另一个线程可以访问该类的一个对象中的其他非静态的方法。 你必须非常小心这一点,因为两个线程可以访问两个不同的同步方法,如果其中一个是静态的而另一个不是。如果这两种方法改变相同的数据,你将会有数据不一致 的错误。

为了学习这个概念,我们将实现一个有两个线程访问共同对象的示例。我们将有一个银行帐户和两个线程:其中一个线程将钱转移到帐户而另一个线程将从账户中扣款。在没有同步方法,我们可能得到不正确的结果。同步机制保证了账户的正确。
阅读全文

基本线程同步(一)引言

声明:本文是《 Java 7 Concurrency Cookbook 》的第二章,作者: Javier Fernández González     译者:许巧辉 校对:方腾飞

引言

在这个章节中,我们将覆盖:

介绍

在并发编程中发生的最常见的一种情况是超过一个执行线程使用共享资源。在并发应用程序中,多个线程读或写相同的数据或访问同一文件或数据库连接这是正常的。这些共享资源会引发错误或数据不一致的情况,我们必须通过一些机制来避免这些错误。
阅读全文

Fork/Join框架(六)取消任务

声明:本文是《 Java 7 Concurrency Cookbook 》的第五章,作者: Javier Fernández González     译者:许巧辉 校对:方腾飞

取消任务

当你在一个ForkJoinPool类中执行ForkJoinTask对象,在它们开始执行之前,你可以取消执行它们。ForkJoinTask类提供cancel()方法用于这个目的。当你想要取消一个任务时,有一些点你必须考虑一下,这些点如下:

  • ForkJoinPool类并没有提供任何方法来取消正在池中运行或等待的所有任务。
  • 当你取消一个任务时,你不能取消一个已经执行的任务。

在这个指南中,你将实现取消ForkJoinTask对象的例子。你将查找数在数组中的位置。第一个找到这个数的任务将取消剩下的任务(未找到这个数的任务)。由于Fork/Join框架并没有提供这种功能,所以,你将实现一个辅助类来做这个取消功能。
阅读全文

Fork/Join框架(五)在任务中抛出异常

声明:本文是《 Java 7 Concurrency Cookbook 》的第五章,作者: Javier Fernández González     译者:许巧辉 校对:方腾飞

在任务中抛出异常

在Java中有两种异常:

  • 已检查异常(Checked exceptions):这些异常必须在一个方法的throws从句中指定或在内部捕捉它们。比如:IOException或ClassNotFoundException。
  • 未检查异常(Unchecked exceptions):这些异常不必指定或捕捉。比如:NumberFormatException。

在ForkJoinTask类的compute()方法中,你不能抛出任何已检查异常,因为在这个方法的实现中,它没有包含任何抛出(异常)声明。你必须包含必要的代码来处理异常。但是,你可以抛出(或者它可以被任何方法或使用内部方法的对象抛出)一个未检查异常。ForkJoinTask和ForkJoinPool类的行为与你可能的期望不同。程序不会结束执行,并且你将不会在控制台看到任何关于异常的信息。它只是被吞没,好像它没抛出(异常)。你可以使用ForkJoinTask类的一些方法,得知一个任务是否抛出异常及其异常种类。在这个指南中,你将学习如何获取这些信息。 阅读全文

return top