java并发编程设计

11 篇文章 0 订阅

读完了Venkat Subramaniam的《java虚拟机并发编程》中文译本,对并发编程有了更进一步的认识。

这本书,名字读来让人有些误解,以为是讲java虚拟机的,其实内容讲的是在jvm上的几种编程模型。

这里权当作个笔记,梳理思路,回忆下书的大概内容。


熟悉java的童鞋知道,java是实体与状态绑定的。所以在处理共享状态时,要考虑线程安全问题。

虽然处理状态不可避免,但我们可以有三种方法来处理状态:

    1、共享可变性

    2、隔离可变性

    3、纯粹不可变性


1、共享可变性

     我们熟悉的java,通常都是这样处理状态,然后通过锁来锁定共享的变量、方法或者对象。java5之前通常使用内部锁(synchronized),java5之后开始有了更多的并发数据结构和工具。相比内部锁,后者增加了操作的灵活性,降低了锁粒度。

    譬如我们使用信号量semaphore

import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.Semaphore;


public class BoundedExecutor {
	private final Executor exec;
	private final Semaphore semaphore;
	
	public BoundedExecutor(Executor exec,Semaphore sem){
		this.exec=exec;
		this.semaphore=sem;
	}
	
	public void submitTask(final Runnable command) throws InterruptedException{
		semaphore.acquire();
		try {
			exec.execute(new Runnable(){
				public void run(){
					try {
						command.run();
					} catch (Exception e) {
					}finally{
						semaphore.release();
					}
				}
			});
		} catch (RejectedExecutionException e) {
			semaphore.release();
		}
	}
}

    通过semaphore.acquire()来获取进入许可,当当前信号量数量到达上限时,其他请求进入阻塞;线程最后,通过samaphore.release()释放许可。

2、隔离可变性

        这里要提到的是基于角色的编程,用角色隔离可变性。oop的缺陷在于对象的状态被封装在对象中,所以只有实例方法才能操作对象的状态。然而不同的线程可以同时调用这些方法,从而导致并发问题的发生。而在基于角色的编程模型中,只允许一个角色操作对象的状态。

       角色是一种能够接收消息、处理请求以及发送响应的自由运行的活动,主要被设计用来支持异步化且高效的消息传递机制。

      Akka是一个基于scala的角色库,Akka开发人员提供了一套java的API。

      抽象类akka.actor.UntypedActor用于表示一个角色,我们实现其方法onReceive()方法来接收消息。

     

public class HollywoodActor extends UntypedActor{
  public void onReceive(final Object role){
    System.out.println("Playind "+role+" from Thread "+Thread.currentThread().getName());
 }
}
下面是发送消息

public class UseHollywoodActor{
   public static void main() throws InterruptedException{
     final ActorRef johnnyDepp=Actors.actorOf(HollywoodActor.class).start();
     johnnyDepp.sendOneWay("Jack Sparrow");
     Thread.sleep(100);
     JohnnyDepp.sendOneWay("lrq");
     Actors.registry().shutdownAll();
 }
}

3、纯粹不变性

   既然可变性制造线程安全问题,那何不让一切都不可变。函数式编程语言clojure实现了不可变性,将实体与状态分离。在设计上,值是不可变的,而实体也仅在clojure的事务中是可变的。没错,事务,熟悉数据库的同学一定不陌生。clojure处理并发最著名的莫过于STM(软件事务内存)了。

(def balance (ref 0))

(println "Balance is " @balance)

(dosync
    (ref-set balance 100))

(println "Balance is now "@balance)

用ref标记balance为可变(如果 (def balance 0)像这样,这里是值绑定,balance值是不会改变的),用ref-set来改变balance,但前提是在dosync包裹下。如果没有dosync包裹,会直接抛出错误。而java即使忘记加锁或者加错了位置,都不会有任何报警机制。

上述clojure代码,若用java API实现,则

public Class Account{
   final private Ref balance;
   public Account(final int initialBalance)throws Exception{
      balance=new Ref(initialBalance);
 }
   public int getBalance(){return (Integer)balance.deref();}
   public void deposit(final int amount) throws Exception{
     LockingTransaction.runInTransation(new Callable<Boolean>(
      public Boolean call(){
      final int currentBalance = (Integer)balance.deref();
      balance.set(currentBalance+amount);
      return true;
    }
   );  
 );
 }
}

  Akka中也可以使用事务,可以在java中直接使用Multiverse STM。例如:

return new Atomic<Object>(){
    public Object atomically{
     //这里的代码会在事务中运行
      return resultObject;
  }
}.execute();

4、混合使用角色与STM

     又是Akka,这里使用的是transactor。

     Akka transactor提供了一种将多个角色的执行过程合并到一个事务中的方法。

public class Account extends UntypedTransactor {
	private final Ref<Integer> balance = new Ref<Integer>(0);

	public void atomically(final Object message) {
		if(message instanceof Deposit) {
		int amount = ((Deposit)(message)).amount;
		if (amount > 0) {
			balance.swap(balance.get() + amount);
			System.out.println("Received Deposit request " + amount);
		}
		}
		if(message instanceof Withdraw) {
			int amount = ((Withdraw)(message)).amount;
			System.out.println("Received Withdraw request " + amount);
			if (amount > 0 && balance.get() >= amount)
			balance.swap(balance.get() - amount);
		else {
			System.out.println("...insufficient funds...");
			throw new RuntimeException("Insufficient fund");
		}
		}
		if(message instanceof FetchBalance) {
			getContext().replySafe(new Balance(balance.get()));
		}
	}
}


并发之路漫漫长矣,clojure要加强学习,java许多东西还要继续看。期待厚积薄发。

有道是,长风破浪会有时,直挂云帆济沧海。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值