动力节点首页 全国咨询热线:400-8080-105

绑定手机号,登录
手机号

验证码

微信登录
手机号登录
手机号

验证码

微信登录与注册
微信扫码登录与注册

扫码关注微信公众号完成登录与注册
手机号登录
首页 > 文章

一例搞懂Java活锁

05-27 14:43 712浏览
举报 T字号
  • 大字
  • 中字
  • 小字

说起Java死锁,大家都不陌生,但说到Java活锁,对于Java基础比较薄弱的小伙伴来说就知之甚少了。活锁指的是任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试—失败—尝试—失败的过程。处于活锁的实体是在不断的改变状态,活锁有可能自行解开。

活锁和死锁相对应,为了更好的理解活锁与死锁的区别,我们采用哲学家进餐的问题作为例子。有五个哲学家同桌而坐,桌子上有五只筷子,示意图如下:

如果所有的哲学家都先拿左手边的筷子后拿右手边的筷子,哲学家们就会死锁,因为他们右手边的筷子被另一个哲学家当作左手边的筷子拿走了。哲学家们发现如果他们“持有并等待”就会发生死锁,于是他们达成协议:如果申请第二根筷子没有成功,则放下第一根筷子,重新拿筷子,于是他们就形成了一个活锁,实现代码如下:

class Philosopher implements Runnable {
    private int id;
    public Philosopher(int id) {
	this.id = id;
    }
    public void run() {
 	int leftCsIndex = id;
	int rightCsIndex = (id+1)%5;
        while(true) {
	    LiveLockTest.chopsticks[leftCsIndex].lock();
            System.out.println("Philosopher"+ id +": I got left chopstick");
	    try { Thread.sleep(100); } catch (Exception e) {}
	    if(LiveLockTest.chopsticks[rightCsIndex].tryLock()) {
		System.out.println("Philosopher"+ id +": I got right chopstick");
		System.out.println("Philosopher"+ id +": eating");
		LiveLockTest.chopsticks[rightCsIndex].unlock();
		break;
	    }
	    else {
		try { Thread.sleep(100); } catch (Exception e) {}
	    }
	    LiveLockTest.chopsticks[leftCsIndex].unlock();
	}
    }
}
public class LiveLockTest {
    public static Lock[] chopsticks = new Lock[5];
    public static void main(String[] args) {
 	for(int i=0; i < chopsticks.length; i++) {
	    chopsticks[i] = new ReentrantLock();
	}
	ExecutorService exec = Executors.newCachedThreadPool();
	for(int i=0; i < 5; i++) {
	    exec.execute(new Philosopher(i));
	}
	exec.shutdown();
    }
}

代码输出结果:

Philosopher0: I got left chopstick

Philosopher1: I got left chopstick

Philosopher2: I got left chopstick

Philosopher3: I got left chopstick

Philosopher4: I got left chopstick

...

在上面的代码中我们声明了一个Lock数组,用来代表筷子,上次我们用的是内置锁实现死锁,而实现活锁则不能使用内置锁,因为当没有拿到内置锁时线程就会无限的等待下去,而我们想实现的是“如果没拿到锁就不拿了”,显式锁的tryLock()方法为我们提供了这个功能。哲学家在尝试拿左手边的筷子时是肯定可以拿到的,拿到之后等待0.1秒(0.1秒之后所有的哲学家就都拿到了左手的筷子),这时候他们再去拿右手筷子肯定会失败,于是他们就开始了新一轮的“拿筷子-放筷子”。

有个场景可以形象地说明活锁和死锁的不同:有个胡同南北走向,它只能同时通过两个人,这时两个人正好互相碰面,如果一个人靠东走另一个人靠西走就都可以通过胡同,但是两个人目前都在靠东走。

1. 他们谁也不肯给对方让路,一直在等待对方让路,他们就陷入了死锁。

2. 他们相遇之后都立即给对方让路,他们两个人的节奏保持了惊人的一致,于是他们就陷入了活锁。

有了上面的例子就可以很好的理解活锁和死锁了,对于线程来说发生死锁和活锁时,线程的状态是不同的,当线程死锁时线程处于阻塞状态,线程只占用内存空间,不占用CPU;而发生活锁时线程处于运行状态,既占用内存空间也占用CPU时间(有点像忙等待),因此活锁对系统性能的影响比死锁还要大。怎么样,是不是很生动形象的例子呢,在理解了什么是Java活锁之后,接下来我们就要探究Java活锁的用法了,我们可以先到动力节点在线的视频课程先了解一二再来深入学习。

0人推荐
共同学习,写下你的评论
0条评论
代码小兵498
程序员代码小兵498

153篇文章贡献528999字

相关课程 更多>

作者相关文章更多>

推荐相关文章更多>

Java面试题及答案整理

提枪策马乘胜追击04-21 20:01

Spring常见面试题

代码小兵92504-17 16:07

Java零基础实战项目——五子棋

代码小兵98804-25 13:57

Java string类详解

杨晶珍05-11 14:54

6道经典算法面试题

杨晶珍05-12 16:39

发评论

举报

0/150

取消