zoukankan      html  css  js  c++  java
  • 线程安全以及死锁问题

    死锁

    死锁是指多个线程运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。

    产生死锁的四个必要条

    1. 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
    2. 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
    3. 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
    4. 环路等待条件:在发生死锁时,必然存在一个进程--资源的环形链。

    编写产生死锁的代码

    复制代码
    public class TestDeadLock {
        private static Object obj1 = new Object();
        private static Object obj2 = new Object();
    
        public static void main(String[] args) {
            new Thread(new Thread1()).start();
            new Thread(new Thread2()).start();
        }
    
        private static class Thread1 implements Runnable {
            @Override
            public void run() {
                synchronized (obj1) {
                    System.out.println("Thread1 拿到了 obj1 的锁!");
                    try {
                    // 停顿2秒的意义在于,让Thread2线程拿到obj2的锁
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj2) {
                        System.out.println("Thread1 拿到了 obj2 的锁!");
                    }
                }
            }
        }
    
        private static class Thread2 implements Runnable {
            @Override
            public void run() {
                synchronized (obj2) {
                    System.out.println("Thread2 拿到了 obj2 的锁!");
                    try {
                    // 停顿2秒的意义在于,让Thread1线程拿到obj1的锁
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj1) {
                        System.out.println("Thread2 拿到了 obj1 的锁!");
                    }
                }
            }
        }
    
    }
    复制代码

    使用jstack加上进程号进行查看

    预防死锁:

    1. 资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
    2. 只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请保持条件)
    3. 可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
    4. 资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)

    线程安全

    模拟线程不安全代码

    复制代码
    public class Test {
        private static Integer count=0;
        public  static void add(){
            count++;
        }
       static CountDownLatch downLatch=new CountDownLatch(100);
        public static void main(String[] args) throws InterruptedException {
    
            for (int i = 0; i <100 ; i++) {
    
                new Thread(()->{
                    for (int j = 0; j <10 ; j++) {
                        add();
                    }
                    downLatch.countDown();
                }).start();
    
            }
            downLatch.await();
            System.out.println(count);
    
        }
    }
    复制代码

    解决方案

    1:添加synchronized

    复制代码
    public class Test {
        private static Integer count=0;
        public synchronized  static void add(){
            count++;
        }
       static CountDownLatch downLatch=new CountDownLatch(100);
        public static void main(String[] args) throws InterruptedException {
    
            for (int i = 0; i <100 ; i++) {
    
                new Thread(()->{
                    for (int j = 0; j <10 ; j++) {
                        add();
                    }
                    downLatch.countDown();
                }).start();
    
            }
            downLatch.await();
            System.out.println(count);
    
        }
    }
    复制代码

    2.加lock锁

    复制代码
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Test {
        private static Integer count = 0;
    
        public  static void add() {
            try {
                reentrantLock.lock();
                count++;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                reentrantLock.unlock();
            }
    
    
        }
    
        static ReentrantLock reentrantLock = new ReentrantLock();
        static CountDownLatch downLatch = new CountDownLatch(100);
    
        public static void main(String[] args) throws InterruptedException {
    
            for (int i = 0; i < 100; i++) {
    
                new Thread(() -> {
                    for (int j = 0; j < 10; j++) {
                        add();
                    }
                    downLatch.countDown();
                }).start();
    
            }
            downLatch.await();
            System.out.println(count);
    
        }
    }
    复制代码

    3.上面的这种情况还可以使用原子类

    复制代码
    package test;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class Test {
        private static AtomicInteger count = new AtomicInteger(0);
    
        public  static void add() {
            count.incrementAndGet();
        }
    
        static CountDownLatch downLatch = new CountDownLatch(100);
    
        public static void main(String[] args) throws InterruptedException {
    
            for (int i = 0; i < 100; i++) {
    
       
  • 相关阅读:
    python中普通函数调用协程
    python骚操作之内建方法的使用
    Python常量类
    python实用技巧之任务切分
    容易被忽视的python装饰器的特性
    C语言学习笔记
    python mongo存在插入不存在更新,同时指定如果不存在才插入的字段
    python使用ThreadPoolExecutor每秒并发5个
    mac使用crawlab
    线性表的顺序存储结构
  • 原文地址:https://www.cnblogs.com/wangdayexinyue/p/12521341.html
Copyright © 2011-2022 走看看