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

Java Concurrent (3)

阅读更多

本文节选自 Effective Java by Joshua Bloch 和 Concurrent Programming in Java by Doug Lea.

 

1.6 避免过多的同步
1.6.1是否需要同步
    过多的同步可能会导致性能降低、死锁,甚至不确定行为。通常,在同步区域内应该做尽可能少的工作。同步区域之外被调用的外来方法被称为“开放调用(open call)”。除了可以避免死锁之外,开放调用还可以极大地增加并发性。
    考虑StringBuffer类和BufferedInputStream类,这些类都是线程安全(thread-safe)的,但是它们往往被用于单个线程中,所以它们所做的锁操作往往是不必要的,虽然同步的开销自java平台早期开始就一直在下降,但是它永远也不会消失。一个给定的类是否应该执行内部同步并不总是很清楚,下面是一些指导性的原则。

   如果你正在编写的类主要被用于同步环境中,同时也被用于不要求同步的环境中,那么一个合理的方法是,同时提供同步版本和和未同步版本。这也正是Collections Framework采用的方法。还有,java.util.Random也是采用这一种做法是提供一个包装类(wrapper class),它实现一个描述该类的接口,同时在将方法调用转发给内部对象中对应的方法之前执行适当的同步操作。种方法。第二种方法适用于那些不是被设计用来扩展或者重新实现的类,它提供一个未同步的类和一个子类,在子类中包含一些被同步的方法,它们依次调用到超类中对应的方法上。

   关于是否对一个用于存取成员变量的方法进行同步,需要考虑两点:合法性和陈旧性。如果成员变量不总是合法的,那么可以的选择是:

  • 同步所有存取方法
  • 确保用户在得到非法值的时候能得到通知
  • 省略存取方法。在并发程序中,对象的属性可以被异步修改,客户通过某行代码得到的值可能在下一行代码中就改变了。因此需要仔细评估存取方法存在的必要性。

   如果成员变量的值总是合法的,但是不能是陈旧数据,那么可以的选择是:

  • 把成员变量定义为volatile,并去掉存取方法的同步。

1.6.2 分解同步和分解锁
    另外一种增加程序并发性的方法是分解同步,如果一个类的行为可以分解为互相独立、互不干扰或者不冲突的子部分,那么就值得用细粒度的辅助对象来重新构造类。普遍的原则是,把类的内部同步操作分得越细,在大多数情况下,它的活性就越高。但是这一点是以更加复杂和潜在的错误为代价的。例如:

public class Shape
{
    public synchronized vodi adjustLocation(){ /*Long time operation*/ }
    public synchronized vodi adjustDimensions(){ /*Long time operation*/ }
}

   我们假设adjustLocation不处理维度信息,adjustDimensions不处理位置信息,那么可以考虑把维度和位置信息分解到两个类中, 例如: 

public class Shape
{
    private final Location location = new Location();
    private final Dimensions dimensions = new Dimensions();
    public void adjustLocation(){ location.adjustLocation(); }
    public void adjustDimensions(){ dimensions.adjustDimensions(); }
}
public class Location
{
    public synchronized void adjustLocation(){ /*Long time operation*/ }
}
public class Dimensions
{
    public synchronized void adjustDimensions(){ /*Long time operation*/ }
}

    如果你不能或者不想分解类,则可以分解每个子功能相关的同步锁。例如

public class Shape
{
    private final Object locationLock = new Object();
    private final Object dimensionsLock = new Object();
    public void adjustLocation()
    {
        synchronized(locationLock)
        {
            /*Long time operation*/
        }
    }
    public void adjustDimensions()
    {
        synchronized(dimensionsLock)
        {
            /*Long time operation*/
        }
    }
}
 

1.6.3 冲突集合
    设想有一个Inventory类,它有store和retrieve方法来存取对象。以下的例子中使用了Hashtable来演示,虽然这种完全同步的Hashtable允许Inventory类的实现无需考虑底层的实现细节。但是,我们仍然想store和retrieve方法添加一些语义上的约束,如下:

  • retrieve操作不应该和store操作并发执行。
  • 两个或者两个以上的retrieve方法不应该同时执行。
  • 两个或者两个以上的store方法可以同时执行。

   以下的非正规符号描述了冲突集合,即不能并发的方法对的集合.        

   {(store, retrieve), (retrieve, retrieve)}
    基于冲突集合的类可以使用before/after这种模式,即基本操作被那些维护者独占关系的代码所环绕。首先,对于每个方法,定义一个计数变量,用以表示该方法是否在执行中。其次,把每个基本操作都隔离入非公共方法中。最后,编写那些基本操作的公共版本,即在那些基本操作的前后添加上before/after的控制。以下是个示例代码:

