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

 

posted @ 2017-04-11 17:08  liuconglin  阅读(235)  评论(0编辑  收藏  举报