活锁

任务没有被阻塞,由于某些条件没有满足,导致一直重复尝试—失败—尝试—失败的过程。处于活锁的实体是在不断的改变状态,活锁有可能自行解开。

死锁是大家都拿不到资源都占用着对方的资源,而活锁是拿到资源却又相互释放不执行。

 

解决活锁的一个简单办法就是在下一次尝试获取资源之前,随机休眠一小段时间。

 

看一下,我们之前的一个例子,如果最后不进行随机休眠,就会产生活锁,现象就是很长一段时间,两个线程都在不断尝试获取和释放锁。

package constxiong.concurrency.a023;

import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* 测试 占有部分资源的线程进一步申请其他资源时,如果申请不到,主动释放它占有的资源,破坏 "不可抢占" 条件
* @author ConstXiong
* @date 2019-09-24 14:50:51
*/

public class TestBreakLockOccupation {

private static Random r = new Random();

private static Lock lock1 = new ReentrantLock();

private static Lock lock2 = new ReentrantLock();

public static void main(String[] args) {
new Thread(() -> {
//标识任务是否完成
boolean taskComplete = false;
while (!taskComplete) {
lock1.lock();
System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock1 成功");
try {
//随机休眠,帮助造成死锁环境
try {
Thread.sleep(r.nextInt(30));
} catch (Exception e) {
e.printStackTrace();
}

//线程 0 尝试获取 lock2
if (lock2.tryLock()) {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock2 成功");
try {
taskComplete = true;
} finally {
lock2.unlock();
}
} else {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock2 失败");
}
} finally {
lock1.unlock();
}

//随机休眠,避免出现活锁
try {
Thread.sleep(r.nextInt(10));
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();

new Thread(() -> {
//标识任务是否完成
boolean taskComplete = false;
while (!taskComplete) {
lock2.lock();
System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock2 成功");
try {
//随机休眠,帮助造成死锁环境
try {
Thread.sleep(r.nextInt(30));
} catch (Exception e) {
e.printStackTrace();
}

//线程2 尝试获取锁 lock1
if (lock1.tryLock()) {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock1 成功");
try {
taskComplete = true;
} finally {
lock1.unlock();
}
} else {
System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock1 失败");
}
} finally {
lock2.unlock();
}

//随机休眠,避免出现活锁
try {
Thread.sleep(r.nextInt(10));
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}

}

 

饥饿

一个线程因为 CPU 时间全部被其他线程抢占而得不到 CPU 运行时间,导致线程无法执行。

产生饥饿的原因:

  • 优先级线程吞噬所有的低优先级线程的 CPU 时间

  • 其他线程总是能在它之前持续地对该同步块进行访问,线程被永久堵塞在一个等待进入同步块

  • 其他线程总是抢先被持续地获得唤醒,线程一直在等待被唤醒

package constxiong.concurrency.a024;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
* 测试线程饥饿
* @author ConstXiong
*/

public class TestThreadHungry {

private static ExecutorService es = Executors.newSingleThreadExecutor();

public static void main(String[] args) throws InterruptedException, ExecutionException {
Future<String> future1 = es.submit(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("提交任务1");
Future<String> future2 = es.submit(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("提交任务2");
return "任务 2 结果";
}
});
return future2.get();
}
});
System.out.println("获取到" + future1.get());
}

}

 

打印结果如下,线程池卡死。线程池只能容纳 1 个任务,任务 1 提交任务 2,任务 2 永远得不到执行。

提交任务1


更多相关文章

  1. 如何停止一个线程池?
  2. Java 中线程池包含哪些状态?
  3. 如何创建、启动 Java 线程?
  4. Executors如何创建线程池?
  5. 什么是线程?什么是进程?为什么要有线程?有什么关系与区别?
  6. 什么是死锁?
  7. 什么是线程池?
  8. 线程包括哪些状态?状态之间是如何转变的?
  9. 什么是守护线程?

随机推荐

  1. Android应用程序绑定服务(bindService)的过
  2. android外存储的状态
  3. Android(安卓)地址转换为经纬度
  4. Android(安卓)View 相关源码分析之三 Vie
  5. Android直播开源项目
  6. Android(安卓)HAL的被调用流程
  7. Android属性动画上手实现各种动画效果,自
  8. 2013年01月06日
  9. android Activity类的使用
  10. Intellij IDEA 导入 Android(安卓)SDK