精华内容
下载资源
问答
  • 通俗的说,死锁就是两个或者线程吗,相互占用对方需要的资源,而都不进行释放,导致彼此之间都相互等待对方释放资源,产生了无限制等待的现象。死锁一旦发生,如果没有外力介入,这种等待将永远存在,从而对程序...

    特别感谢:慕课网jimin老师的《Java并发编程与高并发解决方案》课程,以下知识点多数来自老师的课程内容。
    jimin老师课程地址:Java并发编程与高并发解决方案


    什么是死锁?

    通俗的说,死锁就是两个或者多个线程,相互占用对方需要的资源,而都不进行释放,导致彼此之间都相互等待对方释放资源,产生了无限制等待的现象。死锁一旦发生,如果没有外力介入,这种等待将永远存在,从而对程序产生严重影响。
    用来描述死锁的问题最有名的场景就是“哲学家就餐问题”。哲学家就餐问题可以这样表述:假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事之一:吃饭或者思考。吃东西的时候他们就停止思考,思考的时候也停止吃东西。餐桌中间有一大碗意大利面,每两个哲学家之间有一只餐叉。因为只用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东西。他们只能使用自己左右手边的那两只餐。哲学家从来不交谈,这就跟危险,可能产生死锁,每个哲学家都拿着左手的餐叉永远等右边的餐叉(或者相反)….

    死锁产生的必要条件

    • 互斥条件:进程对锁分配的资源进行排他性使用
    • 请求和保持条件:线程已经保持了一个资源,但是又提出了其他请求,而该资源已被其他线程占用
    • 不剥夺条件:在使用时不能被剥夺,只能自己用完释放
    • 环路等待条件:资源调用是一个环形的链

    死锁示例

    @Slf4j
    public class DeadLock implements Runnable {
        public int flag = 1;
        //静态对象是类的所有对象共享的
        private static Object o1 = new Object(), o2 = new Object();
    
        @Override
        public void run() {
            log.info("flag:{}", flag);
            if (flag == 1) {
                synchronized (o1) {
                    try {
                        Thread.sleep(500);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    synchronized (o2) {
                        log.info("1");
                    }
                }
            }
            if (flag == 0) {
                synchronized (o2) {
                    try {
                        Thread.sleep(500);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    synchronized (o1) {
                        log.info("0");
                    }
                }
            }
        }
    
        public static void main(String[] args) {
            DeadLock td1 = new DeadLock();
            DeadLock td2 = new DeadLock();
            td1.flag = 1;
            td2.flag = 0;
            //td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。
            //td2的run()可能在td1的run()之前运行
            new Thread(td1).start();
            new Thread(td2).start();
        }
    }

    上述代码出现死锁原因:

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

    确认死锁

    在真实的环境中,我们发现程序无法执行,并且CPU占用为0,这样就有理由怀疑产生了死锁,但是光怀疑是不行的,我们需要一个实际的验证方法。接下来我们使用jdk提供的工具来检测是否真正发生了死锁。
    运行上述的代码,并在windows系统中使用cmd进入控制台,输入以下命令:

    jps

    可见控制台输出:我们上边运行的类的类名以及对应的进程ID
    这里写图片描述
    接下来使用命令获取进程对应线程的堆栈信息:

    jstack 9284 

    分析堆栈信息(提取有用的部分)
    这里写图片描述
    两个线程都进行了加锁操作(如上图)
    这里写图片描述
    系统发现了一个Java-level的线程死锁。ok,确认无疑是发生了死锁现象。

    避免死锁

    展开全文
  • 高并发编程-线程通信_使用wait和notify进行线程间的通信 - 遗留问题 我们看到了 应用卡住了 。。。。 怀疑是不是死锁呢? (其实没有) jstack或者可视化工具检测是否死锁(没有) C:\Users\Mr.Yang>E: E:\>...


    在这里插入图片描述

    概述

    高并发编程-线程通信_使用wait和notify进行线程间的通信 - 遗留问题

    在这里插入图片描述

    我们看到了 应用卡住了 。。。。 怀疑是不是死锁呢? (其实没有)


    jstack或者可视化工具检测是否死锁(没有)

    C:\Users\Mr.Yang>E:
    
    E:\>cd E:\Program Files\Java\jdk1.8.0_161\bin
    
    E:\Program Files\Java\jdk1.8.0_161\bin>jps
    1504 MultiProduceConsumerDemo
    
    E:\Program Files\Java\jdk1.8.0_161\bin>jstack 1504
    2019-10-01 00:44:23
    Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.161-b12 mixed mode):
    
    "DestroyJavaVM" #15 prio=5 os_prio=0 tid=0x0000000002983800 nid=0x3348 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Thread-3" #14 prio=5 os_prio=0 tid=0x0000000019fa5000 nid=0x3af0 in Object.wait() [0x000000001abff000]
       java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x000000078b682768> (a java.lang.Object)
            at java.lang.Object.wait(Object.java:502)
            at com.artisan.test.MultiProduceConsumerDemo.consume(MultiProduceConsumerDemo.java:42)
            - locked <0x000000078b682768> (a java.lang.Object)
            at com.artisan.test.MultiProduceConsumerDemo$2.run(MultiProduceConsumerDemo.java:63)
    
    "Thread-2" #13 prio=5 os_prio=0 tid=0x0000000019fa4000 nid=0x9e4 in Object.wait() [0x000000001aaff000]
       java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x000000078b682768> (a java.lang.Object)
            at java.lang.Object.wait(Object.java:502)
            at com.artisan.test.MultiProduceConsumerDemo.consume(MultiProduceConsumerDemo.java:42)
            - locked <0x000000078b682768> (a java.lang.Object)
            at com.artisan.test.MultiProduceConsumerDemo$2.run(MultiProduceConsumerDemo.java:63)
    
    "Thread-1" #12 prio=5 os_prio=0 tid=0x0000000019fa0000 nid=0x37cc in Object.wait() [0x000000001a9ff000]
       java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x000000078b682768> (a java.lang.Object)
            at java.lang.Object.wait(Object.java:502)
            at com.artisan.test.MultiProduceConsumerDemo.produce(MultiProduceConsumerDemo.java:20)
            - locked <0x000000078b682768> (a java.lang.Object)
            at com.artisan.test.MultiProduceConsumerDemo$1.run(MultiProduceConsumerDemo.java:55)
    
    "Thread-0" #11 prio=5 os_prio=0 tid=0x0000000019f9f800 nid=0x1bd4 in Object.wait() [0x000000001a8fe000]
       java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x000000078b682768> (a java.lang.Object)
            at java.lang.Object.wait(Object.java:502)
            at com.artisan.test.MultiProduceConsumerDemo.produce(MultiProduceConsumerDemo.java:20)
            - locked <0x000000078b682768> (a java.lang.Object)
            at com.artisan.test.MultiProduceConsumerDemo$1.run(MultiProduceConsumerDemo.java:55)
    
    "Service Thread" #10 daemon prio=9 os_prio=0 tid=0x0000000019d2f800 nid=0x3ba4 runnable [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C1 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x0000000019ca3800 nid=0x37e4 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x0000000019c54800 nid=0x2db0 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x0000000019c50800 nid=0x37bc waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x0000000019c2e000 nid=0x2ed4 runnable [0x000000001a2fe000]
       java.lang.Thread.State: RUNNABLE
            at java.net.SocketInputStream.socketRead0(Native Method)
            at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
            at java.net.SocketInputStream.read(SocketInputStream.java:171)
            at java.net.SocketInputStream.read(SocketInputStream.java:141)
            at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
            at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
            at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
            - locked <0x000000078b5dad50> (a java.io.InputStreamReader)
            at java.io.InputStreamReader.read(InputStreamReader.java:184)
            at java.io.BufferedReader.fill(BufferedReader.java:161)
            at java.io.BufferedReader.readLine(BufferedReader.java:324)
            - locked <0x000000078b5dad50> (a java.io.InputStreamReader)
            at java.io.BufferedReader.readLine(BufferedReader.java:389)
            at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)
    
    "Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000000018860000 nid=0x34f8 waiting on condition [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000000019c08800 nid=0x1514 runnable [0x0000000000000000]
       java.lang.Thread.State: RUNNABLE
    
    "Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001883a000 nid=0x22c in Object.wait() [0x0000000019b9e000]
       java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x000000078b408ec0> (a java.lang.ref.ReferenceQueue$Lock)
            at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
            - locked <0x000000078b408ec0> (a java.lang.ref.ReferenceQueue$Lock)
            at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
            at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
    
    "Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000002a73800 nid=0x29bc in Object.wait() [0x0000000019a9f000]
       java.lang.Thread.State: WAITING (on object monitor)
            at java.lang.Object.wait(Native Method)
            - waiting on <0x000000078b406b68> (a java.lang.ref.Reference$Lock)
            at java.lang.Object.wait(Object.java:502)
            at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
            - locked <0x000000078b406b68> (a java.lang.ref.Reference$Lock)
            at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
    
    "VM Thread" os_prio=2 tid=0x0000000018818000 nid=0x381c runnable
    
    "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002998000 nid=0x3024 runnable
    
    "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000000000299a000 nid=0x229c runnable
    
    "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000000000299b800 nid=0x2bbc runnable
    
    "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000000000299d000 nid=0x255c runnable
    
    "VM Periodic Task Thread" os_prio=2 tid=0x0000000019d67800 nid=0x1c5c waiting on condition
    
    JNI global references: 334
    
    
    E:\Program Files\Java\jdk1.8.0_161\bin>
    
    

    可以看到 并没有死锁的发生

    或者 使用 jvisualvm 、 jmc 工具来看下都行

    (jmc截图)在这里插入图片描述

    并且可以看到4个线程 均是 WAITING 状态

    (jmc截图)在这里插入图片描述


    原因分析

    在这里插入图片描述

    为了方便观察,我们改造下,给线程起个名

    package com.artisan.test;
    
    import java.util.stream.Stream;
    
    public class MultiProduceConsumerDemo {
    
        // 对象监视器-锁
        private final Object LOCK = new Object();
    
        // 是否生产出数据的标识
        private boolean isProduced = false;
    
        // volatile 确保可见性, 假设 i 就是生产者生产的数据
        private volatile int i = 0 ;
    
        public  void produce(){
            synchronized (LOCK){
                System.out.println(Thread.currentThread() + " GOT LOCK  " + isProduced);
                String msg = isProduced ? "已生产货物" : "没有货物可搬运";
    
                if (isProduced){
                    try {
                        System.out.println(Thread.currentThread() + " wait becauseof  " + msg );
                        LOCK.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    i++;
                    System.out.println(Thread.currentThread() + " Produce:" + i);
                    LOCK.notify();
                    isProduced = true;
                }
            }
        }
    
        public void consume(){
            // 加锁
            synchronized (LOCK){
                System.out.println(Thread.currentThread() + " GOT LOCK " + isProduced);
                String msg = isProduced ? "已生产货物" : "没有货物可搬运";
    
                if (isProduced){
                    System.out.println(Thread.currentThread() + " Consume:" + i);
                    LOCK.notify();
                    isProduced = false;
                }else{
                    try {
                        System.out.println(Thread.currentThread() + " wait becauseof  " + msg);
                        LOCK.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public static void main(String[] args) {
            MultiProduceConsumerDemo produceConsumerDemo = new MultiProduceConsumerDemo();
            Stream.of("P1","P2").forEach(n-> new Thread(n){
                @Override
                public void run() {
                    while(true) produceConsumerDemo.produce();
                }
            }.start());
    
    
            Stream.of("C1","C2").forEach(n->new Thread(n){
                @Override
                public void run() {
                    while(true) produceConsumerDemo.consume();
                }
            }.start());
    
            
        }
    }
    
    
    
    

    运行下 ,可以看到程序并没有持续运行,而是假死了…

    (每次运行的结果都有可能不一样,这里我们以这次的运行结果来分析下)
    在这里插入图片描述

    逐步分析下:

    生产者的代码

    在这里插入图片描述

    消费者代码

    在这里插入图片描述

    1. 线程P1锁,没有货物生产,isProduce=false
    2. 线程P1,生产货物 ,紧接着 LOCK.notify(); isProduced = true;其实第一步的LOCK.notify() 是没有什么作用的,因为没有任何线程wait. 执行完以后释放锁。 对应日志 在这里插入图片描述
    3. 紧接着,P1又抢到了锁,但是生产后没有被消费,所以直接进入LOCK.wati. 执行完以后释放锁。P1-----WAITING . 对应日志 在这里插入图片描述
    4. P2同上一步P1的操作 ,P2-----WAITING 对应日志在这里插入图片描述
    5. C1 抢到资源 ,isProduce已经生产,所以C1线程直接消费,消费完成以后要通知生产者继续生产,即唤醒消费者 (LOCK.notify();),将isProduce标志位置为false . 执行完以后,释放锁。C1-----WAITING日志如下在这里插入图片描述
    6. 紧接着C1又抢到了锁,因没有生产,所以进入LOCK.wait() ,释放锁。 对应日志在这里插入图片描述
    7. 依次类推… 直到最后C2 唤醒了C1 ,此时C1看到isProduce=false, 则C1进入了wait ,这个时候4个线程都是watiing的状态了,就出现了4个线程均是wait状态,都不执行了,出现了假死 (因为notify方法,唤醒一个线程,具体是哪个线程是不确定的。)

    那如何解决呢? 下篇博文我们一起来探讨下

    展开全文
  • 多线程中,当两个及以上线程并发访问同一个资源时,由于线程调度具有不确定性,尽管有控制线程的方法,但也会导致线程冲突,导致线程不安全,所以就有了线程同步机制解决该问题。 实现线程同步机制的方式 使用...

    线程同步的作用

    在多线程中,当两个及以上线程并发访问同个资源时,尽管有控制线程的方法,但由于线程调度具有不确定性,所以极容易导致错误,这时就需要线程同步机制来解决此问题。


    实现线程同步——同步监视器

    Java中,任何对象都能作为同步监视器。同步监视器是为了确保一个线程在对对象操作时,别的线程无权访问,即任何时刻只能有一个线程对同步监视器锁定。所以一般把并发访问的共享资源作为同步监视器。
    在Java中,使用关键字synchronized或者Lock类来对同步监视器锁定。

    synchronized的用法

    • 同步代码块:synchronized(obj){},传入的obj对象就是同步监视器,在执行同步代码块中的代码之前,必须先获得对同步监视器的锁定。
    • synchronized修饰方法:同步方法的监视器是this,所以调用该方法的对象就是同步监视器,则无需显式指定。

    Lock的用法

    • 有ReentrantLock(可重入锁)、ReentrantReadWriteLock(可重入读写锁)、以及Java8新增的StampedLock类。
    • Lock对象相当于同步监视器对象,并发访问时,每次只能允许一个线程对Lock对象加锁,执行完之后必须手动释放锁,释放锁方法一定要放在finally语句中。
    • 另外ReentrantReadWriteLock(提供了三种读写操作模式,但不常用。

    线程同步实例

    先给上一段多线程实现的取钱代码,没有采用线程同步,看看会出现什么情况。

    public class Account {
    	private int balance;//余额
    	public Account(int balance) {
    		this.balance = balance;
    		System.out.println("账户当前余额:" + balance);
    	}	
    	//取钱方法
    	public void quqian(int money) {
    		if(money <= balance) {
    			System.out.println(Thread.currentThread().getName() + ",取出" + money);
    			//睡眠线程2s,用于测试另一个线程
    			try {
    				Thread.sleep(2000);
    			} catch (InterruptedException e) {e.printStackTrace();}							
    			//修改余额的代码
    			this.balance -= money;
    			System.out.println(Thread.currentThread().getName()+",余额:" + this.balance);
    		}else {
    			System.out.println(Thread.currentThread().getName() + "取钱失败!");
    		}		
    	}
    }	
    //执行存钱操作的Runnable线程
    class QqPerson implements Runnable{
    	private int money;
    	private Account ac;
    	public QqPerson(Account ac,int money) {
    		this.ac = ac;
    		this.money = money;
    	}
    	@Override
    	public void run() {
    		ac.quqian(money);		
    	}
    }
    
    public class Test {
    	public static void main(String[] args) {
    		Account ac = new Account(1000);
    		//传入Account对象、取钱金额
    		QqPerson q1 = new QqPerson(ac,600);
    		QqPerson q2 = new QqPerson(ac,600);
    		//线程类传入Runnable对象
    		Thread t1 = new Thread(q1,"取钱1号");
    		Thread t2 = new Thread(q2,"取钱2号");
    		//启动线程
    		t1.start();
    		t2.start();
    	}
    }
    • 实例解析:在执行修改余额代码之前,调用sleep(2000)强制让线程睡眠2s,这样能更好地体现多个线程访问共享资源时,由于线程调度的不确定性导致的线程安全问题。事实上,如果不加sleep(),就会出现一会正确一会错误的情况。遇到这种问题,就需要使用线程同步来解决。

    使用同步代码块实例

    //执行存钱操作的线程
    class QqPerson implements Runnable{
    	private int money;
    	private Account ac;
    	public QqPerson(Account ac,int money) {
    		this.ac = ac;
    		this.money = money;
    	}
    	@Override
    	public void run() {
    		//加入同步代码块
    		synchronized(ac){
    			ac.quqian(money);
    		}
    	}
    }
    • 实例解析:其余的代码都是相同的,只是把并发访问共享资源的执行代码放入同步代码块中,在代码中,依然使用sleep()来睡眠线程,但是会发现别的线程无法执行,这是因为同步锁还未被释放,执行完后释放锁,别的线程才能获取同步锁。

    使用同步方法实例

    	//取钱方法
    	public synchronized void quqian(int money) {
    		if(money <= balance) {
    			System.out.println(Thread.currentThread().getName() + ",取出" + money);
    			//睡眠线程1s,用于测试另一个线程
    			try {
    				Thread.sleep(1000);
    			} catch (InterruptedException e) {e.printStackTrace();}							
    			//修改余额的代码
    			this.balance -= money;
    			System.out.println(Thread.currentThread().getName()+",余额:" + this.balance);
    		}else {
    			System.out.println(Thread.currentThread().getName() + "取钱失败!");
    		}		
    	}
    • 实例解析:因为并发访问共享资源的执行代码是由一个方法来完成,所以可以用synchronized修饰该方法。加锁原理和同步代码块一样,只是无需显式加入同步锁对象。
    • 加锁流程:第一个线程执行取钱方法,先获得同步锁对象,然后执行方法内部代码,执行到sleep()会睡眠当前线程,但因为睡眠线程获得了锁对象,所以别的线程此时无权访问,睡眠时间到了之后,又开始继续执行,直到代码执行结束,然后释放锁。下一个线程开始执行,又继续前面的步骤。

    使用Lock加锁实例

    public class Account {
    	private int balance;//余额
    	ReentrantLock lock = new ReentrantLock();
    	StampedLock slock = new StampedLock();
    	
    	public Account(int balance) {
    		this.balance = balance;
    		System.out.println("账户当前余额:" + balance);
    	}
    	//取钱方法
    	public  void quqian(int money) {
    		try {
    			lock.lock();//加锁
    			if(money <= balance) {
    				System.out.println(Thread.currentThread().getName() + ",取出" + money);												
    				//修改余额的代码
    				this.balance -= money;
    				System.out.println(Thread.currentThread().getName()+",余额:" + this.balance);
    			}else {
    				System.out.println(Thread.currentThread().getName() + "取钱失败!");
    			}
    		}finally {
    			lock.unlock();//手动释放锁
    		}
    	}
    }	
    • 实例解析:上述代码使用了ReentrantLock(可重入锁)来实现线程同步。Lock对象代表了并发访问的同步监视器的领域,以Lock对象作为显式的同步锁对象。使用Lock加锁,一定要手动释放锁。

    线程同步注意点:

    • 线程持有同步锁对象期间,使用sleep()不会释放锁。但如果使用wait()会导致释放同步锁。
    • sleep()的调用者是当前线程,而wait()的调用者是对象,所以wait()会导致锁被释放。
    • 同步代码块需要显式传入同步锁对象,同步方法无需同步锁对象,因为隐式传入。
    • Lock对象相当于隐式传入的同步锁对象,所以使用时要保证该同步锁对象是访问共享资源的调用对象。
    • Lock可以嵌套加锁,即在Lock加锁的代码中能执行另一段加锁的代码。

    线程死锁

    线程同步时,两个及以上线程相互持有同步锁对象,这就导致无法对同步监视器对象进行锁定,造成所有线程都进入阻塞状态,这就是死锁。出现死锁不会出现异常,不会有任何提示。如果通过eclipse的控制台来查看,看起来程序是结束了,但红正方形的出现说明了程序是运行的,只是一直在等待锁的释放。

    关键字volatile——用于修饰变量

    volatile:易变、不稳定的。

    volatile使用场景:

    如果只是读写一两个实例变量就使用线程同步,则开销会很大,volatile关键字能为实例变量的同步访问提供了一种免锁机制。用于在多线程中同步公共变量,保证用户在操作一个变量,但不保证多线程的原子性。

    原子性解释:

    假设对共享变量除了赋值之外并不完成其他操作,那么可以将这些共享变量声明为volatile。

    变量使用同步的条件:

    如果向一个变量写入值,而这个变量接下来可能会被另一个线程读取,或者从一个变量读值,而这个变量可能是之前被另一个线程写入的,此时必须使用同步。




    展开全文
  • 并发编程】死锁

    2018-07-02 22:42:00
    原文:慕课网实战·高并发探索(十五):线程死锁 什么是死锁? 通俗的说,死锁就是两个或者个线程,相互占用对方需要的资源,而都不进行释放,导致彼此之间都相互等待对方释放资源,产生了无限制等待的现象。死锁...

    原文:慕课网实战·高并发探索(十五):线程死锁

    什么是死锁?

    通俗的说,死锁就是两个或者多个线程,相互占用对方需要的资源,而都不进行释放,导致彼此之间都相互等待对方释放资源,产生了无限制等待的现象。死锁一旦发生,如果没有外力介入,这种等待将永远存在,从而对程序产生严重影响。

    用来描述死锁的问题最有名的场景就是“哲学家就餐问题”。哲学家就餐问题可以这样表述:假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事之一:吃饭或者思考。吃东西的时候他们就停止思考,思考的时候也停止吃东西。餐桌中间有一大碗意大利面,每两个哲学家之间有一只餐叉。因为只用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东西。他们只能使用自己左右手边的那两只餐。哲学家从来不交谈,这就跟危险,可能产生死锁,每个哲学家都拿着左手的餐叉永远等右边的餐叉(或者相反)….

    死锁产生的4个必要条件

    互斥条件:进程对锁分配的资源进行排他性使用
    请求和保持条件:线程已经保持了一个资源,但是又提出了其他请求,而该资源已被其他线程占用
    不剥夺条件:在使用时不能被剥夺,只能自己用完释放
    环路等待条件:资源调用是一个环形的链

    死锁示例

    @Slf4j
    public class DeadLock implements Runnable {
        public int flag = 1;
        //静态对象是类的所有对象共享的
        private static Object o1 = new Object(), o2 = new Object();
    
        @Override
        public void run() {
            log.info("flag:{}", flag);
            if (flag == 1) {
                synchronized (o1) {
                    try {
                        Thread.sleep(500);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    synchronized (o2) {
                        log.info("1");
                    }
                }
            }
            if (flag == 0) {
                synchronized (o2) {
                    try {
                        Thread.sleep(500);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    synchronized (o1) {
                        log.info("0");
                    }
                }
            }
        }
    
        public static void main(String[] args) {
            DeadLock td1 = new DeadLock();
            DeadLock td2 = new DeadLock();
            td1.flag = 1;
            td2.flag = 0;
            //td1,td2都处于可执行状态,但JVM线程调度先执行哪个线程是不确定的。
            //td2的run()可能在td1的run()之前运行
            new Thread(td1).start();
            new Thread(td2).start();
        }
    }

    上述代码出现死锁原因:

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

    确认死锁

    在真实的环境中,我们发现程序无法执行,并且CPU占用为0,这样就有理由怀疑产生了死锁,但是光怀疑是不行的,我们需要一个实际的验证方法。接下来我们使用jdk提供的工具来检测是否真正发生了死锁。
    运行上述的代码,并在windows系统中使用cmd进入控制台,输入以下命令:

    jps

    可见控制台输出:我们上边运行的类的类名以及对应的进程ID
    1014100-20180702224133613-823905891.png

    接下来使用命令获取进程对应线程的堆栈信息:

    jstack 9284 

    分析堆栈信息(提取有用的部分)
    1014100-20180702224141202-645118535.png

    两个线程都进行了加锁操作(如上图)
    1014100-20180702224147496-1341475500.png

    系统发现了一个Java-level的线程死锁。确认无疑是发生了死锁现象。

    避免死锁

    转载于:https://www.cnblogs.com/z00377750/p/9256236.html

    展开全文
  • 正交——消除无关事务之间的影响,力求内聚低耦合。...为了解决两个线程都在等待对方释放互斥量导致死锁问题,C++11提供了若干机制: std::lock()函数 std::unique_lock类 锁住所有互斥量 ...
  • 1. 由于GIL的影响,在python中使用多线程,实际上是无法将多线程同时运行在多个CPU上,但是对于io操作来说,多线程并不影响性能,反而在io...如果自己使用线程同步机制,如果实现不好,非常容易导致线程死锁,出现...
  • 饥饿是指某一个或者线程因为种种原因无法获得所要的资源,导致一直无法执行。比如它的优先级可能太低,而优先级的线程不断抢占它需要的资源,导致低优先级线程无法工作 package juc; import java.util....
  • 多线程的情况下,各个代码的执行顺序是不能确定的,所以为了保证并发安全,我们可以使用互斥锁。而 CAS 的特点是避免使用互斥锁,当多个线程同时使用 CAS 更新同一个变量时,只有其中一个线程能够操作成功,而其他...
  • 目前实现的思路: <p>1.... 每次返回的数据大概是1500-2000条左右 ...但是现在接口请求会有大量的重复数据,导致数据库每天数据量很,如果设置唯一约束,批量入库的时候数据库会发生死锁,有没有好的解决思路。</p>
  • 由于消费者和生产者之间的速度差异,队列总是接近满或者空的状态,这种状态会导致高水平的写入争用。 毕竟锁的技术会导致性能变差,而且还有可能会造成死锁。 什么是Disruptor? Disruptor的流行起源于Martin...
  • 悲观锁的问题:总是需要竞争锁,进而导致发生线程切换,挂起其他线程;所以性能不。 乐观锁: 设计上总是乐观的认为数据修改大部分场景都是没有线程并发修改,只有少量情况下才存在。线程安全上采取版本号来控制—
  • 5.1 并发工具类介绍Java对底层线程操作的支持使得你可以创建多线程应用程序。 这些应用程序比对应的单线程程序提供了更好的性能和响应能力。但依然存在问题低级的并发原语,比如synchronized和wait、notify等很难被...
  • Redis为什么是单线程还支持高并发

    千次阅读 2019-08-03 10:21:02
    线程模式省却了CPU上下文切换带来的开销问题,也不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁导致的性能消耗。 单线程为什么快 完全基于内存的操作。 redis特有的数据结构,对存储数据...
  • 死锁:之前在接触多线程的时候,接触过死锁的情况。死锁是线程中最糟糕的情况,如下面的图中的四辆车子一样,如果没有一辆车主动出来。那么这种状态永远会这样维持下去   饥饿(Starvation):饥饿是指某一...
  • 多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度问题,引起性能问题 如果一个优先级的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题 (1)不安全的买票问题 //不安全的买票...
  • │ 高并发编程第一阶段23讲、多线程死锁分析,案例介绍.mp4 │ 高并发编程第一阶段24讲、线程间通信快速入门,使用wait和notify进行线程间的数据通信.mp4 │ 高并发编程第一阶段25讲、多Produce多Consume之间的...
  • │ 高并发编程第一阶段23讲、多线程死锁分析,案例介绍.mp4 │ 高并发编程第一阶段24讲、线程间通信快速入门,使用wait和notify进行线程间的数据通信.mp4 │ 高并发编程第一阶段25讲、多Produce多Consume之间的...
  • 多线程初学

    2017-08-29 23:35:14
    改文章的图片来自java高并发程序设计这本书并发和并行 并发: 主要是指个任务交替执行。而且这个情况可能出现串行的 并行:一般是个任务同时执行。 死锁、饥饿、活锁的概念 死锁一般是指几个线程占用所有 的资源...
  • java多线程入门1

    2019-03-14 22:28:49
    java多线程入门1 并发和并行 并发: 主要是指多个任务交替执行。而且这个情况可能出现串行的 并行:一般是多个任务同时执行。 该图来自java高并发程序设计 死锁、饥饿、活锁的概念 死锁一般是指几个线程...
  • 实际场景就是火车站买票问题:剩余100张火车票,重庆火车站和杭州火车站都在售卖,两个窗口同时卖的时候,在不同步的情况下,就可能导致线程安全问题,导致多卖代码案例:packagecom.thread;public class BuyTic...
  • java5之前,我们使用诸如synchronized,wait(),notify()方法对线程的操作属于对底层线程的操作,这样会出现很的问题:低级的并发原语,比如synchronized,wait(),notify()经常难以正确使用。误用会导致竞态条件,...
  • 死锁详解及死锁的处理策略

    千次阅读 2020-02-02 22:56:42
    多线程并发中两个或多个线程互相持有对方所需要的资源,不主动释放,在没有外力干预的情况下,所有人都无法继续前进,导致程序陷入无尽的阻塞,就是死锁 比如下图,线程A 线程B都尝试获取对方持有的锁,但是又不释放自己...
  • Node特性:高并发 ...无需像多线程那样去关注线程之间的状态同步问题 没有线程切换所带来的开销 没有死锁存在 当然单线程也有许多坏处: 无法充分利用多核CPU 大量计算占用CPU会导致应用阻塞(即不适用
  • java多线程基础与概念

    2019-05-07 09:10:42
    1、多线程的基础与概念 (1)并发和并行 并发: 主要是指多个任务交替执行。而且这个情况可能出现串行的 并行:一般是多个任务同时执行 (2)死锁、饥饿、活锁的概念 死锁,是指几个线程占用所有的资源,不...
  • 高并发环境下, 激烈的锁竞争会导致程序性能下降, 所以非常有必要讨论一下锁的性能问题以及相关的注意事项,如: 避免死锁、减小锁力度、锁分离等。 需要明确一点, 多核场景下, 使用多线程虽然可以提高系统...
  • 死锁 信息量

    2019-10-06 05:27:19
    出现死锁的问题的两种问题的情况1,对同一把锁调用了次 acquire导致死锁问题(最low的死锁问题),你应该避免这种写法2,你有把锁,一个线程抢一把锁,要完成任务必须同时抢到所有的锁,这将导致死锁 ...
  • 多线程和异步事件处理是设计高并发和高性能服务器程序的主要技术,但很少有应用把两种技术组合在一起使用,原因是二者的组合会增加系统的复杂度,并使代码难以理解。多线程程序可以充分利用现代处理器多核的处理能力...
  • 并不是总能提升程序的运行速度,也会造成内存泄漏,上写文切换,线程死锁等 2、并发编程的三要素 原子性:一个或者个操作,要么全部成功要么全部失败 可见性:一个线程对共享变量的修改,另一个线程能够立刻...
  • 高并发的环境下,激烈的锁竞争会导致程序的性能下降,因此我们有必要讨论一些有关锁的性能问题,以及一些注意事项,比如避免死锁、减小锁粒度、锁分离等。 对于单任务或者单线程的应用而言,其主要资源消耗都花在...

空空如也

空空如也

1 2 3 4 5
收藏数 83
精华内容 33
关键字:

高并发导致多线程死锁