zoukankan      html  css  js  c++  java
  • Java并发编程--3.Lock

    Lock接口

    它提供3个常用的锁

    lock() : 获不到锁就就一直阻塞
    
    trylock() :获不到锁就立刻放回 或者 定时的,轮询的获取锁 
    
    lockInterruptibly() : 获不到锁时阻塞,但可接受中断信号后退出阻塞状态

    ReentrantLock

    实现机制

    基于冲突的乐观并发策略:

    如果共享数据被争用,产生了冲突,那就再进行其他的补偿措施,比如说定时的获取锁,直到成功;不需要把线程挂起,也称为非阻塞的同步

    公平性

    公平: 多个线程在等待同一个锁时,必须按照申请锁的时间顺序排队等待
    非公平: 在锁释放时,任何一个等待锁的线程都有机会获得锁,ReentrantLock构造方法,默然是非公平的

    什么时候使用

    当你需要可定时的和可中断的锁操作,公平队列,或者非块结构的锁,否则请使用synchronized

    可中断的例子

    public class MyReentrantLock {
        private ReentrantLock lock = new ReentrantLock();  
          
        public void write() {  
            lock.lock();  
            try {  
                long startTime = System.currentTimeMillis();  
                System.out.println("开始往这个buff写入数据…");  
                for (;;)// 模拟要处理很长时间      
                {  
                    if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE) {  
                        break;  
                    }  
                }  
                System.out.println("终于写完了");  
            } finally {  
                lock.unlock();  
            }  
        }  
      
        public void read() throws InterruptedException {  
            lock.lockInterruptibly();// 注意这里,可以响应中断      
            try {  
                System.out.println("从这个buff读数据");  
            } finally {  
                lock.unlock();  
            }  
        }  
      
        public static void main(String args[]) {  
            MyReentrantLock buff = new MyReentrantLock();  
      
            final Writer2 writer = new Writer2(buff);  
            final Reader2 reader = new Reader2(buff);  
      
            writer.start();  
            reader.start();  
      
            new Thread(new Runnable() {  
      
                @Override  
                public void run() {  
                    long start = System.currentTimeMillis();  
                    for (;;) {  
                        if (System.currentTimeMillis() - start > 5000) {  
                            System.out.println("不等了,尝试中断");  
                            reader.interrupt();  //此处中断读操作  
                            break;  
                        }  
                    }  
                }  
            }).start();  
      
        }  
    }  
      
    class Reader2 extends Thread {  
      
        private MyReentrantLock buff;  
      
        public Reader2(MyReentrantLock buff) {  
            this.buff = buff;  
        }  
      
        @Override  
        public void run() {  
      
            try {  
                buff.read();//可以收到中断的异常,从而有效退出      
            } catch (InterruptedException e) {  
                System.out.println("我不读了");  
            }  
      
            System.out.println("读结束");  
      
        }  
    }  
      
    class Writer2 extends Thread {  
      
        private MyReentrantLock buff;  
      
        public Writer2(MyReentrantLock buff) {  
            this.buff = buff;  
        }  
      
        @Override  
        public void run() {  
            buff.write();  
        }  
    }

    控制台输出:

    开始往这个buff写入数据…
    不等了,尝试中断
    我不读了
    读结束

    ReentrantReadWriteLock读写锁

    特点

    互斥:它使得读写操作互斥,读读操作不互斥
    
    锁降级:写线程获取写入锁后可以获取读取锁,然后释放写入锁,这样就从写入锁变成了读取锁

    少写多读的例子

    public class MyReadWriteLock {
        public static void main(String[] args) {
            PricesInfo pricesInfo = new PricesInfo();
            Writer writer=new Writer(pricesInfo);
            Reader read =  new Reader(pricesInfo);
            
            //写线程
            Thread tw=new Thread(writer);
            tw.start();
            
            //多个读线程
            for (int i=0; i<5; i++){
                Thread tr=new Thread(read);
                tr.start();
            } 
        }
    }
    
    //读线程
    class Reader implements Runnable{
        private PricesInfo pricesInfo;
        
        public Reader(PricesInfo pricesInfo){
            this.pricesInfo = pricesInfo;
        }
    
        @Override
        public void run() {
            pricesInfo.getPrice();
        }
    }
    
    //写线程
    class Writer implements Runnable{
        private PricesInfo pricesInfo;
    
        public Writer(PricesInfo pricesInfo){
            this.pricesInfo = pricesInfo;
        }
        
        @Override
        public void run() {
            pricesInfo.setPrice(Math.random()*10);
            
        }
    }
    
    //数据实体
    class PricesInfo {
        private double price;
        
        private ReadWriteLock  lock = new ReentrantReadWriteLock();
        
        public PricesInfo(){
        }
        
        //读锁
        public void getPrice(){
            lock.readLock().lock();
            System.out.println(Thread.currentThread().getName()+ " : in read*****************************");
            System.out.println(Thread.currentThread().getName()+ ": 读取数据= " + price);
            lock.readLock().unlock();
        }
        
        //写锁
        public void setPrice(double price){
            lock.writeLock().lock(); 
            try {
                System.out.println(Thread.currentThread().getName()+ " :in Writer==============================================");
                Thread.sleep(1000);
                this.price = price;
                System.out.println(Thread.currentThread().getName()+ ":写入数据= " + price);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.writeLock().unlock();
            }
        }
    }

    控制台输出:

    Thread-0 :in Writer==============================================
    Thread-0:写入数据= 3.5843085966236266
    Thread-3 : in read*****************************
    Thread-3: 读取数据= 3.5843085966236266
    ......

     Condition条件变量

    通过ReentrantLock的newCondition()得到Condition对象,它用await()替换wait(),用signal()替换 notify(),用signalAll()替换notifyAll(), 实现线程间的通信;

    如果是公平锁,与Condition关联的任务,以FIFO的形式获取锁,否则的话,是随机获取锁;

    消费者和生产者的例子

    public class MyCondition{  
        public static void main(String args[]){  
            Info info = new Info(); 
            
            //启动生产者
            Producer pro = new Producer(info) ; 
            new Thread(pro).start() ;  
            
            try{  
                Thread.sleep(100) ;  
            }catch(InterruptedException e){  
                e.printStackTrace() ;  
            }  
      
            //启动消费者
            Consumer con = new Consumer(info) ;
            new Thread(con).start() ;  
        }  
    }  
    
    class Info{ // 定义信息类  
        private String name = null;
        private String content = null ;
        private boolean flag = true ;   // true生产, false消费  
      
        private Lock lock = new ReentrantLock();    
        private Condition condition = lock.newCondition(); //产生一个Condition对象  
       
        public  void set(String name,String content){  
            lock.lock();  
            try{  
                while(!flag){  
                    condition.await() ;  
                }  
                
                this.setName(name) ;   
                
                Thread.sleep(300) ;  
                
                this.setContent(content) ; 
                flag  = false ; // 改变标志位,表示可以取走  
                
                System.out.println("生产者: " + this.getName() +  " --> " + this.getContent()) ;
                
                condition.signal();  
            }catch(InterruptedException e){  
                e.printStackTrace() ;  
            }finally{  
                lock.unlock();  
            }  
        }  
      
        public void get(){  
            lock.lock();  
            try{  
                while(flag){  
                    condition.await() ;  
                }     
                
                Thread.sleep(300) ;  
                
                System.out.println("消费者: " + this.getName() +  " --> " + this.getContent()) ;  
               
                flag  = true ;  // 改变标志位,表示可以生产  
               
                condition.signal();  
            }catch(InterruptedException e){  
                e.printStackTrace() ;  
            }finally{  
                lock.unlock();  
            }  
        }  
      
        public void setName(String name){  
            this.name = name ;  
        }  
        public void setContent(String content){  
            this.content = content ;  
        }  
        public String getName(){  
            return this.name ;  
        }  
        public String getContent(){  
            return this.content ;  
        }  
    }  
    
    /**生产者线程 */
    class Producer implements Runnable{   
        private Info info = null ;      // 保存Info引用  
        
        public Producer(Info info){  
            this.info = info ;  
        }  
        
        public void run(){  
            boolean flag = true ;   // 定义标记位  
            for(int i=0;i<10;i++){  
                if(flag){  
                    this.info.set("姓名--1","内容--1") ;    
                    flag = false ;  
                }else{  
                    this.info.set("姓名--2","内容--2") ;     
                    flag = true ;  
                }  
            }  
        }  
    } 
    
    /**消费者线程 */
    class Consumer implements Runnable{  
        private Info info = null ;  
        
        public Consumer(Info info){  
            this.info = info ;  
        }  
        public void run(){ 
            for(int i=0;i<10;i++){  
                this.info.get() ;  
            }  
        }  
    } 

    AQS 和 CAS

    AQS : JUC基础类

    state : 获取锁的标志

    NOde{} : 获取锁的线程
    SHARED : 共享锁
    EXCLUSIVE : 互斥锁

    CLH同步队列

    LockSupport.park() 和 LockSupport.unpark() :阻塞和唤醒

    CAS: JUC基础理论

    对内存中共享数据进行操作的指令集, 自动更新共享数据, 代替了锁

    内存值V,旧的预期值A,要修改的新值B。当且仅A和内存值V相同时,将内存值V修改为B,否则什么都不做

    ABA问题

    因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A

  • 相关阅读:
    java设计模式之工厂方法模式
    java设计模式之抽象工厂模式
    HADOOP集群搭建
    错误代码:0x80070032 处理程序“PageHandlerFactory-Integrated”在其模块列表中有一个错误模块“ManagedPipelineHandler”
    Data Flow ->> Character Map
    Data Flow ->> Import Column & Export Column
    Data Flow ->> Raw File Source & Raw File Destination
    Data Flow ->> OLE DB Destination ->> Fast Load
    Connection Manager ->> Multiple Flat File Connection & Multiple File Connection
    Data Flow ->> Multiple Excel Sheet Loaded Into One Table
  • 原文地址:https://www.cnblogs.com/liuconglin/p/6693825.html
Copyright © 2011-2022 走看看