Thinking in Java 中闪耀的星星(五)

5 篇文章 0 订阅
5 篇文章 0 订阅

61.死锁及死锁解决方法
死锁是并发编程中可能不小心遇到的陷阱,简而言之就是“千军万马过独木桥,挤在一起,谁也别想过”。给定一个场景,5个哲学家围成一个圆吃饭,只有5根筷子,每一根都放在两个哲学家中间,这样每个哲学家的左右均各有一根筷子,每个哲学家吃饭的时候都是先拿左边的筷子,再拿右边的筷子,吃饭的时候,哲学家可能会思考,思考时不会拿筷子吃饭。所以情况可能是这样的:开饭!有一些哲学家在思考,不吃饭,有一些饿了,饿了的哲学家先拿左边的筷子,再拿右边的筷子,两根筷子就可以吃饭,吃完饭再放下手中的筷子,若有一边的筷子已经被拿走了,他只好等着,随着哲学家一个个的循环,突然,出现每个哲学家手里都有一根筷子,却全部吃不了饭的情况—该情况就是死锁!
造成死锁有4个条件:
1).要有共享资源–筷子
2).至少有一个任务持有部分资源,但是执行任务又需要其他任务手中的另一部分资源—拿到
一根筷子的哲学家,必须拿到隔壁哲学家的另一根筷子才能吃饭
3).资源不会被消耗掉–哲学家礼貌的吃完饭就放下了筷子,筷子还是5根,不会少
4).循环等待—哲学家吃饭时要围成一个圆,吃饭时都是从左至右拿筷子
上面的情况中,第4个(循环等待)最容易被打破,可以给哲学家们标记,到最后一个时,他从右至左拿筷子就行。
先看死锁的情况:

import java.util.*;
import java.util.concurrent.*;
class Chopstick{
    private boolean taken=false;
    public synchronized 
    void take() throws InterruptedException{
        while(taken)
            wait();
        taken=true;
    }
    public synchronized void drop(){
        taken=false;
        notifyAll();
    }
}
class Philosopher implements Runnable{
    private Chopstick left;
    private Chopstick right;
    private final int id;
    private final int ponderFacter;
    private Random rand=new Random(47);
    private void pause() throws InterruptedException{
        if(ponderFacter==0) return;
        TimeUnit.MILLISECONDS.sleep(
            rand.nextInt(ponderFacter * 20));
    }
    public Philosopher(Chopstick left,Chopstick right,
           int ident,int ponder){
        this.left=left;
        this.right=right;
        id=ident;
        ponderFacter=ponder;
    }
    public void run(){
        try{
            while(!Thread.interrupted()){
                System.out.println(this+" thinking");
                pause();
                System.out.println(this+" grabbing right");
                right.take();
                System.out.println(this+" grabbing left");
                left.take();
                System.out.println(this+" eating");
                pause();
                right.drop();
                left.drop();
            }
        }catch(InterruptedException e){
            System.out.println(this+" exiting via interrupt");
        }
    }
    public String toString(){return "Philosopher "+id;}
}
public class DeadlockingDiningPhilosophers{
    public static void main(String[] args) throws Exception{
        int ponder=5;
        if(args.length>0)
            ponder=Integer.parseInt(args[0]);
        int size=5;
        if(args.length>1)
            size=Integer.parseInt(args[1]);
        ExecutorService exec=Executors.newCachedThreadPool();
        Chopstick[] sticks=new Chopstick[size];
        for(int i=0;i<size;i++)
            sticks[i]=new Chopstick();
        for(int i=0;i<size;i++)
            exec.execute(new Philosopher(
                sticks[i],sticks[(i+1)%size],i,ponder));
        if(args.length==3&&args[2].equals("timeout"))
            TimeUnit.SECONDS.sleep(5);
        else{
            System.out.println("Press 'Enter' to quit");
            System.in.read();
        }   
        exec.shutdownNow();
    }
}/*Output
Philosopher 4 grabbing right
Philosopher 1 grabbing right
Philosopher 1 grabbing left
Philosopher 2 grabbing right
Philosopher 2 grabbing left
Philosopher 3 thinking
Philosopher 3 grabbing right
Philosopher 3 grabbing left
Philosopher 0 thinking
Philosopher 1 eating
Philosopher 4 grabbing left
Philosopher 0 grabbing right
Philosopher 1 thinking
Philosopher 0 grabbing left
Philosopher 2 eating
Philosopher 1 grabbing right
Philosopher 2 thinking
Philosopher 1 grabbing left
Philosopher 3 eating
...
*///~
解决死锁,打破循环,改动主函数的部分内容即可:
public static void main(String[] args) throws Exception{
        int ponder=5;
        if(args.length>0)
            ponder=Integer.parseInt(args[0]);
        int size=5;
        if(args.length>1)
            size=Integer.parseInt(args[1]);
        ExecutorService exec=Executors.newCachedThreadPool();
        Chopstick[] sticks=new Chopstick[size];
        for(int i=0;i<size;i++)
            sticks[i]=new Chopstick();
        for(int i=0;i<size;i++)
            if(i<(size-1))
                exec.execute(new Philosopher(
                    sticks[i],sticks[i+1],i,ponder));
            else
                exec.execute(new Philosopher(
                    sticks[0],sticks[i+1],i,ponder));
        if(args.length==3&&args[2].equals("timeout"))
            TimeUnit.SECONDS.sleep(5);
        else{
            System.out.println("Press 'Enter' to quit");
            System.in.read();
        }
        exec.shutdownNow();

62.运用java类库已封装好的类
有:CountDownLatch, CyclicBarrier, DelayQueue, PriorityBlockingQueue, Semaphore…具体的使用根据需求而选择,这里介绍CyclicBarrier,Thinking in Java 的作者Bruce 在高中用VB写过一个赛马的例子, 而在书里,他利用CyclicBarrier 写了java版的赛马。CyclicBarrier 用在当你生成一堆平行任务(每匹马都是平行的)时,直到等待这些任务完成目标(也可以理解为达到某个条件)的情况。看例子:

import java.util.*;
import java.util.concurrent.*;
class Horse implements Runnable{
    private static int counter=0;
    private final int id=counter++;
    private int strides=0;
    private static Random rand=new Random(47);
    private static CyclicBarrier barrier;
    public Horse(CyclicBarrier b){barrier=b;}
    public synchronized int getStrides(){return strides;}
    public void run(){
        try{
            while(!Thread.interrupted()){
                synchronized(this){
                    strides += rand.nextInt(10);
                }
                barrier.await();
            }
        }catch(InterruptedException e){

        }catch(BrokenBarrierException e){
            throw new RuntimeException(e);
        }
    }
    public String toString(){return "Horse "+id+" ";}
    public String tracks(){
        StringBuilder s=new StringBuilder();
        for(int i=0;i<getStrides();i++)
            s.append("*");
        s.append(id);
        return s.toString();
    }
}
public class HorseRace{
    static final int FINISH_LINE=75;
    private List<Horse> horses=new ArrayList<Horse>();
    private ExecutorService exec=Executors.newCachedThreadPool();
    private CyclicBarrier barrier;
    public HorseRace(int nHorses,final int pause){
        barrier=new CyclicBarrier(nHorses,new Runnable(){
           public void run(){
               StringBuilder s=new StringBuilder();
               for(int i=0;i<FINISH_LINE;i++)
                   s.append("=");
               System.out.println(s);
               for(Horse horse : horses)
                   System.out.println(horse.tracks());
               for(Horse horse:horses)
                   if(horse.getStrides()>=FINISH_LINE){
                       System.out.println(horse+" won!");
                       exec.shutdownNow();
                       return;
                   }
               try{
                   TimeUnit.MILLISECONDS.sleep(pause);
               }catch(InterruptedException e){
                   System.out.println("barrier-action sleep interrupted");
               }
            }
        });
        for(int i=0;i<nHorses;i++){
            Horse horse=new Horse(barrier);
            horses.add(horse);
            exec.execute(horse);
        }
    }
    public static void main(String[] args){
        int nHorses=7;
        int pause=200;
        if(args.length>0){
            int n=new Integer(args[0]);
            nHorses=n>0 ? n : nHorses;
        }
        if(args.length>1){
            int p=new Integer(args[1]);
            pause=p>-1 ? p : pause;
        }
        new HorseRace(nHorses,pause);
    }
}/*Output
===========================================================================
********0
*****1
***2
*3
*4
*********5
********6
===========================================================================
**********0
************1
***********2
*********3
**4
******************5
********6
===========================================================================
******************0
*************1
***********2
*****************3
********4
***************************5
****************6
===========================================================================
**********************0
****************1
*****************2
**********************3
********4
****************************5
******************6
===========================================================================
**********************0
**********************1
*******************2
**********************3
**********4
********************************5
**********************6
===========================================================================
*************************0
***********************1
*************************2
*************************3
**************4
************************************5
**********************6
===========================================================================
*******************************0
*******************************1
***************************2
*******************************3
***********************4
*****************************************5
************************6
===========================================================================
*************************************0
**************************************1
***********************************2
***********************************3
****************************4
**********************************************5
****************************6
===========================================================================
***************************************0
********************************************1
******************************************2
******************************************3
********************************4
************************************************5
**********************************6
===========================================================================
*****************************************0
********************************************1
**************************************************2
*******************************************3
*****************************************4
*******************************************************5
**************************************6
===========================================================================
**************************************************0
**********************************************1
***********************************************************2
***************************************************3
*******************************************4
************************************************************5
******************************************6
===========================================================================
******************************************************0
**********************************************1
***************************************************************2
***********************************************************3
**********************************************4
***************************************************************5
************************************************6
===========================================================================
**************************************************************0
*****************************************************1
**********************************************************************2
**************************************************************3
***********************************************4
***********************************************************************5
********************************************************6
===========================================================================
***************************************************************0
**************************************************************1
***************************************************************************2
****************************************************************3
***********************************************4
******************************************************************************5
*****************************************************************6
Horse 2  won!
*///~

63.互斥技术synchronized lock atomic的比较
Lock相对是较为有优势的,速度比synchronized和atomic要快,atomic经常用在修饰一些变量上,用处较小,而synchronized的用处在59题有说到(一个是被修饰的代码块很小,另一个是可读性),下面通过一个基于模板模式的例子来测试每个技术的速度:

import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.*;
import java.util.*;
abstract class Accumulator{
    public static long cycles=50000L;
    private static final int N=4;
    public static ExecutorService exec=
        Executors.newFixedThreadPool(N*2);
    private static CyclicBarrier barrier=
        new CyclicBarrier(N*2+1);
    protected volatile int index=0;
    protected volatile long value=0;
    protected long duration=0;
    protected String id="error";
    protected final static int SIZE=10000000;
    protected static int[] preLoaded=new int[SIZE];
    static{
        Random rand=new Random(47);
        for(int i=0;i<SIZE;i++)
            preLoaded[i]=rand.nextInt();
    }
    public abstract void accumulate();
    public abstract long read();
    private class Modifier implements Runnable{
        public void run(){
            for(long i=0;i<cycles;i++)
                accumulate();
            try{
                barrier.await(); 
            }catch(Exception e){
                throw new RuntimeException(e);
            }
        }
    }
    private class Reader implements Runnable{
        private volatile long value;
        public void run(){
            for(long i=0;i<cycles;i++)
                value=read();
            try{
                barrier.await();
            }catch(Exception e){
                throw new RuntimeException(e);
            }
        }
    }
    public void timedTest(){
        long start =System.nanoTime();
        for(int i=0;i<N;i++){
            exec.execute(new Modifier());
            exec.execute(new Reader());
        }
        try{
            barrier.await();
        }catch(Exception e){
            throw new RuntimeException(e);
        }
        duration =System.nanoTime()-start;
        System.out.printf("%-13s:%13d\n",id,duration);
    }
    public static void
    report(Accumulator acc1,Accumulator acc2){
        System.out.printf("%-22s:%.2f\n",acc1.id+"/"+acc2.id,
            (double)acc1.duration/(double)acc2.duration);
    }
}
class BaseLine extends Accumulator{
    {id="BaseLine";}
    public void accumulate(){
        value+=preLoaded[index++];
        if(index>=SIZE)index=0;
    }
    public long read(){return value;}
}
class SynchronizedTest extends Accumulator{
    {id="Synchronized";}
    public synchronized void accumulate(){
        value+=preLoaded[index++];
        if(index>=SIZE)index=0;
    }
    public synchronized long read(){return value;}
}
class LockTest extends Accumulator{
    {id="Lock";}
    private Lock lock=new ReentrantLock();
    public void accumulate(){
        lock.lock();
        try{
            value+=preLoaded[index++];
            if(index>=SIZE)index=0;
        }finally{
            lock.unlock();
        }
    }
    public long read(){
        lock.lock();
        try{
            return value;
        }finally{
            lock.unlock();
        }
    }
}
class AtomicTest extends Accumulator{
    {id="Atomic";}
    private AtomicInteger index=new AtomicInteger(0);
    private AtomicLong value=new AtomicLong(0);
    public void accumulate(){
        int i=index.getAndIncrement();
        value.getAndAdd(preLoaded[i]);
        if(++i>=SIZE)index.set(0);
    }
    public long read(){return value.get();}
}
public class SynchronizationComparisons{
    static BaseLine baseLine=new BaseLine();
    static SynchronizedTest synch=new SynchronizedTest();
    static LockTest lock=new LockTest();
    static AtomicTest atomic=new AtomicTest();
    static void test(){
        System.out.println("============================");
        System.out.printf("%-12s : %13d\n","Cycles",Accumulator.cycles);
        baseLine.timedTest();
        synch.timedTest();
        lock.timedTest();
        atomic.timedTest();
        Accumulator.report(synch,baseLine);
        Accumulator.report(lock,baseLine);
        Accumulator.report(atomic,baseLine);
        Accumulator.report(synch,lock);
        Accumulator.report(synch,synch);
        Accumulator.report(lock,atomic);
    }
    public static void main(String[] args){
        int iterations =5;
        if(args.length>0)
            iterations=new Integer(args[0]);
        System.out.println("Warnup");
        baseLine.timedTest();
        for(int i=0;i<iterations;i++){
            test();
            Accumulator.cycles *=2;
        }
        Accumulator.exec.shutdown();
    }
}/*Output
Warnup
BaseLine     :     30552649
============================
Cycles       :         50000
BaseLine     :     18959312
Synchronized :     50959889
Lock         :     88088505
Atomic       :     55392224
Synchronized/BaseLine :2.69
Lock/BaseLine         :4.65
Atomic/BaseLine       :2.92
Synchronized/Lock     :0.58
Synchronized/Synchronized:1.00
Lock/Atomic           :1.59
============================
Cycles       :        100000
BaseLine     :     42487919
Synchronized :    116166688
Lock         :    100310972
Atomic       :     40789084
Synchronized/BaseLine :2.73
Lock/BaseLine         :2.36
Atomic/BaseLine       :0.96
Synchronized/Lock     :1.16
Synchronized/Synchronized:1.00
Lock/Atomic           :2.46
============================
Cycles       :        200000
BaseLine     :     85989973
Synchronized :    160134632
Lock         :    180714101
Atomic       :     80777186
Synchronized/BaseLine :1.86
Lock/BaseLine         :2.10
Atomic/BaseLine       :0.94
Synchronized/Lock     :0.89
Synchronized/Synchronized:1.00
Lock/Atomic           :2.24
============================
Cycles       :        400000
BaseLine     :    164767181
Synchronized :    299636651
Lock         :    389302924
Atomic       :    212353490
Synchronized/BaseLine :1.82
Lock/BaseLine         :2.36
Atomic/BaseLine       :1.29
Synchronized/Lock     :0.77
Synchronized/Synchronized:1.00
Lock/Atomic           :1.83
============================
Cycles       :        800000
BaseLine     :    345644202
Synchronized :    621498341
Lock         :    708810623
Atomic       :    415066983
Synchronized/BaseLine :1.80
Lock/BaseLine         :2.05
Atomic/BaseLine       :1.20
Synchronized/Lock     :0.88
Synchronized/Synchronized:1.00
Lock/Atomic           :1.71
*///~

64.乐观锁
我们用互斥技术来确保任务之间不会相互干扰,出现一个在读取数据,另一个在修改数据的情况,但是,在一些数据不会经常被修改的情况下,为了程序的速度,我们需要更加“乐观”些,就不给数据上锁了。下面的例子中,当数据在计算时遇到任务碰撞,任务会检测出碰撞并忽视修改数据的任务,保证读取数据的完整性:

import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.*;
public class FastSimulation{
    static final int N_ELEMENTS=100000;
    static final int N_GENES=30; 
    static final int N_EVOLVERS=50;
    static final AtomicInteger[][] GRID=
        new AtomicInteger[N_ELEMENTS][N_GENES];
    static Random rand=new Random(47);
    static class Evolver implements Runnable{
        public void run(){
            while(!Thread.interrupted()){
                int element=rand.nextInt(N_ELEMENTS);
                for(int i=0;i<N_GENES;i++){
                    int previous=element-1;
                    if(previous<0)previous=N_ELEMENTS-1;
                    int next=element+1;
                    if(next>=N_ELEMENTS)next=0;
                    int oldvalue=GRID[element][i].get();
                    int newvalue=oldvalue+
                        GRID[previous][i].get()+GRID[next][i].get();
                    newvalue/=3;
                    if(!GRID[element][i].compareAndSet(oldvalue,newvalue)){
                        System.out.println("Old value change from "+oldvalue);
                    }
                }
            }
        }
    }
    public static void main(String[] args) throws Exception{
        ExecutorService exec=Executors.newCachedThreadPool();
        for(int i=0;i<N_ELEMENTS;i++)
            for(int j=0;j<N_GENES;j++)
                GRID[i][j]=new AtomicInteger(rand.nextInt(1000));
        for(int i=0;i<N_EVOLVERS;i++)
            exec.execute(new Evolver());
        TimeUnit.SECONDS.sleep(5);
        exec.shutdownNow(); 
    }
}/*Output
Old value change from 458
Old value change from 460
Old value change from 492
Old value change from 473
Old value change from 512
Old value change from 546
...
*///~

65.Active Object 模式
这是并发章节中最后介绍的,它是一种异步模式,对象的方法调用与执行是分开的。在下面的例子中,主函数中调用传给对象信息去调用方法,但是,方法并不会马上被执行,而是被加到待执行的任务队列中,这样一来,就不会产生两个对象在调用方法时阻塞的情况,避免死锁的出现,也体现了一种代理的思想,作者说代理是面向对象最成功的(在j2ee就可见一斑,几乎所有的框架都是围绕着spring)

import java.util.concurrent.*;
import java.util.*;
public class ActiveObjectDemo{
    private ExecutorService ex=
        Executors.newSingleThreadExecutor();
    private Random rand=new Random(47);
    private void pause(int factor){
        try{
            TimeUnit.MILLISECONDS.sleep(
                100+rand.nextInt(factor));
        }catch(InterruptedException e){
            System.out.println("sleep() interrupted");
        }
    }
    public Future<Integer> calculateInt(final int x,final int y){
        return  ex.submit(new Callable<Integer>(){
                    public Integer call(){
                        System.out.println("starting "+x+" + "+y);
                        pause(500);
                        return x+y;
                    }
                });
    }
    public Future<Float> calculateFloat(final float x,final float y){
        return  ex.submit(new Callable<Float>(){
                    public Float call(){
                        System.out.println("starting "+x+" + "+y);
                        pause(2000);
                        return x+y;
                    }
                });
    }
    public void shutdown(){ex.shutdown();}
    public static void main(String[] args){
        ActiveObjectDemo d1=new ActiveObjectDemo();
        List<Future<?>> results=
            new CopyOnWriteArrayList<Future<?>>();
        for(float f=0.0f;f<1.0f;f+=0.2f)
            results.add(d1.calculateFloat(f,f));
        for(int i=0;i<5;i++)
            results.add(d1.calculateInt(i,i));
        System.out.println("All asynch calls made");
        while(results.size()>0){
            for(Future<?> f : results)
                if(f.isDone()){
                    try{
                        System.out.println(f.get());
                    }catch(Exception e){
                         throw new RuntimeException(e);
                    }
                    results.remove(f);
                }
        }
        d1.shutdown();
    }
}/*Output
All asynch calls made
starting 0.0 + 0.0
0.0
starting 0.2 + 0.2
0.4
starting 0.4 + 0.4
0.8
starting 0.6 + 0.6
1.2
starting 0.8 + 0.8
1.6
starting 0 + 0
0
starting 1 + 1
2
starting 2 + 2
4
starting 3 + 3
6
starting 4 + 4
8
*///~

到此完结,敬请期待小编第二次读 Thinking in Java 的笔记!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值