public class Inventory
{
	protected final Hashtable items = new Hashtable();
	protected final Hashtable suppliers = new Hashtable();
	
	protected int storing = 0;
	protected int retrieving = 0;
	
	
	public void store(String desc, Object item, String supplier)
	throws InterruptedException
	{
		synchronized(this)
		{
			while(retrieving != 0)
			{
				wait();
				++storing;
			}
		}
		
		try
		{
			doStore(desc, item, supplier);
		}
		finally
		{
			synchronized(this)
			{
				if(--storing == 0)
				{
					notifyAll();
				}
			}
		}
	}
	
	public Object retrieve(String desc)
	throws InterruptedException
	{
		synchronized(this)
		{
			while(storing != 0 || retrieving != 0)
			{
				wait();
				++retrieving;
			}
		}
		
		try
		{
			return doRetrieve(desc);
		}
		finally
		{
			synchronized(this)
			{
				if(--retrieving == 0)
				{
					notifyAll();
				}
			}
		}
	}
	
	protected void doStore(String desc, Object item, String supplier)
	{
		items.put(desc, item);
		suppliers.put(supplier, desc);
	}
	
	protected Object doRetrieve(String desc)
	{
		Object x = items.get(desc);
		if(x != null)
		{
			items.remove(desc);
		}
		return x;
	}
}

 

   接下来考虑一个更复杂的例子,一个读出者和写入者模型,与Inventroy不同,读出者和写入者策略不仅应用于特定方法,而是控制所有具有读出和写入语义的方法。假设我们需要进行有目的的锁定(intention lock),比如,要求按照write,read,write,read,write的顺序等。这时候我们需要考虑的有以下几点:  

  • 如果当前已经存在一个或者多个活动(执行中)的读出者,而且有一个写入者正在等待的时候,一个新的读出者是否能否立即加入?如果答案是肯定的话,那么不断增加的读出者将会使写入者无法执行;如果答案为否,那么读出者的吞吐量就会下降。
  • 如果某些读出者与写入者同时在等待一个活动的写入者完成操作,那么你的处理策略会偏向读出者还是写入者?先到者优先?随意?轮流?

  虽然以上策略没有明确的答案,但是一些标准的解决方案和相关的实现还是存在的,以下一个通用的实现,使用了模板类和before/after这种模式,其子类版本不需要做过多的修改。而且可以通过让allowReader和allowWriter方法中的谓词依赖与这个值,来调整控制策略。以下是示例代码:

public abstract class ReadWrite 
{
	protected int activeReaders = 0;
	protected int activeWriters = 0;
	
	protected int waitingReaders = 0;
	protected int waitingWriters = 0;
	
	protected abstract void doRead();
	protected abstract void doWrite();
	
	public void read() throws InterruptedException
	{
		beforeRead();
		try { doRead(); }
		finally { afterRead(); }
	}
	
	public void write() throws InterruptedException
	{
		beforeWrite();
		try { doWrite(); }
		finally { afterWrite(); }
	} 
	
	protected boolean allowReader()
	{
		return waitingWriters == 0 && activeWriters == 0;
	}
	
	protected boolean allowWriter()
	{
		return activeReaders == 0 && activeWriters == 0;
	}
	
	protected synchronized void beforeRead() throws InterruptedException
	{
		++waitingReaders;
		while(!allowReader())
		{
			try { wait(); }
			catch(InterruptedException ie)
			{
				--waitingReaders;
				throw ie;
			}
		}

		--waitingReaders;
		++activeReaders;
	}
	
	protected synchronized void afterRead()
	{
		--activeReaders;
		notifyAll();
	}
	
	protected synchronized void beforeWrite() throws InterruptedException
	{
		++waitingWriters;
		while(!allowWriter())
		{
			try { wait(); }
			catch(InterruptedException ie)
			{
				--waitingWriters;
				throw ie;
			}
		}
		
		--waitingWriters;
		++activeWriters;
	}
	
	protected synchronized void afterWrite()
	{
		--activeWriters;
		notifyAll();
	}
}
分享到:
评论
1 楼 kruce 2008-08-11  
Inventory的代码有问题吧, ++storing和++retrieving都应该在while循环之外

