看完后,你再也不用怕面试问并发编程啦( 四 )


看完后,你再也不用怕面试问并发编程啦

文章插图
执行sleep的时候会让出CPU,但不是释放锁 。package cn.itcast.thread;/** 学习死锁的概念和解决死锁 */public class DeadLock { // 定义两个对象作为锁 private static Object objA = new Object(); private static Object objB = new Object(); public static void main(String[] args) { // 线程1 Thread t1 = new Thread(() -> { // 同步锁 synchronized (objA){ try { // 线程休眠(让出CPU,不释放锁) Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("AAAAAAA"); // 同步锁 synchronized (objB){ System.out.println("BBBBBBB"); } } }); // 线程2 Thread t2 = new Thread(() -> { // 同步锁 synchronized (objB){ System.out.println("CCCCCCC"); // 同步锁 synchronized (objA){ System.out.println("DDDDDDD"); } } }); t1.start(); t2.start(); }} 上面的代码只是演示死锁的场景,在现实中你可能不会写出这样的代码 。但是,在一些更为复杂的场景中,你可能会遇到这样的问题,比如t1拿到锁之后,因为一些异常情况没有释放锁(死循环) 。又或者是t1拿到一个数据库锁,释放锁的时候抛出了异常,没释放掉 。一旦出现死锁,业务是可感知的,因为不能继续提供服务了 。
查看线程执行情况
打开cmd命令dos窗口输入如下命令
jps命令:查看Java程序进程id信息
jstack命令:查看指定进程堆栈信息
看完后,你再也不用怕面试问并发编程啦

文章插图

看完后,你再也不用怕面试问并发编程啦

文章插图
如何避免死锁?
现在,我们介绍避免死锁的几个常见方法:
  • 避免一个线程同时获取多个锁 。
  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源 。
  • 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制 。
小结
1.什么是死锁: 多线程竞争共享资源,导致线程相互等待,程序无法向下执行 。
2.死锁产生的条件
  • 有多个线程   
  • 有多把锁   
  • 有同步代码块嵌套
3.如何避免死锁: 干掉其死锁产生的条件中一个条件即可 。7、并发编程需要处理的问题:上下文过度切换多线程一定快吗?
看完后,你再也不用怕面试问并发编程啦

文章插图
测试代码
代码演示串行和并发执行并累加操作的时间,请分析: 下面的代码并发执行一定比串行执行快吗?package cn.itcast.thread;public class Test4 {// 定义变量private static final long count = 1000000000;public static void main(String[] args) throws InterruptedException {concurrency();serial();}// 定义方法1(使用线程)private static void concurrency() throws InterruptedException {long start = System.currentTimeMillis();// 创建线程 循环累加Thread thread = new Thread(() -> {int a = 0;for (long i = 0; i < count; i++) {a += 5;}});// 开启线程thread.start();// 循环累减int b = 0;for (long i = 0; i < count; i++) {b--;}long time = System.currentTimeMillis() - start;// thread.join();System.out.println("concurrency :" + time + "ms,b=" + b);}// 定义方法2 (不用线程)private static void serial() {long start = System.currentTimeMillis();// 循环累加int a = 0;for (long i = 0; i < count; i++) {a += 5;}// 循环累减int b = 0;for (long i = 0; i < count; i++) {b--;}long time = System.currentTimeMillis() - start;System.out.println("serial:" + time + "ms,b=" + b );}} 测试结果
上述问题的答案是“不一定”,测试结果如表所示:
看完后,你再也不用怕面试问并发编程啦

文章插图
当并发执行累加操作不超过百万次时,速度会比串行执行累加操作要慢 。那么,为什么并发执行的速度会比串行慢呢?这是因为线程有创建和上下文切换的开销 。
上下文切换