您的位置:首页 > 编程语言 > Java开发

Java 并发编程中使用 ReentrantLock 替代 synchronized 关键字原语

2016-12-01 09:33 621 查看
      Java 5 引入的 Concurrent 并发库软件包中,提供了 ReentrantLock 可重入同步锁,用来替代 synchronized 关键字原语,并可提供更好的性能,以及更强大的功能。使用方法也很简单:

public final ReentrantLock lock=new ReentrantLock();

......

try {

    lock.lock();

    // 进入同步内容

    ....

} finally {

    lock.unlock(); // 必须在 finally 块中解锁,否则一旦出现异常,执行不到解锁,则一直锁住了。

}

 

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原语来加锁,如下:

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个线程,一个线程去写,一个线程去读。

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来试验下,我们有意先去“写”,然后让“读”等待,“写”的时间是无穷的,就看“读”能不能放弃了。

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吧,可中断缓存。

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做响应的修改

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("读结束");    

     

    }    

     

}    

     

    

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()中断,并且有效处理了这个“异常”。

本文永久更新链接地址:http://www.linuxidc.com/Linux/2015-12/125920.htm

本篇文章来源于 Linux公社网站(www.linuxidc.com)  原文链接:http://www.linuxidc.com/Linux/2015-12/125920.htm
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 编程 并发
相关文章推荐