相关推荐

    java concurrent 精简源码

    本资源“java concurrent 精简源码”着重关注Java并发库(java.util.concurrent)的核心概念,包括阻塞队列和线程管理。下面将详细阐述这些知识点。 1. **Java并发库(java.util.concurrent)** Java并发库是Java ...

    Java Concurrent in practice (animated)

    Java Concurrent in practice (animated)

    java concurrent 包 详细解析

    Java并发包(java.concurrent)是Java平台中处理多线程编程的核心工具包,它提供了丰富的类和接口,使得开发者能够高效、安全地编写多线程程序。这个包的设计目标是提高并发性能,减少同步代码的复杂性,并提供高级...

    JAVA的CONCURRENT用法详解.pdf

    JAVA的CONCURRENT用法详解.pdf

    使用java concurrent调用xmlp api生成pdf

    这里我们关注的是如何使用`java.concurrent`包中的工具和XML Processing API(通常指的是JAXB或DOM4J等处理XML的库)来高效地生成PDF。下面将详细解释这个过程以及涉及的相关知识点。 首先,`java.concurrent`包是...

    java concurrent source code

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

    java并发工具包 java.util.concurrent中文版用户指南pdf

    1. java.util.concurrent - Java 并发工具包 2. 阻塞队列 BlockingQueue 3. 数组阻塞队列 ArrayBlockingQueue 4. 延迟队列 DelayQueue 5. 链阻塞队列 LinkedBlockingQueue 6. 具有优先级的阻塞队列 ...

    java并发工具包 java.util.concurrent中文版pdf

    ### Java并发工具包 `java.util.concurrent` 知识点详解 #### 一、引言 随着多核处理器的普及和应用程序复杂度的增加,多线程编程成为了现代软件开发不可或缺的一部分。为了简化并发编程的复杂性,Java 5 引入了 `...

    java concurrent包分类结构图

    java concurrent包分类结构图

    java concurrent program的实现

    EBS java concurrent program的实现

    Java Concurrent处理并发需求

    ### Java Concurrent处理并发需求 #### 一、Java并发基础与Concurrent API介绍 在现代软件开发中,尤其是在服务器端应用中,对并发处理的需求日益增长。为了满足这种需求,Java平台提供了一系列强大的工具和API来...

    JAVA Concurrent Programming

    3. `java.util.concurrent.atomic`包 这个包提供了原子变量类,如`AtomicInteger`、`AtomicLong`等,它们支持无锁的线程安全操作,可以在不使用`synchronized`的情况下保证变量的原子性更新,从而降低锁的使用,提高...

    java concurrent in practive

    《Java并发编程实战》还会讨论`java.util.concurrent`包中的高级并发工具,如`ExecutorService`和`Future`,它们可以方便地管理和控制线程池,提高系统的并行处理能力。`CountDownLatch`、`CyclicBarrier`和`...

    java同步大杀器concurrent 包

    java同步大杀器concurrent 包java同步大杀器concurrent 包java同步大杀器concurrent 包java同步大杀器concurrent 包java同步大杀器concurrent 包java同步大杀器concurrent 包java同步大杀器concurrent 包java同步大...

    java_util_concurrent_中文用户使用手册@微信公众号-架构探险之道.zip

    JUC使用指导手册 http://tutorials.jenkov.com/java-util-concurrent/blockingqueue.html 中文译文

    Concurrent Programming in Java

    3. **高级并发工具**:随着Java 5的发布,`java.util.concurrent`包引入了许多高级并发工具,如`ExecutorService`、`Future`、`Semaphore`、`CyclicBarrier`和`CountDownLatch`等。这些工具简化了并发代码的编写,...

    动画学习 java.util.concurrent并发工具包

    如何启动:以win7系统为例,最好jdk8 1.打开cmd,cd到jdk的path,本机是:cd C:\Java\...3.使用java -cp命令: java -cp D:\javaConcurrentAnimated.jar vgrazi.concurrent.samples.launcher.ConcurrentExampleLauncher

    java.util.concurrent

    java.util.concurrent总体概览图。 收取资源分3分。需要的同学可以下载一下。 java.util.concurrent主要包括5个部分executor,colletions,locks,atomic,tools。 该图详细的列举了并发包下面的结构,包含所有接口和...

    使用Java并发编程Concurrent Programming Using Java

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

Global site tag (gtag.js) - Google Analytics