zoukankan      html  css  js  c++  java
  • Lock 锁

    简单使用 Lock 锁

    Java 5 中引入了新的锁机制——java.util.concurrent.locks 中的显式的互斥锁:Lock 接口,它提供了比synchronized 更加广泛的锁定操作。Lock 接口有 3 个实现它的类:ReentrantLock、ReetrantReadWriteLock.ReadLock 和 ReetrantReadWriteLock.WriteLock,即重入锁、读锁和写锁。

    采用 lock 加锁和释放锁的一般形式如下:

    Lock lock = new ReentrantLock();//默认使用非公平锁,如果要使用公平锁,需要传入参数true  
    ........  
    lock.lock();  
    try {  
         //更新对象的状态  
        //捕获异常,必要时恢复到原来的不变约束  
       //如果有return语句,放在这里  
     finally {  
           lock.unlock();        //锁必须在finally块中释放  

     

    可中断锁

    ReetrantLock 有两种锁:忽略中断锁和响应中断锁。忽略中断锁与 synchronized 实现的互斥锁一样,不能响应中断,而响应中断锁可以响应中断。

    获得响应中断锁的一般形式如下:

    ReentrantLock lock = new ReentrantLock();  
    ...........  
    lock.lockInterruptibly();//获取响应中断锁  
    try {  
          //更新对象的状态  
          //捕获异常,必要时恢复到原来的不变约束  
          //如果有return语句,放在这里  
    }finally{  
        lock.unlock();        //锁必须在finally块中释放  
    } 

    当用 synchronized 中断对互斥锁的等待时,并不起作用,该线程依然会一直等待,如下面的实例:

    public class Buffer {
    
        private Object lock;
    
        public Buffer() {
            lock = this;
        }
    
        public void write() {
            synchronized (lock) {
                long startTime = System.currentTimeMillis();
                System.out.println("开始往这个buff写入数据…");
                for (; ; ) {// 模拟要处理很长时间
                    if (System.currentTimeMillis() - startTime > Integer.MAX_VALUE) {
                        break;
                    }
                }
                System.out.println("终于写完了");
            }
    
        }
    
        public void read() {
            synchronized (lock) {
                System.out.println("从这个buff读数据");
            }
        }
    
        public static void main(String[] args) {
            Buffer buff = new Buffer();
    
            final Writer writer = new Writer(buff);
            final Reader reader = new Reader(buff);
    
            writer.start();
            reader.start();
    
            new Thread(new Runnable() {
    
                @Override
                public void run() {
                    long start = System.currentTimeMillis();
                    for (; ; ) {                    //等5秒钟去中断读
                        if (System.currentTimeMillis() - start > 5000) {
                            System.out.println("不等了,尝试中断");
                            reader.interrupt();  //尝试中断读线程
                            break;
                        }
                    }
    
                }
            }).start();
            // 我们期待“读”这个线程能退出等待锁,可是事与愿违,一旦读这个线程发现自己得不到锁,
            // 就一直开始等待了,就算它等死,也得不到锁,因为写线程要21亿秒才能完成 T_T ,即使我们中断它,
            // 它都不来响应下,看来真的要等死了。这个时候,ReentrantLock给了一种机制让我们来响应中断,
            // 让“读”能伸能屈,勇敢放弃对这个锁的等待。我们来改写Buffer这个类,就叫BufferInterruptibly吧,可中断缓存。
        }
    }
    
    class Writer extends Thread {
    
        private Buffer buff;
    
        public Writer(Buffer buff) {
            this.buff = buff;
        }
    
        @Override
        public void run() {
            buff.write();
        }
    }
    
    class Reader extends Thread {
    
        private Buffer buff;
    
        public Reader(Buffer buff) {
            this.buff = buff;
        }
    
        @Override
        public void run() {
            buff.read();//这里估计会一直阻塞
            System.out.println("读结束");
        }
    }

    将上面代码中 synchronized 的互斥锁改为 ReentrantLock 的响应中断锁,即改为如下代码:

    public class BufferInterruptibly {
    
        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[]) {
            BufferInterruptibly buff = new BufferInterruptibly();
    
            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 BufferInterruptibly buff;
    
        public Reader2(BufferInterruptibly 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 BufferInterruptibly buff;
    
        public Writer2(BufferInterruptibly buff) {
            this.buff = buff;
        }
    
        @Override
        public void run() {
            buff.write();
        }
    
    }

    条件变量实现线程间协作

    下面将生产者——消费者模型一文中的代码改为用条件变量实现,如下:

    class Info { // 定义信息类
        private String name = "name";//定义name属性,为了与下面set的name属性区别开
        private String content = "content";// 定义content属性,为了与下面set的content属性区别开
        private boolean flag = true;   // 设置标志位,初始时先生产
        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; // 改变标志位,表示可以取走
                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 { // 通过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();
            }
        }
    }
    
    class ThreadCaseDemo {
        public static void main(String args[]) {
            Info info = new Info(); // 实例化Info对象
            Producer pro = new Producer(info); // 生产者
            Consumer con = new Consumer(info); // 消费者
            new Thread(pro).start();
            //启动了生产者线程后,再启动消费者线程
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            new Thread(con).start();
        }
    }

    从以上并不能看出用条件变量的 await()、signal()、signalAll()方法比用 Object 对象的 wait()、notify()、notifyAll()方法实现线程间协作有多少优点,但它在处理更复杂的多线程问题时,会有明显的优势。所以,Lock 和 Condition 对象只有在更加困难的多线程问题中才是必须的。

    读写锁

    另外,synchronized 获取的互斥锁不仅互斥读写操作、写写操作,还互斥读读操作,而读读操作时不会带来数据竞争的,因此对对读读操作也互斥的话,会降低性能。Java 5 中提供了读写锁,它将读锁和写锁分离,使得读读操作不互斥,获取读锁和写锁的一般形式如下:

    ReadWriteLock rwl = new ReentrantReadWriteLock();      
    rwl.writeLock().lock()  //获取写锁  
    rwl.readLock().lock()  //获取读锁  

    用读锁来锁定读操作,用写锁来锁定写操作,这样写操作和写操作之间会互斥,读操作和写操作之间会互斥,但读操作和读操作就不会互斥。

    《Java 并发编程实践》一书给出了使用 ReentrantLock 的最佳时机:

    当你需要以下高级特性时,才应该使用:可定时的、可轮询的与可中断的锁获取操作,公平队列,或者非块结构的锁。否则,请使用 synchronized。

    参考来源:jike.并发新特性—Lock 锁与条件变量

  • 相关阅读:
    常用的模板标签
    django的静态文件的引入
    模板路径的查找
    类型初始值设定项引发异常
    ASP.NET基础笔记
    ASP.NET 学习笔记
    【1】验证适配器
    同时安装vs2010和VS2012后IEnumerable<ModelClientValidationRule>编译错误
    跨窗体传值
    扩展方法
  • 原文地址:https://www.cnblogs.com/ooo0/p/12456657.html
Copyright © 2011-2022 走看看