[Java并发编程实战]活锁

学而不思则罔,思而不学则殆。—《论语》
只学习却不思考就不会感到迷茫,只空想却不学习就会疲倦而没有收获。

我们都知道死锁,然而还存在一种锁叫做活锁。死锁是一直死等,活锁他不死等,它会一直执行,但是线程就是不能继续,因为它不断重试相同的操作。换句话说,就是信息处理线程并没有发生阻塞,但是永远都不会前进了。

活锁同样会发生在多个相互协作的线程间,当他们为了彼此间的响应而相互礼让,使得没有一个线程能够继续前进,那么就发生了活锁。

好比两个过于礼貌的人在半路相遇,出于礼貌他们相互礼让,避开对方的路,但是在另一条路上又相遇了。就这样,不停地一直避让下去。。。。

活锁典型的例子实在一些重试机制中,比如以太网络上,两个基站尝试使用相同的载波发送数据包,包会发生冲突。发生冲突后,稍后都会重发。如果这时他们都是在 1s 后重发,那么他们又会再次发生冲突,一直循环下去,导致数据包永远不能发送。为了解决这个问题,我们可以对重试机制引入一些随机性,不指定 1s 重发,而是重发的时间内是随机的。通过随机的等待再发送能够相当有效的避免活锁的发生。

下面看一个活锁例子:

/************************
 * 活锁例子
 * 创建一个勺子类,有且只有一个。
 * 丈夫和妻子用餐时,需要使用勺子,这时只能有一人持有,也就是说同一时刻只有一个人能够进餐。
 * 但是丈夫和妻子互相谦让,都想让对方先吃,所以勺子一直传递来传递去,谁都没法用餐。
 * */
public class LiveLockTest {

    //定义一个勺子,ower 表示这个勺子的拥有者
    static class Spoon {
        Diner owner;//勺子的拥有者

        //获取拥有者
        public String getOwnerName() {
            return owner.getName();
        }
        //设置拥有者
        public void setOwner(Diner diner) {
            this.owner = diner;
        }

        public Spoon(Diner diner) {
            this.owner = diner;
        }
        //表示正在用餐
        public void use() {
            System.out.println(owner.getName() + " use this spoon and finish eat.");
        }
    }

    //定义一个晚餐类
    static class Diner {
        public Diner(boolean isHungry, String name) {
            this.isHungry = isHungry;
            this.name = name;
        }
        private boolean isHungry;//是否饿了
        private String name;//定义当前用餐者的名字

        public String getName() {//获取当前用餐者
            return name;
        }
        //可以理解为和某人吃饭
        public void eatWith(Diner spouse, Spoon sharedSpoon) {
            try {
                synchronized (sharedSpoon) {
                    while (isHungry) {
                        //当前用餐者和勺子拥有者不是同一个人,则进行等待
                        while (!sharedSpoon.getOwnerName().equals(name)) {
                            sharedSpoon.wait();
                            //System.out.println("sharedSpoon belongs to" + sharedSpoon.getOwnerName())
                        }
                        //spouse此时是饿了,把勺子分给他,并通知他可以用餐
                        if (spouse.isHungry) {
                            System.out.println("I am " + name + ", and my " + spouse.getName() + " is hungry, I should give it to him(her).\n");
                            sharedSpoon.setOwner(spouse);
                            sharedSpoon.notifyAll();
                        } else {
                            //用餐
                            sharedSpoon.use();
                            sharedSpoon.setOwner(spouse);
                            isHungry = false;
                        }
                        Thread.sleep(500);
                    }
                }
            } catch (InterruptedException e) {
                System.out.println(name + " is interrupted.");
            }
        }
    }

    public static void main(String[] args) {
        final Diner husband = new Diner(true, "husband");//创建一个丈夫用餐类
        final Diner wife = new Diner(true, "wife");//创建一个妻子用餐类
        final Spoon sharedSpoon = new Spoon(wife);//创建一个勺子,初始状态并由妻子持有

        //创建一个 线程,由丈夫进行用餐
        Thread h = new Thread() {
            @Override
            public void run() {
                //表示和妻子用餐,这个过程判断妻子是否饿了,如果是,则会把勺子分给妻子,并通知她
                husband.eatWith(wife, sharedSpoon);
            }
        };
        h.start();

        //创建一个 线程,由妻子进行用餐
        Thread w = new Thread() {
            @Override
            public void run() {
                //表示和妻子用餐,这个过程判断丈夫是否饿了,如果是,则会把勺子分给丈夫,并通知他
                wife.eatWith(husband, sharedSpoon);
            }
        };
        w.start();

        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        h.interrupt();
        w.interrupt();

        try {
            h.join();//join()方法阻塞调用此方法的线程(calling thread),直到线程t完成,此线程再继续;通常用于在main()主线程内,等待其它线程完成再结束main()主线程。
            w.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

执行结果如下:
[Java并发编程实战]活锁_第1张图片

这就是一个发生活锁的例子了,相互礼让,但是程序一直没有执行结果。通过这个例子,就可以知道活锁发生的情况到底是怎么运行的了。

参考:
《Java并发编程实战》
https://stackoverflow.com/questions/1036364/good-example-of-livelock

你可能感兴趣的:(Java并发编程,Java并发编程实战)