精华内容
下载资源
问答
  • 2022-04-15 18:13:12

    一、多线程的资源竞争

    多线程编写的复杂度其实就是体现在资源的竞争控制上,这个在以前的文章中反复提到过。资源竞争主要是两种情况,数据竞争和执行任务竞争。通过前面的文章介绍可以知道,锁可以解决这些问题,但如何更好的处理竞争问题并达到最优,这就需要策略和算法了。包括提到的无锁编程,其实也是一种解决的策略。
    无论是在单核亦是多核的情况下,资源的竞争总是不可避免的,所以对资源的控制就是需要认真对待的一个问题。

    二、死锁

    所谓死锁,其实就是“集合中的每一个进程都在等待只能由本集合中的其他进程才能引发的事件,那么该组进程是死锁的。”死锁,即意味着程序已经无法继续工作,大量的计算机资源被浪费。死锁形成需要以下条件:
    1、互相持有
    就是A进程持有B进程需要继续工作的资源,反过来,B进程同时也持有A进程需要继续工作的资源。
    2、循环等待
    这个好理解,就是两个进程间互相需要对方的资源,而且不断等待着对方资源。
    3、不可剥夺
    和优先级一样,不能够一个进程强行从另外一进程把需要的资源强制夺取过来
    4、资源互斥
    这个意思是说资源必须是独的,一个进程拥有,另外一个进程就不能拥有。

    三、活锁

    活锁(liveLock),指线程间资源冲突激烈,引起线程不断的尝试获取资源,不断的失败。活锁有点类似于线程饥饿,虽然资源并没有被别人持有,但由于各种原因而无法得到。最常见的原因是进程组的执行顺序不合理,导致某些先需要的资源被后置。活锁和死锁的不同在于,活锁的状态是变化的,只是无法达到目的。活锁有可能在一定时间后自动解开,但死锁不能。
    一般来说,解决活锁的方式是采用退避的方式,也就是说让进程对资源的需求产生随机性,就有极大的可能解决活锁。

    四、竞争异常激烈

    护航现象(Lock Convoys),就是一个组进程中,等待同一个锁,如果持有这个锁的某个线程时间片轮转完成后,其它线程都需要这个持有锁的进程唤醒释放才能开始工作。而如果锁的实现是以公平的方式实现的,意味着所有的进程工作都必须按顺序进行,从而导致所有的进程都必须阻塞。
    在实际应用中,经常会遇到生产者消费者的问题,如果二者的速度严重不匹配,就会产生对锁竞争的激烈现象。也就是说在线程或进程的执行过程中,如果对锁操作的时间小于执行的时间就会产生激烈的锁获取动作,这时就可能产生护航现象。解决这种现象的一般方法是对资源进行拆分,把锁一个资源改成多个锁去锁多个资源,也就是说控制锁的粒度。

    五、总结

    锁的问题最好还是能够使用无锁编程来实现,这是对锁的一个最完美的解决,锁都没有了,它的各种不良的后果,自然就烟消云散了。

    更多相关内容
  • 主要介绍了Java中常见死锁与活锁的实例详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 活锁

    2021-04-11 17:01:15
    1、活锁的概念 2、活锁代码示例 2.1 代码示例 2.2 程序运行结果 3、解决活锁的两种方案 3.1 重试时休眠一个随机时间再进行重试 3.2 严格控制获取资源的顺序 1、活锁的概念 概念与定义:是指两个或两个以上...

    目录

    1、活锁的概念

    2、活锁代码示例

    2.1 代码示例

    2.2 程序运行结果

    3、解决活锁的两种方案

    3.1 重试时休眠一个随机时间再进行重试

    3.2 严格控制获取资源的顺序


    1、活锁的概念

           概念与定义:是指两个或两个以上的进程(或线程)在执行过程中,因不断地尝试性获取资源而造成的一种无限循环的现象。

    2、活锁代码示例

    2.1 代码示例

    package com.autocoding.lock.livelock;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * 
     * 场景:
     * <p> t1线程先尝试获取lock1,再尝试获取lock2,如果获取lock2失败,则释放lock1,再进行重试 </p>
     * <p> t2线程先尝试获取lock2,再尝试获取lock1,如果获取lock1失败,则释放lock2,再进行重试 </p>
     * 这种场景是会出现活锁现象的
     */
    @Slf4j
    public class LiveLockTest {
    	public static void main(String[] args) throws InterruptedException {
    		final Lock lock1 = new ReentrantLock();
    		final Lock lock2 = new ReentrantLock();
    
    		final Thread thread1 = new Thread(new Runnable() {
    
    			@Override
    			public void run() {
    				final AtomicInteger counter = new AtomicInteger();
    				while (true) {
    					if (lock1.tryLock()) {
    						try {
    							LiveLockTest.log.info("获取了lock1,准备获取lock2.....");
    							//为了模拟两个线程相互持有对方需要的锁,这里休眠一下
    							try {
    								TimeUnit.MILLISECONDS.sleep(1);
    							} catch (final InterruptedException e) {
    								// TODO Auto-generated catch block
    								e.printStackTrace();
    							}
    							if (lock2.tryLock()) {
    								try {
    									LiveLockTest.log.info("获取了lock2");
    									LiveLockTest.log.error("经过{}次重试之后,获取了两把锁,业务逻辑处理", counter);
    									break;
    								} finally {
    									lock2.unlock();
    								}
    							}
    						} finally {
    							lock1.unlock();
    						}
    					}
    					counter.incrementAndGet();
    				}
    
    			}
    		});
    		thread1.setName("线程1");
    		final Thread thread2 = new Thread(new Runnable() {
    
    			@Override
    			public void run() {
    				final AtomicInteger counter = new AtomicInteger();
    				while (true) {
    					if (lock2.tryLock()) {
    						try {
    							LiveLockTest.log.info("获取了lock2,准备获取lock1.....");
    							//为了模拟两个线程相互持有对方需要的锁,这里休眠一下
    							try {
    								TimeUnit.MILLISECONDS.sleep(1);
    							} catch (final InterruptedException e) {
    								// TODO Auto-generated catch block
    								e.printStackTrace();
    							}
    							if (lock1.tryLock()) {
    								try {
    									LiveLockTest.log.info("获取了lock1");
    									LiveLockTest.log.error("经过{}次重试之后,获取了两把锁,业务逻辑处理", counter);
    									break;
    								} finally {
    									lock1.unlock();
    								}
    							}
    						} finally {
    							lock2.unlock();
    						}
    					}
    					counter.incrementAndGet();
    				}
    
    			}
    		});
    		thread2.setName("线程2");
    		thread1.start();
    		thread2.start();
    		thread1.join();
    		thread2.join();
    	}
    }
    

     

    2.2 程序运行结果
     

    经过大量的重试时间,最终两个线程都可以获取锁,继续执行,但是大部分情况是,每次重试都是无效的重试,进行自旋,浪费cpu宝贵的执行时间。

    2021-04-11 17:07:25.985 [线程1] INFO  com.autocoding.lock.livelock.LiveLockTest - 获取了lock1,准备获取lock2.....
    2021-04-11 17:07:25.985 [线程2] INFO  com.autocoding.lock.livelock.LiveLockTest - 获取了lock2,准备获取lock1.....
    2021-04-11 17:07:25.990 [线程2] INFO  com.autocoding.lock.livelock.LiveLockTest - 获取了lock2,准备获取lock1.....
    2021-04-11 17:07:25.990 [线程1] INFO  com.autocoding.lock.livelock.LiveLockTest - 获取了lock1,准备获取lock2.....
    2021-04-11 17:07:25.992 [线程1] INFO  com.autocoding.lock.livelock.LiveLockTest - 获取了lock1,准备获取lock2.....
    2021-04-11 17:07:25.992 [线程2] INFO  com.autocoding.lock.livelock.LiveLockTest - 获取了lock2,准备获取lock1.....
    2021-04-11 17:07:25.994 [线程2] INFO  com.autocoding.lock.livelock.LiveLockTest - 获取了lock2,准备获取lock1.....
    2021-04-11 17:07:25.994 [线程1] INFO  com.autocoding.lock.livelock.LiveLockTest - 获取了lock1,准备获取lock2.....
    2021-04-11 17:07:25.996 [线程2] INFO  com.autocoding.lock.livelock.LiveLockTest - 获取了lock2,准备获取lock1.....
    2021-04-11 17:07:25.996 [线程1] INFO  com.autocoding.lock.livelock.LiveLockTest - 获取了lock1,准备获取lock2.....
    2021-04-11 17:07:25.998 [线程1] INFO  com.autocoding.lock.livelock.LiveLockTest - 获取了lock2
    2021-04-11 17:07:25.998 [线程1] ERROR com.autocoding.lock.livelock.LiveLockTest - 经过4次重试之后,获取了两把锁,业务逻辑处理
    2021-04-11 17:07:25.999 [线程2] INFO  com.autocoding.lock.livelock.LiveLockTest - 获取了lock2,准备获取lock1.....
    2021-04-11 17:07:26.001 [线程2] INFO  com.autocoding.lock.livelock.LiveLockTest - 获取了lock1
    2021-04-11 17:07:26.001 [线程2] ERROR com.autocoding.lock.livelock.LiveLockTest - 经过25620次重试之后,获取了两把锁,业务逻辑处理
    

     

    3、解决活锁的两种方案

    3.1 重试时休眠一个随机时间再进行重试

    package com.autocoding.lock.livelock;
    
    import java.util.Random;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * 
     * 场景:
     * <p> t1线程先尝试获取lock1,再尝试获取lock2,如果获取lock2失败,则释放lock1,再进行重试 </p>
     * <p> t2线程先尝试获取lock2,再尝试获取lock1,如果获取lock1失败,则释放lock2,再进行重试 </p>
     * 这种场景是会出现活锁现象的
     * <p>解决方案1</p>
     * 加入一些随机因素,再次竞争资源时,重试时间间隔加入一些随机时间
     */
    @Slf4j
    public class Solve1LiveLockTest {
    	public static void main(String[] args) throws InterruptedException {
    		final Lock lock1 = new ReentrantLock();
    		final Lock lock2 = new ReentrantLock();
    		final Thread thread1 = new Thread(new Runnable() {
    
    			@Override
    			public void run() {
    				while (true) {
    					if (lock1.tryLock()) {
    						try {
    							Solve1LiveLockTest.log.info("获取了lock1,准备获取lock2.....");
    							//为了模拟两个线程相互持有对方需要的锁,这里休眠一下
    							try {
    								TimeUnit.MILLISECONDS.sleep(1);
    							} catch (final InterruptedException e) {
    								// TODO Auto-generated catch block
    								e.printStackTrace();
    							}
    							if (lock2.tryLock()) {
    								try {
    									Solve1LiveLockTest.log.info("获取了lock2");
    									Solve1LiveLockTest.log.info("业务逻辑处理");
    									break;
    								} finally {
    									lock2.unlock();
    								}
    							}
    						} finally {
    							lock1.unlock();
    						}
    					}
    					// 加入一些随机因素,再次竞争资源时,重试时间间隔加入一些随机时间
    					try {
    						TimeUnit.MILLISECONDS.sleep(new Random().nextInt(10));
    					} catch (final InterruptedException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    
    				}
    
    			}
    		});
    		thread1.setName("线程1");
    		final Thread thread2 = new Thread(new Runnable() {
    
    			@Override
    			public void run() {
    				while (true) {
    					if (lock2.tryLock()) {
    						try {
    							Solve1LiveLockTest.log.info("获取了lock2,准备获取lock1.....");
    							//为了模拟两个线程相互持有对方需要的锁,这里休眠一下
    							try {
    								TimeUnit.MILLISECONDS.sleep(1);
    							} catch (final InterruptedException e) {
    								// TODO Auto-generated catch block
    								e.printStackTrace();
    							}
    							if (lock1.tryLock()) {
    								try {
    									Solve1LiveLockTest.log.info("获取了lock1");
    									Solve1LiveLockTest.log.info("业务逻辑处理");
    									break;
    								} finally {
    									lock1.unlock();
    								}
    							}
    						} finally {
    							lock2.unlock();
    						}
    					}
    					// 加入一些随机因素,再次竞争资源时,重试时间间隔加入一些随机时间
    					try {
    						TimeUnit.MILLISECONDS.sleep(new Random().nextInt(10));
    					} catch (final InterruptedException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    				}
    
    			}
    		});
    		thread2.setName("线程2");
    		thread1.start();
    		thread2.start();
    		thread1.join();
    		thread2.join();
    	}
    }
    

    程序运行结果:

    2021-04-11 17:09:10.895 [线程1] INFO  com.autocoding.lock.livelock.Solve1LiveLockTest - 获取了lock1,准备获取lock2.....
    2021-04-11 17:09:10.895 [线程2] INFO  com.autocoding.lock.livelock.Solve1LiveLockTest - 获取了lock2,准备获取lock1.....
    2021-04-11 17:09:10.899 [线程1] INFO  com.autocoding.lock.livelock.Solve1LiveLockTest - 获取了lock2
    2021-04-11 17:09:10.899 [线程1] INFO  com.autocoding.lock.livelock.Solve1LiveLockTest - 业务逻辑处理
    2021-04-11 17:09:10.909 [线程2] INFO  com.autocoding.lock.livelock.Solve1LiveLockTest - 获取了lock2,准备获取lock1.....
    2021-04-11 17:09:10.911 [线程2] INFO  com.autocoding.lock.livelock.Solve1LiveLockTest - 获取了lock1
    2021-04-11 17:09:10.911 [线程2] INFO  com.autocoding.lock.livelock.Solve1LiveLockTest - 业务逻辑处理

    3.2 严格控制获取资源的顺序

     

    package com.autocoding.lock.livelock;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * 
     * 场景:
     * <p> t1线程先尝试获取lock1,再尝试获取lock2,如果获取lock2失败,则释放lock1,再进行重试 </p>
     * <p> t2线程先尝试获取lock2,再尝试获取lock1,如果获取lock1失败,则释放lock2,再进行重试 </p>
     * 这种场景是会出现活锁现象的
     * <p>解决方案2</p>
     * 严格控制获取资源的顺序,所有的线程,都是先获取lock1,再获取lock2
     */
    @Slf4j
    public class Solve2LiveLockTest {
    	public static void main(String[] args) throws InterruptedException {
    		final Lock lock1 = new ReentrantLock();
    		final Lock lock2 = new ReentrantLock();
    		final Thread thread1 = new Thread(new Runnable() {
    
    			@Override
    			public void run() {
    				while (true) {
    					if (lock1.tryLock()) {
    						try {
    							Solve2LiveLockTest.log.info("获取了lock1,准备获取lock2.....");
    							//为了模拟两个线程相互持有对方需要的锁,这里休眠一下
    							try {
    								TimeUnit.MILLISECONDS.sleep(1);
    							} catch (final InterruptedException e) {
    								// TODO Auto-generated catch block
    								e.printStackTrace();
    							}
    							if (lock2.tryLock()) {
    								try {
    									Solve2LiveLockTest.log.info("获取了lock2");
    									Solve2LiveLockTest.log.info("业务逻辑处理");
    									break;
    								} finally {
    									lock2.unlock();
    								}
    							}
    						} finally {
    							lock1.unlock();
    						}
    					}
    
    				}
    
    			}
    		});
    		thread1.setName("线程1");
    		final Thread thread2 = new Thread(new Runnable() {
    
    			@Override
    			public void run() {
    				while (true) {
    					if (lock1.tryLock()) {
    						try {
    							Solve2LiveLockTest.log.info("获取了lock1,准备获取lock2.....");
    							//为了模拟两个线程相互持有对方需要的锁,这里休眠一下
    							try {
    								TimeUnit.MILLISECONDS.sleep(1);
    							} catch (final InterruptedException e) {
    								// TODO Auto-generated catch block
    								e.printStackTrace();
    							}
    							if (lock2.tryLock()) {
    								try {
    									Solve2LiveLockTest.log.info("获取了lock2");
    									Solve2LiveLockTest.log.info("业务逻辑处理");
    									break;
    								} finally {
    									lock2.unlock();
    								}
    							}
    						} finally {
    							lock1.unlock();
    						}
    					}
    
    				}
    
    			}
    		});
    		thread2.setName("线程2");
    		thread1.start();
    		thread2.start();
    		thread1.join();
    		thread2.join();
    	}
    }
    

     

    程序运行结果:

    2021-04-11 17:09:43.755 [线程2] INFO  com.autocoding.lock.livelock.Solve2LiveLockTest - 获取了lock1,准备获取lock2.....
    2021-04-11 17:09:43.759 [线程2] INFO  com.autocoding.lock.livelock.Solve2LiveLockTest - 获取了lock2
    2021-04-11 17:09:43.759 [线程2] INFO  com.autocoding.lock.livelock.Solve2LiveLockTest - 业务逻辑处理
    2021-04-11 17:09:43.759 [线程1] INFO  com.autocoding.lock.livelock.Solve2LiveLockTest - 获取了lock1,准备获取lock2.....
    2021-04-11 17:09:43.761 [线程1] INFO  com.autocoding.lock.livelock.Solve2LiveLockTest - 获取了lock2
    2021-04-11 17:09:43.761 [线程1] INFO  com.autocoding.lock.livelock.Solve2LiveLockTest - 业务逻辑处理
    

     

    展开全文
  • 但在出现竞争的情况下,其收敛速度很慢,甚至可能出现活锁的情况,例如当有等于或多于审批委员会数量的“提案委员”在同时发送提案请求后,很难有一个“提案委员”收到半数以上的回复而不断地执行第一阶段的协议。...
  • Java中的死锁与活锁

    2022-01-03 20:36:51
    活锁与死锁 活锁 活锁同样会发生在多个相互协作的线程间,当他们为了彼此间的响应而相互礼让,使得没有一个线程能够继续前进,那么就发生了活锁。同死锁一样,发生活锁的线程无法继续执行。 相当于两个在半路相遇的...

    活锁与死锁

    活锁

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

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

    死锁

    两个或更多线程阻塞着等待其它处于死锁状态的线程所持有的锁。死锁通常发生在多个线程同时但以不同的顺序请求同一组锁的时候,死锁会让你的程序挂起无法完成任务。

    死锁的四个必要条件

    在 Java 中使用多线程,就会有可能导致死锁问题。死锁会让程序一直住,不再程序往下执行。我们只能通过中止并重启的方式来让程序重新执行。这是我们非常不愿意看到的一种现象,我们要尽可能避免死锁的情况发生!

    互斥条件

    指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用完释放。

    请求和保持条件

    指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。

    不剥夺条件

    指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。

    环路等待条件

    指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{A,B,C,···,Z} 中的A正在等待一个B占用的资源;B正在等待C占用的资源,……,Z正在等待已被A占用的资源。

    死锁示例

    /** @author Strive */
    @SuppressWarnings("all")
    public class DeadLock implements Runnable {
      public int flag = 1;
    
      /** 面包 */
      private static final Object bread = new Object();
    
      /** 水 */
      private static final Object water = new Object();
    
      @Override
      public void run() {
        if (flag == 1) {
          // 先吃面包再喝水,环路等待条件
          synchronized (bread) {
            try {
              Thread.sleep(500);
            } catch (Exception e) {
              e.printStackTrace();
            }
            System.out.println("面包吃了,等喝水。。。");
            synchronized (water) {
              System.out.println("面包吃了,水也喝到了");
            }
          }
        }
        if (flag == 0) {
          // 先喝水再吃面包,环路等待条件
          synchronized (water) {
            try {
              Thread.sleep(500);
            } catch (Exception e) {
              e.printStackTrace();
            }
            System.out.println("喝完水,等吃面包了。。。");
            synchronized (bread) {
              System.out.println("喝完水,面包也吃完了。。。");
            }
          }
        }
      }
    
      public static void main(String[] args) {
        DeadLock lockBread = new DeadLock();
        DeadLock lockWater = new DeadLock();
        lockBread.flag = 1;
        lockWater.flag = 0;
        // lockBread,lockWater 都处于可执行状态,但 JVM 线程调度先执行哪个线程是不确定的。
        // lockWater 的 run() 可能在 lockBread 的 run() 之前运行
        new Thread(lockBread).start();
        new Thread(lockWater).start();
      }
    }
    

    程序运行结果:

    喝完水,等吃面包了。。。
    面包吃了,等喝水。。。
    

    死锁排查

    先执行上面的代码,再进行排查!

    1、使用 jps -l

    D:\workspace-code\gitee\dolphin>jps -l
    7072 org.jetbrains.jps.cmdline.Launcher
    8824 sun.tools.jps.Jps
    17212
    4012 com.dolphin.thread.locks.DeadLock
    6684 org.jetbrains.idea.maven.server.RemoteMavenServer36
    

    4012 com.dolphin.thread.locks.DeadLock,粘贴进程号 4012

    2、使用 jstack 4012

    ... ...
    Java stack information for the threads listed above:
    ===================================================
    "Thread-1":
            at com.dolphin.thread.locks.DeadLock.run(DeadLock.java:40)
            - waiting to lock <0x000000076bf9e3d8> (a java.lang.Object)
            - locked <0x000000076bf9e3e8> (a java.lang.Object)
            at java.lang.Thread.run(Thread.java:748)
    "Thread-0":
            at com.dolphin.thread.locks.DeadLock.run(DeadLock.java:26)
            - waiting to lock <0x000000076bf9e3e8> (a java.lang.Object)
            - locked <0x000000076bf9e3d8> (a java.lang.Object)
            at java.lang.Thread.run(Thread.java:748)
    
    Found 1 deadlock.
    

    从打印信息我们可以看出:

    1. Found 1 deadlock: 发现一个死锁;
    2. Thread-1 locked <0x000000076bf9e3e8> waiting to lock <0x000000076bf9e3d8>:线程1,锁住了 0x000000076bf9e3e8 等待 0x000000076bf9e3d8 锁;
    3. Thread-0 locked <0x000000076bf9e3d8> waiting to lock <0x000000076bf9e3e8>:线程0,锁住了 0x000000076bf9e3d8 等待 0x000000076bf9e3e8 锁;

    image-20220103152621661

    总结一下

    1. 当 DeadLock 类的对象 flag=1 时(lockBread),先锁定 bread,睡眠500毫秒;
    2. 而 lockBread 在睡眠的时候另一个 flag==0 的对象(lockWater)线程启动,先锁定 water,睡眠500毫秒;
    3. lockBread 睡眠结束后需要锁定 water 才能继续执行,而此时 water 已被 lockWater 锁定;
    4. lockWater 睡眠结束后需要锁定 bread 才能继续执行,而此时 bread 已被 lockBread 锁定;
    5. lockBread、lockWater 相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁;

    如何避免死锁

    预防死锁

    其实就是破坏死锁的四个必要条件!!!

    • 破坏互斥条件:使资源同时访问而非互斥使用,就没有进程会阻塞在资源上,从而不发生死锁。
    • 破坏请求和保持条件:采用静态分配的方式,静态分配的方式是指进程必须在执行之前就申请需要的全部资源,且直至所要的资源全部得到满足后才开始执行,只要有一个资源得不到分配,也不给这个进程分配其他的资源。
    • 破坏不剥夺条件:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源,但是只适用于内存和处理器资源。
    • 破坏循环等待条件:给系统的所有资源编号,规定进程请求所需资源的顺序必须按照资源的编号依次进行。

    设置加锁顺序

    两个线程试图以不同的顺序来获得相同的锁,如果按照相同的顺序来请求锁,那么就不会出现循环的加锁依赖性,因此也就不会产生死锁,每个需要锁面包和锁水的线程都以相同的顺序来获取面包和水,那么就不会发生死锁了,如下图所示:

    image-20220103154444206

    根据上图我们修改一下之前写的死锁代码,消除死锁!

    @Override
    public void run() {
        if (flag == 1) {
            // 先吃面包再喝水,环路等待条件
            synchronized (bread) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("面包吃了,等喝水。。。");
            }
    
            synchronized (water) {
                System.out.println("面包吃了,水也喝到了");
            }
        }
        if (flag == 0) {
            // 先喝水再吃面包,环路等待条件
            synchronized (water) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("喝完水,等吃面包了。。。");
            }
    
            synchronized (bread) {
                System.out.println("喝完水,面包也吃完了。。。");
            }
        }
    }
    

    程序运行结果:

    喝完水,等吃面包了。。。
    面包吃了,等喝水。。。
    面包吃了,水也喝到了
    喝完水,面包也吃完了。。。
    

    这样就可以消除死锁发生~~~

    活锁示例

    import java.lang.management.ManagementFactory;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    /** @author Strive */
    public class LiveLock {
      static ReentrantLock bread = new ReentrantLock();
      static ReentrantLock water = new ReentrantLock();
    
      public static void main(String[] args) {
        String name = ManagementFactory.getRuntimeMXBean().getName();
        String pid = name.split("@")[0];
        System.out.println("Pid is:" + pid);
        System.out.println("jstack " + pid);
    
        ExecutorService executorService = Executors.newCachedThreadPool();
    
        executorService.submit(
            () -> {
              try {
                // 尝试获得 bread 锁
                while (bread.tryLock(10, TimeUnit.SECONDS)) {
                  System.out.println("拿到面包了,等着拿水。。。");
                  TimeUnit.SECONDS.sleep(1);
    
                  // 判断 water 是否被锁住,如果锁住,退出!再次尝试获取 water 锁
                  boolean locked = water.isLocked();
                  if (locked) {
                    bread.unlock();
                    continue;
                  }
    
                  // 尝试获得 water 锁
                  boolean w = water.tryLock(10, TimeUnit.SECONDS);
                  // 如果没有获取到 water 锁,释放 bread 锁,再次尝试!
                  if (!w) {
                    bread.unlock();
                    continue;
                  }
                  System.out.println("拿到水了");
                  break;
                }
                System.out.println("吃面包,喝水!");
              } catch (InterruptedException e) {
                System.out.println("线程中断");
              } finally {
                water.unlock();
                bread.unlock();
              }
            });
    
        executorService.submit(
            () -> {
              try {
                while (water.tryLock(10, TimeUnit.SECONDS)) {
                  System.out.println("拿到水了,等着拿面包。。。");
                  TimeUnit.SECONDS.sleep(2);
    
                  // 判断 bread 是否被锁住,如果锁住,退出!再次尝试获取 bread 锁
                  boolean locked = bread.isLocked();
                  if (locked) {
                    water.unlock();
                    continue;
                  }
    
                  // 尝试获得 bread 锁
                  boolean b = bread.tryLock(10, TimeUnit.SECONDS);
                  // 如果没有获取到 bread 锁,释放 water 锁,再次尝试!
                  if (!b) {
                    water.unlock();
                    continue;
                  }
                  System.out.println("拿到面包了");
                  break;
                }
                System.out.println("喝水,吃面包!");
              } catch (InterruptedException e) {
                System.out.println("线程中断");
              } finally {
                bread.unlock();
                water.unlock();
              }
            });
        executorService.shutdown();
      }
    }
    

    程序运行结果:

    Pid is:8788
    jstack 8788
    拿到面包了,等着拿水。。。
    拿到水了,等着拿面包。。。
    拿到面包了,等着拿水。。。
    拿到水了,等着拿面包。。。
    拿到面包了,等着拿水。。。
    拿到面包了,等着拿水。。。
    拿到水了,等着拿面包。。。
    

    已经输出打印了 Pid,尝试自己用 jstack 调试一下吧!可以参考如何排查死锁的章节~

    解决活锁

    锁让出的时候添加随机睡眠时间

    修改上面的程序,参考下面的代码:

    executorService.submit(
            () -> {
              try {
                // 尝试获得 bread 锁
                while (bread.tryLock(10, TimeUnit.SECONDS)) {
                  System.out.println("拿到面包了,等着拿水。。。");
                  TimeUnit.SECONDS.sleep(1);
    
                  // 判断 water 是否被锁住,如果锁住,退出!再次尝试获取 water 锁
                  boolean locked = water.isLocked();
                  if (locked) {
                    bread.unlock();
                    // 避免活锁
                    TimeUnit.MILLISECONDS.sleep(1000);
                    continue;
                  }
    
                  // 尝试获得 water 锁
                  boolean w = water.tryLock(10, TimeUnit.SECONDS);
                  // 如果没有获取到 water 锁,释放 bread 锁,再次尝试!
                  if (!w) {
                    bread.unlock();
                    continue;
                  }
                  System.out.println("拿到水了");
                  break;
                }
                System.out.println("吃面包,喝水!");
              } catch (InterruptedException e) {
                System.out.println("线程中断");
              } finally {
                water.unlock();
                bread.unlock();
              }
            });
    
    executorService.submit(
        () -> {
            try {
                while (water.tryLock(10, TimeUnit.SECONDS)) {
                    System.out.println("拿到水了,等着拿面包。。。");
                    TimeUnit.SECONDS.sleep(2);
    
                    // 判断 bread 是否被锁住,如果锁住,退出!再次尝试获取 bread 锁
                    boolean locked = bread.isLocked();
                    if (locked) {
                        water.unlock();
                        // 避免活锁
                        TimeUnit.MILLISECONDS.sleep(1500);
                        continue;
                    }
    
                    // 尝试获得 bread 锁
                    boolean b = bread.tryLock(10, TimeUnit.SECONDS);
                    // 如果没有获取到 bread 锁,释放 water 锁,再次尝试!
                    if (!b) {
                        water.unlock();
                        continue;
                    }
                    System.out.println("拿到面包了");
                    break;
                }
                System.out.println("喝水,吃面包!");
            } catch (InterruptedException e) {
                System.out.println("线程中断");
            } finally {
                bread.unlock();
                water.unlock();
            }
        });
    executorService.shutdown();
    

    程序输出结果:

    Pid is:18256
    jstack 18256
    拿到面包了,等着拿水。。。
    拿到水了,等着拿面包。。。
    拿到面包了
    喝水,吃面包!
    拿到面包了,等着拿水。。。
    拿到水了
    吃面包,喝水!
    
    Process finished with exit code 0
    
    展开全文
  • 活锁4.1 活锁原因4.2 活锁解决5. 饥饿6. 使用ReentrantLock解决死锁、活锁、饥饿6.1 解决哲学家就餐死锁问题 1. 死锁 死锁:一组互相竞争资源的线程因互相等待,导致“永久”阻塞的现象。 说白了就是:两个线程...

    1. 死锁

    死锁:一组互相竞争资源的线程因互相等待,导致“永久”阻塞的现象。

    说白了就是:两个线程互相持有对方所需的资源,互不释放且互相等待

    • t1 线程获得A对象锁,接下来想获取B对象的锁
    • t2 线程获得B对象锁,接下来想获取A对象的锁

    我们举个栗子:

    public class DeadLock {
    
        public static void main(String[] args) {
            Object A = new Object(); //锁A
            Object B = new Object(); //锁B
            new Thread(()->{
                synchronized (A){
                    log.debug("t1 lock A");
                    sleep(0.5);
                    synchronized (B){
                        log.debug("t1 lock B");
                        log.debug("操作...");
                    }
                }
            },"t1").start();
    
    
            new Thread(()->{
                synchronized (B){
                    log.debug("t2 lock B");
                    sleep(0.5);
                    synchronized (A){
                        log.debug("t2 lock A");
                        log.debug("操作...");
                    }
                }
            },"t2").start();
        }
    
        static void sleep(double time){
            try {
                Thread.sleep((int)(1000 * time));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    

    可以看到t1和t2各持有一把锁,当尝试获取对方线程锁的时候自己却不释放锁,这就导致死锁发生

    2. 定位死锁

    如果发生了死锁,那我们如何定位死锁呢?我们需要借助一些工具:

    • 基于命令行: jps 定位进程 id,再用 jstack 定位死锁

    • 基于图形化界面:可以使用 jconsole工具

    2.1 jstack工具使用

    我们首先在项目路径下使用JPS命令查看正在运行的Java线程

    image-20220506203716053

    接着我们可以使用jstack+可能发生死锁的线程ID打印线程情况:

    image-20220506204054847

    然后我们就可以看到一些线程的信息

    image-20220506204647558

    当然我们也能在最后面看到发生死锁的信息

    image-20220506204920057

    2.2 jconsole工具使用:

    Jconsole (Java Monitoring and Management Console),是一种基于JMX的可视化监视、管理工具、

    Jconsole 基本使用:

    • 点击JDK/bin 目录下面的jconsole.exe 即可启动
    • 然后会自动自动搜索本机运行的所有虚拟机进程。
    • 选择其中一个进程可开始进行监视

    image-20220506205337535

    可以看到本机所有的虚拟机进程

    image-20220506205530750

    检测死锁

    image-20220506205848464

    可以查看到线程发生死锁的信息和发生死锁的行数

    image-20220506211021343

    注意:

    • 避免死锁要注意加锁顺序
    • 另外如果由于某个线程进入了死循环,导致其它线程一直等待,对于这种情况 linux 下可以通过 top 先定位到 CPU 占用高的 Java 进程,再利用 top -Hp 进程id 来定位是哪个线程,最后再用 jstack 排查

    3. 解决死锁

    3.1 哲学家就餐问题

    在解决死锁之前,我们先来通过一个经典的问题来分析一下死锁

    image-20220506211454918

    有五位哲学家,围坐在圆桌旁。

    • 他们只做两件事,思考和吃饭,思考一会吃口饭,吃完饭后接着思考。
    • 吃饭时要用两根筷子吃,桌上共有 5 根筷子,每位哲学家左右手边各有一根筷子。
    • 如果筷子被身边的人拿着,自己就得等待

    五位哲学家相当于五个线程,无根筷子相当于五份互斥的资源,当每个哲学家即线程持有一根筷子时,他们都在等待另一个线程释放锁,因此造成了死锁(典型的持有一个资源,等待对方释放资源的场景)

    这种线程没有按预期结束,执行不下去的情况,归类为【活跃性】问题,除了死锁以外,还有活锁和饥饿者两种情况

    下面通过具体的代码演示下这个死锁的问题:

    筷子类:

    public class Chopstick {
        String name;
        public Chopstick(String name) {
            this.name = name;
        }
        @Override
        public String toString() {
            return "筷子{" + name + '}';
        }
    }
    

    哲学家类:

    public class Philosopher extends Thread {
        Chopstick left;
        Chopstick right;
        public Philosopher(String name, Chopstick left, Chopstick right) {
            super(name);
            this.left = left;
            this.right = right;
        }
        private void eat() {
            log.debug("eating...");
            Sleeper.sleep(1);
        }
    
        @Override
        public void run() {
            while (true) {
                // 获得左手筷子
                synchronized (left) {
                    // 获得右手筷子
                    synchronized (right) {
                        // 吃饭
                        eat();
                    }
                    // 放下右手筷子
                }
                // 放下左手筷子
            }
        }
    }
    

    测试类:

    public class TestPhilosopher {
        public static void main(String[] args) {
            Chopstick c1 = new Chopstick("1");
            Chopstick c2 = new Chopstick("2");
            Chopstick c3 = new Chopstick("3");
            Chopstick c4 = new Chopstick("4");
            Chopstick c5 = new Chopstick("5");
            new Philosopher("苏格拉底",c1, c2).start();
            new Philosopher("柏拉图", c2, c3).start();
            new Philosopher("亚里士多德", c3, c4).start();
            new Philosopher("赫拉克利特", c4, c5).start();
            new Philosopher("阿基米德", c5, c1).start();
        }
    
    }
    

    我们的程序运行一会后发生了死锁:

    image-20220506213705769

    使用Jconsole可以看到:

    -------------------------------------------------------------------------
    名称: 阿基米德
    状态: com.fx.Synchronized.philosopher@1540e19d (筷子1) 上的BLOCKED, 拥有者: 苏格拉底
    总阻止数: 2, 总等待数: 1
    堆栈跟踪:
    com.fx.Synchronized.philosopher.run(TestDinner.java:48)
     - 已锁定 com.fx.Synchronized.philosopher@6d6f6e28 (筷子5)
    -------------------------------------------------------------------------
    名称: 苏格拉底
    状态: com.fx.Synchronized.philosopher@677327b6 (筷子2) 上的BLOCKED, 拥有者: 柏拉图
    总阻止数: 2, 总等待数: 1
    堆栈跟踪:
    com.fx.Synchronized.philosopher.run(TestDinner.java:48)
     - 已锁定 com.fx.Synchronized.philosopher@1540e19d (筷子1)
    -------------------------------------------------------------------------
    名称: 柏拉图
    状态: com.fx.Synchronized.philosopher@14ae5a5 (筷子3) 上的BLOCKED, 拥有者: 亚里士多德
    总阻止数: 2, 总等待数: 0
    堆栈跟踪:
    com.fx.Synchronized.philosopher.run(TestDinner.java:48)
     - 已锁定 com.fx.Synchronized.philosopher@677327b6 (筷子2)
    -------------------------------------------------------------------------
    名称: 亚里士多德
    状态: com.fx.Synchronized.philosopher@7f31245a (筷子4) 上的BLOCKED, 拥有者: 赫拉克利特
    总阻止数: 1, 总等待数: 1
    堆栈跟踪:
    com.fx.Synchronized.philosopher.run(TestDinner.java:48)
     - 已锁定 com.fx.Synchronized.philosopher@14ae5a5 (筷子3)
    -------------------------------------------------------------------------
    名称: 赫拉克利特
    状态: com.fx.Synchronized.philosopher@6d6f6e28 (筷子5) 上的BLOCKED, 拥有者: 阿基米德
    总阻止数: 2, 总等待数: 0
    堆栈跟踪:
    com.fx.Synchronized.philosopher.run(TestDinner.java:48)
     - 已锁定 com.fx.Synchronized.philosopher@7f31245a (筷子4)
    
    

    这种线程没有按预期结束,执行不下去的情况,归类为【活跃性】问题,除了死锁以外,还有 活锁饥饿者 两种情况

    解决方案在第六节

    4. 活锁

    4.1 活锁原因

    活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束

    举个栗子:

    public class LiveLock {
        static volatile int count = 10;
        static final Object lock = new Object();
    
        public static void main(String[] args) {
            new Thread(() -> {
                // 期望减到 0 退出循环
                while (count > 0) {
                    Sleeper.sleep(0.2);
                    count--;
                    log.debug("count: {}", count);
                }
            }, "t1").start();
            new Thread(() -> {
                // 期望超过 20 退出循环
                while (count < 20) {
                    Sleeper.sleep(0.2);
                    count++;
                    log.debug("count: {}", count);
                }
            }, "t2").start();
        }
    }
    
    

    观察程序运行结果,我们会发现这两个线程在一直运行,且不会停止,因为自己线程的停止条件被其他线程修改了,导致线程停止不了

    活锁和死锁不同,死锁是线程发生堵塞,而活锁是线程不发生堵塞但是却不会停止

    image-20220506215312901

    4.2 活锁解决

    • 让互相影响的线程执行的顺序交错开
    • 睡眠的时间差异化
    • 在开发中可以通过增加随机睡眠时间来避免活锁

    5. 饥饿

    饥饿:线程因无法访问所需资源而无法执行下去的情况,说白了就是:假设有1万个线程,还没等前面的线程执行完,后面的线程就饿死了

    很多教程中把饥饿定义为,一个线程由于优先级太低,始终得不到 CPU 调度执行,也不能够结束

    先来看看使用顺序加锁的方式解决之前的死锁问题,就是两个线程对两个不同的对象加锁的时候都使用相同的顺序进行加锁

    我们看下面的图,当线程1和线程2都分别持有一把锁(获取锁的顺序不一样),又想要获取对方锁的时候就会发生死锁情况

    image-20220506215750664

    我们可以通过顺序加锁的方式来避免这种死锁,即线程1和线程2都按A、B的顺序去获取锁

    image-20220506223726810

    显然这样就可以解决死锁的情况发生,但是这样又可能会导致饥饿的现象发生

    这里以上面哲学家就餐的例子演示一下:

    我们看之前的代码,都是按顺序获取锁

    image-20220506225641716

    我们现在将其改成不按顺序争抢锁

    image-20220506225607689

    我们观察运行结果就会发现一个神奇的现象

    • 首先并没有发生死锁
    • 赫拉克利特线程获取锁的频率最高,等运行几次后阿基米德线程就再也没有获取到锁了

    image-20220506225924451

    这样就是一种线程饥饿的现象发生了,即一些线程一直等不到cpu调度

    6. 使用ReentrantLock解决死锁、活锁、饥饿

    ReentrantLock相对于 synchronized 它具备如下特点

    1. 可中断
    2. 可以设置超时时间
    3. 可以设置为公平锁
    4. 支持多个条件变量,即对与不满足条件的线程可以放到不同的集合中等待

    ReentrantLock基本使用思维导图

    image-20220503003158484

    6.1 解决哲学家就餐死锁问题

    我们知道导致死锁必须的四个条件:

    1. 互斥条件:共享资源被一个线程占用
    2. 请求与保持条件(占有且等待):一个进程因请求资源而被阻塞时,对已经获得资源保持不释放
    3. 不可剥夺条件(不可抢占):进程已获得资源,在未使用完之前,不能进行剥夺
    4. 循环等待条件:多个线程 循环等待资源,而且是循环的互相等待

    对应着如果想要避免死锁,我们只需要让上面的四个条件其中一个不成立即可

    1. 请求与保持条件:放大锁范围,去除对资源的抢占
    2. 不剥夺:换成可重入锁ReentrantLock
    3. 循环等待:改成顺序加锁,避免循环等待
    4. 互斥是多线程的特性,所以这个条件无法避免

    我们可以看到发生死锁的代码主要是下面的代码

    当前线程获得左手筷子线程时,由于右手筷子拿不到就会一直等待,导致死锁(锁不可剥夺)

    // 获得左手筷子
    synchronized (left) {
        // 获得右手筷子
        synchronized (right) {
            // 吃饭
            eat();
        }
        // 放下右手筷子
    }
    // 放下左手筷子
    

    现在我们可以通过ReentrantLock的tryLock对上面的代码进行改进

    首先让筷子类继承自ReentrantLock,让其拥有锁的性质

    public class ReentrantLockChopstick extends ReentrantLock {...}
    

    接着我们改写上面的代码

    while (true) {
        // 获得左手筷子
        if(left.tryLock()){
            try {
                //尝试获得右手筷子
                if(right.tryLock()){
                    try {
                    	eat();
                    } finally {
                        right.unlock();
                    }
                }
            } finally {
                left.unlock();
            }
        }
        // 放下左手筷子
    }
    

    接着测试一下,我们会发现死锁问题解决

    总之,死锁的问题就是要解决资源不可剥夺、循环等待、请求与保持条件等问题,而在Java并发包内提供了很多工具类和方法来为我们保证多线程下程序的安全性

    展开全文
  • 解决死锁代码实现活锁概念活锁示例:如何解决活锁呢?饥饿概念如何解决饥饿呢?死锁概念死锁:一组互相竞争资源的线程因互相等待,导致“永久”阻塞的现象。说白了就是:两个线程互相持有对方所需的资源,互不释放且...
  • Java 中的活锁和死锁

    2021-04-17 08:39:14
    # Java 中的活锁和死锁> 原文: [https://javatutorial.net/livelock-and-deadlock-in-java](https://javatutorial.net/livelock-and-deadlock-in-java)以下文章讨论了 Java 中的活锁和死锁状态,它们如何发生...
  • Java中的活锁

    2022-05-23 15:39:24
    活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束,例如 public class TestLiveLock { static volatile int count = 10; static final Object lock = new Object(); public static void main...
  • 有人可以举例说明(代码) 死锁和活锁有什么区别吗?
  • Java线程活锁

    2021-01-27 13:44:18
    活锁是一种递归情况,其中两个或更多线程将继续重复特定的代码逻辑。预期的逻辑通常是给其他线程机会以支持“此”线程。 当两个人在狭窄的走廊里相遇时,发生了现实生活中的活锁例子,每个人都试图通过移动到一边让...
  • 活锁的产生

    2021-12-08 14:07:34
    活锁定义 程序一直在运行,也没有被阻塞,却一直获取不到结果。 活锁一般是因为代码上流程的问题导致的。 活锁会一直消耗系统资源。 相关案例 特定状态的单据需要同步给其他系统,同步成功后更改状态。 原有...
  • 多线程系列之死锁、活锁、饥饿
  • } } } } 其中的Chopstick类需要继承ReentrantLock作为锁对象来使用 活锁 活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束,例如 public static int count=10; public final static Object lock=new ...
  • 什么是活锁和饥饿?及示例

    千次阅读 2019-09-26 09:27:47
    活锁 任务没有被阻塞,由于某些条件没有满足,导致一直重复尝试—失败—尝试—失败的过程。 处于活锁的实体是在不断的改变状态,活锁有可能自行解开。 死锁是大家都拿不到资源都占用着对方的资源,而活锁是拿到...
  • 彻底理解死锁和活锁 java实现

    千次阅读 2020-07-10 11:29:39
    以下使用转账场景来解释说明活锁和死锁,以及4种正确的实现方法。 首先认识两个基础类:抽象账户类和工具类: 参考文章:极客时间 Java并发编程实战( https://time.geekbang.org/column/article/85001) 抽象账户类...
  • 5. 活锁6. 饥饿 1. 什么是死锁? 死锁是指两个或两个以上线程因抢占锁而造成的相互等待的现象。 线程X按照先后次序去抢占锁A与锁B,线程Y按照先后次序去抢占锁B与锁A,当线程X抢到锁A再去抢占锁B时,发现已经被其他...
  • 什么是活锁 活锁:线程一直处于运行状态,但是其任务却一直无法进展的一种活性故障. 活锁和死锁的区别在于,处于活锁的实体是在不断的改变状态,而处于死锁的实体表现为等待;活锁有可能自行解开,死锁则不能。 什么...
  • 操作系统:线程死锁、饥饿、活锁
  • 多线程—活锁

    2021-10-21 17:44:44
    概念:活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束。 示例代码: @Slf4j public class LockTset { //静态共享变量 private static int count = 10; public static void main(String[] args) ...
  • 1、什么是死锁 死锁,是指两个或两个以上的进程(或线程)执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用干预,他们都死等下去,也都将无法...2、什么是活锁 任务或者执行者没有被阻塞,但由于某些
  • 多线程死锁,活锁,竞争锁问题总结。举例分析产生各种锁的原因以及解决方法
  • 死锁和活锁
  • 怎么避免活锁 多个线程重复的获取锁和释放锁导致了活锁,可以修改下代码加一个随机时间间隔来获取锁,这样就有机会获取到需要的锁了。 消息队列活锁(消费者异常回滚事务并把消息丢到队列头部,然后相同的消息又被...
  • 并发控制之活锁和死锁

    千次阅读 2022-02-09 09:39:08
    活锁 活锁 事务T1封锁了数据R 事务T2又请求封锁R,于是T2等待。 T3也请求封锁R,当T1释放了R上的封锁之后系统首先批准了T3的请求,T2仍然等待。 T4又请求封锁R,当T3释放了R上的封锁之后系统又批准了T4的请求…… T2...
  • 死锁与活锁的区别

    2021-02-03 13:19:08
    死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。产生死锁的必要条件:互斥条件:所谓互斥就是进程在某一...活锁:任务或者执...
  • 死锁、活锁、饥饿,是关于多线程是否活跃出现的运行阻塞障碍问题,若线程出现这三种情况,即线程不再活跃,无法再正常地执行下去。 1、死锁 死锁,是多线程中最差的一种情况,多个线程相互占用对方的资源的锁,而...
  • 并发控制——封锁、活锁和死锁

    千次阅读 2022-04-20 19:18:25
    而不是必要条件 即可串行化的调度中,不一定所有事务都必须符合两段锁协议 死锁和活锁 封锁技术可以有效地解决并行操作的一致性问题 但也带来了一些新的问题 死锁 活锁 活锁 什么是活锁 系统可能使某个事务永远处于...
  • 活锁:是指线程1可以使用资源,但它很礼貌,让其他线程先使用资源,线程2也可以使用资源,但它很绅士,也让其他线程先使用资源。这样你让我,我让你,最后两个线程都无法使用资源。 ......

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,884
精华内容 4,753
关键字:

活锁

友情链接: vue-elem.zip