`
Technoboy
  • 浏览: 156795 次
  • 性别: Icon_minigender_1
  • 来自: 大连
社区版块
存档分类
最新评论

Java Concurrent Programming (4)

    博客分类:
  • J2SE
阅读更多
4 锁
  锁是递归的,是基于每线程的。锁提供了两种主要特性:互斥(mutual exclusion)和可见性(visibility)。互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享数据。可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的。Java语言中,使用synchronized关键字可以实现锁机制,但是它有很多限制:
  1)无法中断一个正在等候获得锁的线程。
  2)没有办法改变锁的语意,即重入性,获取锁的公平性等。
  3)方法和块内的同步,使得只能够够对严格的块结构使用锁。例如:不能在一个方法中获得锁,而在另外一个方法中释放锁。
在这种情况下,引入了java.util.concurrent.locks具有编写的高质量、高效率、语义上准确的线程控制结构工具包。

4.1 Lock & Condition & ReadWriteLock接口
  Lock接口定义如下:
public interface Lock {
	  void lock();
	  void lockInterruptibly() throws InterruptedException;
	  boolean tryLock();
	  boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
	  void unlock();
	  Condition newCondition();
}

  lock方法与tryLock,都是获取锁,但是不同之处在于,tryLock方法如果获取不到锁,并不会等待,如果获取到锁,需要显示的释放。下面是使用的例子:
Lock lock = new ReentrantLock();
	      if (lock.tryLock()) { 
	          try {
	              // manipulate protected state
	          } finally { // if acquire lock ,must release finally
	              lock.unlock();
	          }
	      } else {
	          // perform alternative actions
	      }

  lockInterruptibly表示如果当前线程未被中断,则获取锁。Condition是一个具有条件功能变量的class,必须要Lock interface并用。因为这个新的interface是与调用的对象以及lock对象分开的。其定义如下:
public interface Condition{
	 void await() throws InterruptedException;
	 void awaitUninterruptibly();
	 long awaitNanos(long nanosTimeout) throws InterruptedException;
	 boolean await(long time, TimeUnit unit) throws InterruptedException;
	 boolean awaitUntil(Date deadline) throws InterruptedException;
	 void signal();
	 void signalAll();
 }

  其await和signal方法与Object类的wait,notify的使用是类似的。awaitUninterruptibly表示等待条件的发生,与await方法不同是,它的调用不可能被中断。
awaitUnti表示等待条件的发生,如果通知没有在指定的绝对时间前出现,返回false值。一般使用如下:
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();

  接口ReadWriteLock表示一个读写锁,
public interface ReadWriteLock {
	Lock readLock();
	Lock writeLock();
}

  readLock用于只读操作,writeLock用于写入操作。只要没有 writer,读取锁可以由多个 reader线程同时保持。写入锁是独占的。

4.2 ReentrantLock类
  ReentrantLock实现了Lock接口,具有与内部锁相同的互斥、重入性和内存可见性的保证,它必须被显式地释放。ReentrantLock是可中断的、可定时的,非块结构锁。在Java5中,ReentrantLock的性能要远远高于内部锁。在Java6中,由于管理内部锁的算法采用了类似于ReentrantLock使用的算法,因此内部锁和ReentrantLock之间的性能差别不大。
  reentrant 锁意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。这模仿了 synchronized 的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续) synchronized 块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个 synchronized 块时,才释放锁。
  ReentrantLock的构造函数有两个:
public ReentrantLock();
public ReentrantLock(boolean fair);

  fair表示是否是公平性,true表示是公平性获取锁。默认的构造是非公平性获取锁。在公平锁中,如果锁已被其它线程占有,那么请求线程会加入到等待队列中,并按顺序获得锁;在非公平锁中,当请求锁的时候,如果锁的状态是可用,那么请求线程可以直接获得锁,而不管等待队列中是否有线程已经在等待该锁。公平锁的代价是更多的挂起和重新开始线程的性能开销。在多数情况下,非公平锁的性能高于公平锁。一下是一个例子:
public class ReentrantLockDemo{
	
	//
	private int count;
	
	private final int CAPACITY = 1;
	
	private final Lock lock = new ReentrantLock();
	
	private final Condition isEmpty = lock.newCondition();
	
	private final Condition isFull = lock.newCondition();
	
	private final Object[] cache = new Object[CAPACITY];
	
	public ReentrantLockDemo(int count){
		this.count = count;
	}
	
	public Object get() throws InterruptedException{
		lock.lock();
		try {
			while(isEmpty()){
				isEmpty.await();
			}
			count--;
			isFull.signal();
			return cache[count];
		} finally{
			lock.unlock();
		}
	}
	
	public void put() throws InterruptedException{
		lock.lock();
		try {
			while(isFull()){
				isFull.await();
			}
			count++;
			cache[0] = Thread.currentThread().getName() + "put a message";
			System.out.println(cache[0]);
			isEmpty.signal();
		} finally{
			lock.unlock();
		}
	}
	
	public boolean isFull(){
		return count == cache.length;
	}
	
	public boolean isEmpty(){
		return count == 0;
	}
}


4.3 ReentrantReadWriteLock类
  ReentrantReadWriteLock实现了ReadWriteLock接口,支持与ReentrantLock类似语义的ReadWriteLock实现,将控制的更细粒化,将锁分为读锁和写锁。其也支持公平性锁竞争的策略。当一个线程申请读锁时,只要写入锁没有被其他线程持有,那么会立刻拥有读锁。当一个线程申请写入锁时,其他线程不能持有读锁和写入锁,否则会一直等待。如果当前线程已经持有写入锁,然后再次申请写入锁,会成功,但是需要释放两次锁。以下是一个小例子:
public class ReentrantReadWriteLockDemo {
	
	//
	private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
	
	private final Lock read = rwl.readLock();
	
	private final Lock write = rwl.writeLock();
	
	private final Map<String,Object> map = new HashMap<String, Object>();
	
	public Object get(String key) {
		read.lock();
		try {
			return map.get(key);
		} finally{
			read.unlock();
		}
	}
	
	public void set(String key, Object value){
		write.lock();
		try {
			map.put(key, value);
		} finally{
			write.unlock();
		}
	}
	
	public void clear(){
		write.lock();
		try {
			map.clear();
		} finally{
			write.unlock();
		}
	}
}


4.4 Semaphore
  信号量,其维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数。
  信号量通常用于限制可以访问某些资源的线程数目。下面这个小程序会帮助您理解Semaphore:
public static void main(String[] args) {
		ExecutorService service = Executors.newCachedThreadPool();
		final Semaphore semaphore = new Semaphore(5);
		for (int index = 0; index < 20; index++) {
			final int NO = index;
			Runnable run = new Runnable() {
				public void run() {
					try {
						semaphore.acquire();
						System.out.println("acquire thread" + NO + "***********");
						Thread.sleep(3000);
						semaphore.release();
						System.out.println("release thread" + NO + "###########");
					} catch (Exception e) {
					}
				}
			};
			service.execute(run);
		}
		service.shutdown();
	}


4.5 Mutex
  非重入互斥独占锁(non-reentrant mutual exclusion lock),它不提供公平性锁竞争策略已经顺序化资源的保证。如果锁已经被执行acquire的线程持有,当再次acquire后,会形成阻塞。Mutex可以看做信号量为1的Semaphore。其acquire和release方法,可以不用成对在同一个方法中出现。其定义如下:
public class Mutex implemets Sync {
    public void acquire() throws InterruptedException;
    public void release();
    public boolean attempt(long msec) throws InterruptedException;
}

如果当前线程在试图acquire和attempt获取锁的过程中被中断,会抛出InterruptedException异常。

4.6 Latch
  闭锁,一个release操作将使得所有之前或者之后的acquire操作都恢复执行。
util.concurrent下的CountDownLatch是其的扩展,其主要方法为countDown方法和await方法,前者表示计数减一,后者表示等待计数为0,如果计数不为0,就形成阻塞,一直阻塞。以下是一个例子,模拟百米才跑比赛,所有人到达终点后,比赛结束:
public class CountDownLatchDemo {
	//
	private static final int PLAY_AMOUNT = 10;
	
	public static void main(String[] args) {
		
		CountDownLatch begin = new CountDownLatch(1);
		
		CountDownLatch end = new CountDownLatch(PLAY_AMOUNT);
		
		Player[] players = new Player[PLAY_AMOUNT];
		
		for(int i = 0; i < players.length; i ++){
			players[i] = new Player(i+1, begin, end);
		}
		
		ExecutorService executor = Executors.newFixedThreadPool(PLAY_AMOUNT);
		
		for(Player player : players){
			executor.execute(player);
		}
		
		System.out.println("game begin ");
		begin.countDown();
		try {
			end.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("game over ");
		executor.shutdown();
	}
	
}

class Player implements Runnable{
	//
	private final int id;
	
	private final CountDownLatch begin;
	
	private final CountDownLatch end;
	
	public Player(int id, CountDownLatch begin, CountDownLatch end){
		this.id = id;
		this.begin = begin;
		this.end = end;
	}
	
	public void run(){
		try {
			begin.await();
			Thread.sleep((long)(Math.random() * 100));
			System.out.println("Player " + id + " has arrived");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			end.countDown();
		}
	}
}

4.7 Barrier
  Barrier能够阻塞一组线程,其与闭锁的区别在于:闭锁等待的是事件,barrier等待的是线程。CyclicBarrier表示的cyclic就是表示,线程在释放后可以重用。有两个构造函数:
public CyclicBarrier(int parties, Runnable barrierAction);
public CyclicBarrier(int parties);

  parties表示可阻挡线程数量,barrierAction表示线程数量到达parties后执行的动作,null表示没有执行动作。第二个版本的构造函数默认为null的执行动作。
public int await() throws InterruptedException, BrokenBarrierException
public int await(long timeout, TimeUnit unit) throws InterruptedException,BrokenBarrierException,TimeoutException;
public void reset();

  barrier的核心是await方法,这个方法的行为与Condition的await方法相似。有一个选项可以选择是等到barrier释放掉thread还是有时限条件。不需要signal()方法是因为通知就是由barrier在有正确数目的参与者等待的时候所完成的。barrier可以被reset()方法重置到初始状态以备下一次使用,如果此时有线程在等待,调用reset后会抛出BrokenBarrierException异常。
  然而,发生异常的条件会导致barrier失败,当barrier失败的时候,CyclicBarrier会解开barrier并以BrokenBarrierException释放掉正在await方法上等待的thread。barrier会被数种原因解开,等待中的thread被中断,超时或是因为barrier的动作所抛出的异常。但是在每种异常条件下,barrier只是被打开。barrier中的getNumberWaiting()方法返回在barrier上等待thread的数量,getParties()方法返回构造的parties数量,isBroken()方法查询barrier是否已被打开。
  以下这个小例子,会帮助您理解CyclicBarrier,模拟5人去登山,当5人都到齐了,便出发:
public class CyclicBarrierDemo {
	
	public static void main(String[] args) {
		
		final ExecutorService executor = Executors.newCachedThreadPool();
		
		final CyclicBarrier barrier = new CyclicBarrier(5, new Runnable(){
			
			public void run(){
				System.out.println("all people arrived , let's go");
			}
		});
		
		for(int i = 0; i < 5; i ++){
			executor.execute(new Runnable(){
				
				public void run(){
					try {
						Thread.sleep((long)(Math.random() * 100));
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getId() + " has arrived");
					try {
						barrier.await();  // wait other people to arrive
					} catch (InterruptedException e) {
						e.printStackTrace();
					} catch (BrokenBarrierException e) {
						e.printStackTrace();
					}
				}
			});
		}
		executor.shutdown();
	}
}
5
2
分享到:
评论

相关推荐

    Java Concurrent Programming

    为了简化多线程编程,Java提供了一系列工具和API,如`java.util.Timer`和`java.util.concurrent`包,这些工具可以帮助开发者更高效地管理线程间的同步问题。 ##### 1.2 synchronized关键字 `synchronized`关键字是...

    concurrent programming in java design principles and patterns .chm

    concurrent programming in java design principles and patterns .chm

    Concurrent Programming in Java

    Java的并发库提供了一系列工具和API,如`java.util.concurrent`包,帮助开发者有效地管理并发任务。本书主要涵盖以下几个方面: 1. **线程基础**:书中首先介绍了线程的基本概念,包括如何创建和管理线程,以及线程...

    JAVA Concurrent Programming

    4. `java.util.concurrent.locks`包 此包包含各种锁实现,如`ReentrantLock`(可重入锁)、`ReadWriteLock`(读写锁)和`Condition`(条件变量)。这些锁机制提供了更灵活的同步控制,比如读写锁允许多个读线程同时...

    Doug Lea, Concurrent Programming in Java Design Principles and Patterns

    4. **Java并发工具**:Doug Lea详细介绍了java.util.concurrent包中的各种工具类,如Executor框架、Semaphore信号量、CountDownLatch倒计时器、CyclicBarrier回环栅栏和Future异步计算接口。这些工具极大地简化了...

    使用Java并发编程Concurrent Programming Using Java

    Java平台提供了丰富的API支持并发编程,如`java.util.concurrent`包下的各种类和接口,这些工具可以帮助开发者更高效地管理多线程环境下的任务调度和数据共享问题。 ### Java并发编程基础 #### 1. 多线程基础 - **...

    concurrent programming in java(doug lea)

    《Java并发编程》一书是由著名并发编程专家Doug Lea所著,他同时也是Java并发包(JUC)的作者,这本书详细介绍了Java多线程编程的基础概念和高级技术。 首先,书中提到了并发编程的基本概念,包括并发模型、设计力...

    Concurrent Programming in Java™: Design Principles and Patterns 2nd

    本书《Concurrent Programming in Java™: Design Principles and Patterns 2nd》由Doug Lea编写,出版于1999年,是关于Java并发编程的一本权威指南。Java平台因其强大的线程支持能力而备受青睐,这使得Java程序员...

    Concurrent Programming on Windows 无水印pdf

    Concurrent Programming on Windows 英文无水印pdf pdf所有页面使用FoxitReader和PDF-XChangeViewer测试都可以打开 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系...

    Concurrent Programming in Java™: Design Principles and Patterns, Second Edition

    Concurrent Programming in Java™: Design Principles and Patterns, Second Edition. 介绍并发编程的好的著作,著名的并发大师 Doug Lea的杰作。

    Learning Concurrent Programming in Scala, 2nd Edition

    Title: Learning Concurrent Programming in Scala, 2nd Edition Author: Aleksandar Prokopec Length: 382 pages Edition: 2nd Revised edition Language: English Publisher: Packt Publishing - ebooks Account ...

    word版本

    《Java并发编程实践》第二版是一本专注于Java编程语言中并发程序设计的著作,由Doug Lea撰写,Addison Wesley出版社于1999年10月出版。这本书旨在为那些熟悉面向对象编程但对并发编程了解不多的开发者提供指导,同时...

    Java并发编程:设计原则与模式(Concurrent.Programming.in.Java)(中英版)

    例如,使用无锁数据结构或原子操作(`java.util.concurrent.atomic`包)。 3. **避免死锁、活锁和饥饿**:理解并预防这些并发问题至关重要。死锁发生在两个或多个线程相互等待对方释放资源导致僵局;活锁是线程不断...

    Concurrent Programming in Java Design Principles and Pattern

    Concurrent Programming in Java Design Principles and Pattern英文版 2.48M Java并发编程设计原则与模式_第二版(原书中文版) 19.4M Concurrent_Programming_in_Java_Design_Principles_Lecture DougLea

    Concurrent - Programming in Java.pdf

    - **标准库**:Java并发工具包(java.util.concurrent)提供了丰富的并发工具类。 - **第三方库**:例如Apache Commons Concurrency等。 ##### 2. 构建库 - **自定义并发组件**:根据项目需求开发特定的并发工具。 ...

    Concepts and Notations for Concurrent Programming

    《Concepts and Notations for Concurrent Programming》这篇论文由Gregory R. Andrews和Fred B. Schneider撰写,深入探讨了并行编程的核心概念和技术。尽管这是一篇较为古老的文章,但其内容仍然具有很高的参考价值...

    java concurrent source code

    资深Java专家10年经验总结,全程案例式讲解,首本全面介绍Java多线程编程技术的专著 结合大量实例,全面讲解Java多线程编程中的并发访问、线程间通信、锁等最难突破的核心技术与应用实践 封底 Java多线程无处不在,...

Global site tag (gtag.js) - Google Analytics