zoukankan      html  css  js  c++  java
  • 高级java必会系列二:多线程经常使用的3个关键字:synchronized、ReentrantLock、volatile

    系列一讲解了多线程,本章讲解多线程开发中经常使用到的3个关键字synchronized、ReentrantLock、volatile。

    一、synchronized

      互斥锁,即操作互斥,并发线程过来,串行获得锁,串行执行代码。就像一个房间一把钥匙,一个人进去后,下一个人得等第一个人出来得到钥匙才能进入。如果代码写的不好(A),可能出现死锁!(A得到锁,B等待A释放锁,A不释放,B死锁)

    示例代码:

       //修饰静态方法:类级别互斥(只要是房子此方法就互斥)
        public synchronized static void foo1(){
            //do 
        }
        
        //修饰普通方法:对象级别互斥(只有同一间房子此方法互斥)
        public synchronized void foo2(){
            //do 
        }
        
        //修饰代码块
        public void foo3(){
            //类级别互斥
            synchronized(DennyTest.class) {
                //do 
            }
        }
        
        //修饰代码块
        public void foo4(){
            //对象级别互斥
            synchronized(this) {
                //do 
            }
        }

    需要注意的是,经常使用的是对象级别的互斥,那么特别需要注意是同一个对象的锁。新手经常犯错,都不是同一个对象,当然锁不住。

    二、ReentrantLock

    可重入锁,和同步锁功能类似,不过需要显示的创建、销毁。特点:

    1.ReentrantLock有tryLock方法,如果锁被其他线程持有,返回false,可避免形成死锁。

    2.创建时可自定义是否可抢占。

    3.ReentrantReadWriteLock,用于读多写少,且读不需要互斥的场景,大大提高性能。

    示例代码:

    1、尝试获取一次    
        ReentrantLock lock = new ReentrantLock();  
        if (lock.tryLock()) {  //得到执行,得不到不执行,就一次。  
            try {  
                //操作  
            } finally {  
                lock.unlock();  
            }  
        }
    2、同步执行,类似synchronized(也是使用最多的)
        ReentrantLock lock = new ReentrantLock(); //参数默认false,不公平锁:可抢占
        ReentrantLock lock = new ReentrantLock(true); //公平锁:严格按照请求锁的排队顺序获取锁
          
        lock.lock(); //如果被其它资源锁定,会在此等待锁释放,阻塞  
        try {  
            //操作  
        } finally {  
            lock.unlock();  
        }  
    3、尝试等待固定时间再次获取
        ReentrantLock lock = new ReentrantLock(true); //公平锁  
        try {  
            if (lock.tryLock(5, TimeUnit.SECONDS)) {      
                //如果已经被lock,尝试等待5s,看是否可以获得锁,如果5s后仍然无法获得锁则返回false  
                try {  
                    //操作  
                } finally {  
                    lock.unlock();  
                }  
            }  
        } catch (InterruptedException e) {  
            e.printStackTrace(); //当前线程被中断时(interrupt),会抛InterruptedException                   
        }
    4、可中断锁的同步执行
        ReentrantLock lock = new ReentrantLock(true); //公平锁  
        lock.lockInterruptibly();  
        try {  
            //操作  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        } finally {  
            lock.unlock();  
        }         
      

    三、volatile

    volatile,翻译过来是易变的。只保证同一变量在多线程中的可见性。

      1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;

      2)它会强制将对缓存的修改操作立即写入主存;

      3)如果是写操作,它会导致其他CPU中对应的缓存行无效。

    注意:虽然保证变量是主存数据,但是操作不是原子的,多线程读取到同一个值(是主存的值),同时进行判断或者操作,导致出错。

    总结:

    可见,synchronized和ReentrantLock是一个级别的,但是volatile只是一个轻量级的关键字。可用场景:

    1.状态标记

     1 volatile boolean inited = false;
     2 //线程1:
     3 context = loadContext();  
     4 inited = true;            
     5  
     6 //线程2:
     7 while(!inited ){
     8 sleep()
     9 }
    10 doSomethingwithconfig(context);

    2.double check :使用 volatile 关键字来保证多线程下的单例

        public class Singleton {  
            private volatile Singleton instance = null;  
            public Singleton getInstance() {  
                if (instance == null) {  
                    synchronized(this) {  
                        if (instance == null) {  
                            instance = new Singleton();  
                        }  
                    }  
                }  
                return instance;  
            }  
        }  

    参考资料:

    《深入理解Java虚拟机》

    《effective java》

     http://www.cnblogs.com/dolphin0520/p/3920373.html

  • 相关阅读:
    游戏与必胜策略
    中国剩余定理
    中国剩余定理
    欧几里得和扩展欧几里得
    欧几里得和扩展欧几里得
    51nod 1028 大数乘法 V2
    51nod 1028 大数乘法 V2
    51nod 1029 大数除法
    51nod 1029 大数除法
    51nod 1166 大数开平方
  • 原文地址:https://www.cnblogs.com/dennyzhangdd/p/6020566.html
Copyright © 2011-2022 走看看