• (转)深入研究ReentrantLock(重入锁)之引出话题篇


    synchronized原语和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面2种需求的时候。 
    1.某个线程在等待一个锁的控制权的这段时间需要中断 
    2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程 
    3.具有公平锁功能,每个到来的线程都将排队等候 
    下面细细道来…… 

    先说第一种情况,ReentrantLock的lock机制有2种,忽略中断锁和响应中断锁,这给我们带来了很大的灵活性。比如:如果A、B2个线程去竞争锁,A线程得到了锁,B线程等待,但是A线程这个时候实在有太多事情要处理,就是一直不返回,B线程可能就会等不及了,想中断自己,不再等待这个锁了,转而处理其他事情。这个时候ReentrantLock就提供了2种机制,第一,B线程中断自己(或者别的线程中断它),但是ReentrantLock不去响应,继续让B线程等待,你再怎么中断,我全当耳边风(synchronized原语就是如此);第二,B线程中断自己(或者别的线程中断它),ReentrantLock处理了这个中断,并且不再等待这个锁的到来,完全放弃。(如果你没有了解java的中断机制,请参考下相关资料,再回头看这篇文章,80%的人根本没有真正理解什么是java的中断,呵呵) 

    这里来做个试验,首先搞一个Buffer类,它有读操作和写操作,为了不读到脏数据,写和读都需要加锁,我们先用synchronized原语来加锁,如下: 

    Java代码 
    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读数据");    
            }    
        }    
    }   


    接着,我们来定义2个线程,一个线程去写,一个线程去读。 

    Java代码 
    public class Writer extends Thread {    
         
        private Buffer buff;    
         
        public Writer(Buffer buff) {    
            this.buff = buff;    
        }    
         
        @Override    
        public void run() {    
            buff.write();    
        }    
         
    }    
         
    public class Reader extends Thread {    
         
        private Buffer buff;    
         
        public Reader(Buffer buff) {    
            this.buff = buff;    
        }    
         
        @Override    
        public void run() {    
         
            buff.read();//这里估计会一直阻塞    
         
            System.out.println("读结束");    
         
        }    
         
    }   

    好了,写一个Main来试验下,我们有意先去“写”,然后让“读”等待,“写”的时间是无穷的,就看“读”能不能放弃了。 

    Java代码 
    public class Test {    
        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吧,可中断缓存。 

    Java代码 
    import java.util.concurrent.locks.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();    
            }    
        }    
         
    }   

    当然,要对reader和writer做响应的修改 

    Java代码 
    public class Reader extends Thread {    
         
        private BufferInterruptibly buff;    
         
        public Reader(BufferInterruptibly buff) {    
            this.buff = buff;    
        }    
         
        @Override    
        public void run() {    
         
            try {    
                buff.read();//可以收到中断的异常,从而有效退出    
            } catch (InterruptedException e) {    
                System.out.println("我不读了");    
            }    
               
            System.out.println("读结束");    
         
        }    
         
    }    
         
    /**   
    * Writer倒不用怎么改动   
    */    
    public class Writer extends Thread {    
         
        private BufferInterruptibly buff;    
         
        public Writer(BufferInterruptibly buff) {    
            this.buff = buff;    
        }    
         
        @Override    
        public void run() {    
            buff.write();    
        }    
         
    }    
         
    public class Test {    
        public static void main(String[] args) {    
            BufferInterruptibly buff = new BufferInterruptibly();    
         
            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 (;;) {    
                        if (System.currentTimeMillis()    
                                - start > 5000) {    
                            System.out.println("不等了,尝试中断");    
                            reader.interrupt();    
                            break;    
                        }    
         
                    }    
         
                }    
            }).start();    
         
        }    
    }   

    这次“读”线程接收到了lock.lockInterruptibly()中断,并且有效处理了这个“异常”。好奇的读者,肯定要探个究竟,为什么ReentrantLock能做到这点,接下来,我们去迷宫探险吧…… 

  • 相关阅读:
    封装LuaEngine
    Lua for循环
    lua 排序
    Lua中的数学库
    linux 常用指令
    lua table 遍历
    Charles——charles代理菜单proxy总结——端口转发
    Charles——charles代理菜单proxy总结—— 开始/暂停模拟慢网速—— stop/start throttling 和 throttling settings
    Charles——charles代理菜单proxy总结——代理设置proxy setting
    Charles——charles代理菜单proxy总结——SSL代理设置SSL Proxying Srtting
  • 原文地址:https://www.cnblogs.com/balaamwe/p/2266480.html
Copyright © 2020-2023  润新知