美文网首页
java基础回忆录 - 线程安全

java基础回忆录 - 线程安全

作者: watayouxiang | 来源:发表于2017-09-23 10:04 被阅读8次

    1. 线程安全

    说明时候会出现线程安全问题?

    多线程执行有多条语句操作的共享数据

    具体步骤:

    1. 明确哪些代码是多线程运行代码
    2. 明确共享数据
    3. 明确多线程运行代码中哪些语句是操作共享数据的

    出现问题怎么解决?

    同步代码块

    synchronized(锁对象){
        需要被同步的代码
    }
    
    对象如同锁,持有锁的线程才可以执行同步代码
    没有锁的线程即使有cpu执行权,也进不去,因为没有锁
    
    弊端:多个线程需要判断锁,较为消耗资源
    

    同步函数

    class Demo{
        public synchronized void add(){
            需要被同步的代码
        }
    }
    
    同步函数的锁就是函数所属的对象引用 Demo.this
    
    class Demo{
        public static synchronized void add(){
            需要被同步的代码
        }
    }
    
    同步静态函数的锁不再是 Demo.this
    静态进内存时,内存中没有本类对象,但是一定有该类的字节码文件对象
    所以静态同步函数的锁是 Demo.class
    

    2. 单例中的线程安全问题

    // 饿汉式
    class Single{
        // final保证对象的唯一性
        private static final Single s = new Single();
        private Single(){}
        public static Single getInstance(){
            return s;
        }
    }
    
    // 懒汉式(延时加载)
    class Single{
        private static Single s = null;
        private Single(){}
        public static Single getInstance(){
            if(s == null){          // 操作了共享数据
                s = new Single();   // 操作了共享数据
            }
            return s;
        }
    }
    
    1. getInstance()方法中的代码是多线程运行的代码
    2. 共享数据是 s(Single对象)
    3. 有2条语句操作了共享数据
    4. 所以多线程时会出现线程安全问题
    
    ---------------------------
    
    class Single{
        private static Single s = null;
        private Single(){}
        public static synchronized Single getInstance(){
            if(s == null){          // 操作了共享数据
                s = new Single();   // 操作了共享数据
            }
            return s;
        }
    }
    
    通过修改成同步函数的方式来解决多线层下的安全问题
    但是 return s; 语句并不需要同步,所以会降低程序运行效率
    
    ---------------------------
    
    class Single{
        private static Single s = null;
        private Single(){}
        public static Single getInstance(){
            if(s == null){
                synchronized(Single.class){
                    if(s == null){          // 操作了共享数据
                        s = new Single();   // 操作了共享数据
                    }
                }   
            }
            return s;
        }
    }
    
    通过同步代码块的形式包裹需要同步的2行代码
    再双重判断的形式提高程序运行的效率
    
    

    3. 死锁是怎么产生的?

    同步中嵌套同步

    4. 等待唤醒机制

    class Demo implements Runnable{
        private Object o = new Object();
    
        public void add(){
            sychronized(o){
                o.wait();    // 让“持有r锁的线程”变成等待状态
                o.notify();  // 唤醒“持有r锁并处于等待状态”的线程
            }
            notifyAll();    // 唤醒所有”等待线程“
        }   
    }
    
    wait(), notify(), notifyAll() 都是Object对象的方法
    

    5. JDK1.5 - Lock锁机制

    Lock锁机制简介

    将同步synchronized替换成了显式的Lock机制
    将Object中的lock(), notify(), notifyAll() 替换成Condition对象
    Condition对象可以通过lock.newCondition(); 获取
    
    优点:可以指定不同锁对象上的线程 等待或者唤醒
    Condition condition_a = lock.newCondition();
    Condition condition_b = lock.newCondition();
    condition_a.await();   // 让condition_a上的线程等待
    condition_b.signal();  // 唤醒condition_b上的线程
    

    常用方法

    • await(): 让线程处于等待状态
    • signal(): 唤醒一个等待线程
    • signalAll(): 唤醒所有等待线程
    // 相当于 synchronized 
    // 创建 Lock 接口的实例
    private Lock lock = new ReentranLock();
    
    // 相当于Object(wait, notify, notifyAll)
    // 返回绑定到此 Lock 实例的新 Condition 实例
    private Condition condition_pro = lock.newCondition();
    private Condition condition_com = lock.newCondition();
    
    // 获取锁
    lock.lock();
    
    // 让 ”condition对象“ 上的线程变成等待状态
    condition_pro.await();
    condition_com.await();
    
    // 释放锁
    // 该语句必须执行,所以放在finally中处理
    lock.unlock();
    
    // 从线程池中唤醒 “condition对象” 上的一个线程
    condition_pro.signal();
    condition_com.signal();
    
    // 从线程池中唤醒 “condition对象” 上的所有线程
    condition_pro.signalAll();
    

    相关文章

      网友评论

          本文标题:java基础回忆录 - 线程安全

          本文链接:https://www.haomeiwen.com/subject/hkuqextx.html