海 子

不经历风雨,怎能见彩虹?做一个快乐的程序员。

  • 博客园
  • 首页
  • 新随笔
  • 联系
  • 订阅
  • 管理
随笔 - 184  文章 - 0  评论 - 1523

Java并发编程:Lock

Java并发编程:Lock

  在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问。本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock。

  也许有朋友会问,既然都可以通过synchronized来实现同步访问了,那么为什么还需要提供Lock?这个问题将在下面进行阐述。本文先从synchronized的缺陷讲起,然后再讲述java.util.concurrent.locks包下常用的有哪些类和接口,最后讨论以下一些关于锁的概念方面的东西

  以下是本文目录大纲:

  一.synchronized的缺陷

  二.java.util.concurrent.locks包下常用的类

  三.锁的相关概念介绍

  若有不正之处请多多谅解,并欢迎批评指正。

  请尊重作者劳动成果,转载请标明原文链接:

  http://www.cnblogs.com/dolphin0520/p/3923167.html

一.synchronized的缺陷

  synchronized是java中的一个关键字,也就是说是Java语言内置的特性。那么为什么会出现Lock呢?

  在上面一篇文章中,我们了解到如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:

  1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;

  2)线程执行发生异常,此时JVM会让线程自动释放锁。

  那么如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。

  因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到。

  再举个例子:当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。

  但是采用synchronized关键字来实现同步的话,就会导致一个问题:

  如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。

  因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。

  另外,通过Lock可以知道线程有没有成功获取到锁。这个是synchronized无法办到的。

  总结一下,也就是说Lock提供了比synchronized更多的功能。但是要注意以下几点:

  1)Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问;

  2)Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。

二.java.util.concurrent.locks包下常用的类

  下面我们就来探讨一下java.util.concurrent.locks包中常用的类和接口。

  1.Lock

  首先要说明的就是Lock,通过查看Lock的源码可知,Lock是一个接口:

1
2
3
4
5
6
7
8
public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

  下面来逐个讲述Lock接口中每个方法的使用,lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是用来获取锁的。unLock()方法是用来释放锁的。newCondition()这个方法暂且不在此讲述,会在后面的线程协作一文中讲述。

  在Lock中声明了四个方法来获取锁,那么这四个方法有何区别呢?

  首先lock()方法是平常使用得最多的一个方法,就是用来获取锁。如果锁已被其他线程获取,则进行等待。

  由于在前面讲到如果采用Lock,必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。通常使用Lock来进行同步的话,是以下面这种形式去使用的:

1
2
3
4
5
6
7
8
9
Lock lock = ...;
lock.lock();
try{
    //处理任务
}catch(Exception ex){
     
}finally{
    lock.unlock();   //释放锁
}

  tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。

  tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。

  所以,一般情况下通过tryLock来获取锁时是这样使用的:

1
2
3
4
5
6
7
8
9
10
11
12
Lock lock = ...;
if(lock.tryLock()) {
     try{
         //处理任务
     }catch(Exception ex){
         
     }finally{
         lock.unlock();   //释放锁
     
}else {
    //如果不能获取锁,则直接做其他事情
}

  lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。

  由于lockInterruptibly()的声明中抛出了异常,所以lock.lockInterruptibly()必须放在try块中或者在调用lockInterruptibly()的方法外声明抛出InterruptedException。

  因此lockInterruptibly()一般的使用形式如下:

1
2
3
4
5
6
7
8
9
public void method() throws InterruptedException {
    lock.lockInterruptibly();
    try {  
     //.....
    }
    finally {
        lock.unlock();
    }  
}

  注意,当一个线程获取了锁之后,是不会被interrupt()方法中断的。因为本身在前面的文章中讲过单独调用interrupt()方法不能中断正在运行过程中的线程,只能中断阻塞过程中的线程。

  因此当通过lockInterruptibly()方法获取某个锁时,如果不能获取到,只有进行等待的情况下,是可以响应中断的。

  而用synchronized修饰的话,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去。

  2.ReentrantLock

  ReentrantLock,意思是“可重入锁”,关于可重入锁的概念在下一节讲述。ReentrantLock是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法。下面通过一些实例看具体看一下如何使用ReentrantLock。

  例子1,lock()的正确使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Test {
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();
    public static void main(String[] args)  {
        final Test test = new Test();
         
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
         
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
    }  
     
    public void insert(Thread thread) {
        Lock lock = new ReentrantLock();    //注意这个地方
        lock.lock();
        try {
            System.out.println(thread.getName()+"得到了锁");
            for(int i=0;i<5;i++) {
                arrayList.add(i);
            }
        } catch (Exception e) {
            // TODO: handle exception
        }finally {
            System.out.println(thread.getName()+"释放了锁");
            lock.unlock();
        }
    }
}

  各位朋友先想一下这段代码的输出结果是什么?

Thread-0得到了锁
Thread-1得到了锁
Thread-0释放了锁
Thread-1释放了锁

  也许有朋友会问,怎么会输出这个结果?第二个线程怎么会在第一个线程释放锁之前得到了锁?原因在于,在insert方法中的lock变量是局部变量,每个线程执行该方法时都会保存一个副本,那么理所当然每个线程执行到lock.lock()处获取的是不同的锁,所以就不会发生冲突。

  知道了原因改起来就比较容易了,只需要将lock声明为类的属性即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Test {
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();
    private Lock lock = new ReentrantLock();    //注意这个地方
    public static void main(String[] args)  {
        final Test test = new Test();
         
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
         
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
    }  
     
    public void insert(Thread thread) {
        lock.lock();
        try {
            System.out.println(thread.getName()+"得到了锁");
            for(int i=0;i<5;i++) {
                arrayList.add(i);
            }
        } catch (Exception e) {
            // TODO: handle exception
        }finally {
            System.out.println(thread.getName()+"释放了锁");
            lock.unlock();
        }
    }
}

  这样就是正确地使用Lock的方法了。

  例子2,tryLock()的使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class Test {
    private ArrayList<Integer> arrayList = new ArrayList<Integer>();
    private Lock lock = new ReentrantLock();    //注意这个地方
    public static void main(String[] args)  {
        final Test test = new Test();
         
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
         
        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
    }  
     
    public void insert(Thread thread) {
        if(lock.tryLock()) {
            try {
                System.out.println(thread.getName()+"得到了锁");
                for(int i=0;i<5;i++) {
                    arrayList.add(i);
                }
            } catch (Exception e) {
                // TODO: handle exception
            }finally {
                System.out.println(thread.getName()+"释放了锁");
                lock.unlock();
            }
        } else {
            System.out.println(thread.getName()+"获取锁失败");
        }
    }
}

  输出结果:

Thread-0得到了锁
Thread-1获取锁失败
Thread-0释放了锁

  例子3,lockInterruptibly()响应中断的使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class Test {
    private Lock lock = new ReentrantLock();   
    public static void main(String[] args)  {
        Test test = new Test();
        MyThread thread1 = new MyThread(test);
        MyThread thread2 = new MyThread(test);
        thread1.start();
        thread2.start();
         
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread2.interrupt();
    }  
     
    public void insert(Thread thread) throws InterruptedException{
        lock.lockInterruptibly();   //注意,如果需要正确中断等待锁的线程,必须将获取锁放在外面,然后将InterruptedException抛出
        try {  
            System.out.println(thread.getName()+"得到了锁");
            long startTime = System.currentTimeMillis();
            for(    ;     ;) {
                if(System.currentTimeMillis() - startTime >= Integer.MAX_VALUE)
                    break;
                //插入数据
            }
        }
        finally {
            System.out.println(Thread.currentThread().getName()+"执行finally");
            lock.unlock();
            System.out.println(thread.getName()+"释放了锁");
        }  
    }
}
class MyThread extends Thread {
    private Test test = null;
    public MyThread(Test test) {
        this.test = test;
    }
    @Override
    public void run() {
         
        try {
            test.insert(Thread.currentThread());
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName()+"被中断");
        }
    }
}

  运行之后,发现thread2能够被正确中断。

  3.ReadWriteLock

  ReadWriteLock也是一个接口,在它里面只定义了两个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public interface ReadWriteLock {
    /**
     * Returns the lock used for reading.
     *
     * @return the lock used for reading.
     */
    Lock readLock();
    /**
     * Returns the lock used for writing.
     *
     * @return the lock used for writing.
     */
    Lock writeLock();
}

  一个用来获取读锁,一个用来获取写锁。也就是说将文件的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。下面的ReentrantReadWriteLock实现了ReadWriteLock接口。

  4.ReentrantReadWriteLock

  ReentrantReadWriteLock里面提供了很多丰富的方法,不过最主要的有两个方法:readLock()和writeLock()用来获取读锁和写锁。

  下面通过几个例子来看一下ReentrantReadWriteLock具体用法。

  假如有多个线程要同时进行读操作的话,先看一下synchronized达到的效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Test {
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
     
    public static void main(String[] args)  {
        final Test test = new Test();
         
        new Thread(){
            public void run() {
                test.get(Thread.currentThread());
            };
        }.start();
         
        new Thread(){
            public void run() {
                test.get(Thread.currentThread());
            };
        }.start();
         
    }  
     
    public synchronized void get(Thread thread) {
        long start = System.currentTimeMillis();
        while(System.currentTimeMillis() - start <= 1) {
            System.out.println(thread.getName()+"正在进行读操作");
        }
        System.out.println(thread.getName()+"读操作完毕");
    }
}

  这段程序的输出结果会是,直到thread1执行完读操作之后,才会打印thread2执行读操作的信息。

View Code

  而改成用读写锁的话:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Test {
    private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
     
    public static void main(String[] args)  {
        final Test test = new Test();
         
        new Thread(){
            public void run() {
                test.get(Thread.currentThread());
            };
        }.start();
         
        new Thread(){
            public void run() {
                test.get(Thread.currentThread());
            };
        }.start();
         
    }  
     
    public void get(Thread thread) {
        rwl.readLock().lock();
        try {
            long start = System.currentTimeMillis();
             
            while(System.currentTimeMillis() - start <= 1) {
                System.out.println(thread.getName()+"正在进行读操作");
            }
            System.out.println(thread.getName()+"读操作完毕");
        } finally {
            rwl.readLock().unlock();
        }
    }
}

  此时打印的结果为:

View Code

  说明thread1和thread2在同时进行读操作。

  这样就大大提升了读操作的效率。

  不过要注意的是,如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。

  如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。

  关于ReentrantReadWriteLock类中的其他方法感兴趣的朋友可以自行查阅API文档。

  5.Lock和synchronized的选择

  总结来说,Lock和synchronized有以下几点不同:

  1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

  2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

  3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

  4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

  5)Lock可以提高多个线程进行读操作的效率。

  在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

三.锁的相关概念介绍

  在前面介绍了Lock的基本使用,这一节来介绍一下与锁相关的几个概念。

  1.可重入锁

  如果锁具备可重入性,则称作为可重入锁。像synchronized和ReentrantLock都是可重入锁,可重入性在我看来实际上表明了锁的分配机制:基于线程的分配,而不是基于方法调用的分配。举个简单的例子,当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法method2。

  看下面这段代码就明白了:

1
2
3
4
5
6
7
8
9
class MyClass {
    public synchronized void method1() {
        method2();
    }
     
    public synchronized void method2() {
         
    }
}

  上述代码中的两个方法method1和method2都用synchronized修饰了,假如某一时刻,线程A执行到了method1,此时线程A获取了这个对象的锁,而由于method2也是synchronized方法,假如synchronized不具备可重入性,此时线程A需要重新申请锁。但是这就会造成一个问题,因为线程A已经持有了该对象的锁,而又在申请获取该对象的锁,这样就会线程A一直等待永远不会获取到的锁。

  而由于synchronized和Lock都具备可重入性,所以不会发生上述现象。

  2.可中断锁

  可中断锁:顾名思义,就是可以相应中断的锁。

  在Java中,synchronized就不是可中断锁,而Lock是可中断锁。

  如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。

  在前面演示lockInterruptibly()的用法时已经体现了Lock的可中断性。

  3.公平锁

  公平锁即尽量以请求锁的顺序来获取锁。比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。

  非公平锁即无法保证锁的获取是按照请求锁的顺序进行的。这样就可能导致某个或者一些线程永远获取不到锁。

  在Java中,synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。

  而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。

  看一下这2个类的源代码就清楚了:

  

  在ReentrantLock中定义了2个静态内部类,一个是NotFairSync,一个是FairSync,分别用来实现非公平锁和公平锁。

  我们可以在创建ReentrantLock对象时,通过以下方式来设置锁的公平性:

1
ReentrantLock lock = new ReentrantLock(true);

  如果参数为true表示为公平锁,为fasle为非公平锁。默认情况下,如果使用无参构造器,则是非公平锁。

  

  另外在ReentrantLock类中定义了很多方法,比如:

  isFair()        //判断锁是否是公平锁

  isLocked()    //判断锁是否被任何线程获取了

  isHeldByCurrentThread()   //判断锁是否被当前线程获取了

  hasQueuedThreads()   //判断是否有线程在等待该锁

  在ReentrantReadWriteLock中也有类似的方法,同样也可以设置为公平锁和非公平锁。不过要记住,ReentrantReadWriteLock并未实现Lock接口,它实现的是ReadWriteLock接口。

  4.读写锁

  读写锁将对一个资源(比如文件)的访问分成了2个锁,一个读锁和一个写锁。

  正因为有了读写锁,才使得多个线程之间的读操作不会发生冲突。

  ReadWriteLock就是读写锁,它是一个接口,ReentrantReadWriteLock实现了这个接口。

  可以通过readLock()获取读锁,通过writeLock()获取写锁。

  上面已经演示过了读写锁的使用方法,在此不再赘述。

  参考资料:

  http://blog.csdn.net/ns_code/article/details/17487337

  http://houlinyan.iteye.com/blog/1112535

  http://ifeve.com/locks/

  http://ifeve.com/read-write-locks/

  http://blog.csdn.net/fancyerii/article/details/6783224

  http://blog.csdn.net/ghsau/article/details/7461369/

  http://blog.csdn.net/zhaozhenzuo/article/details/37109015

作者:海子

    

出处:http://www.cnblogs.com/dolphin0520/

    

本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

分类: Java并发编程
标签: java并发编程
好文要顶 关注我 收藏该文

海 子
关注 - 6
粉丝 - 5026

+加关注

75
1

« 上一篇:Java并发编程:synchronized
» 下一篇:Java并发编程:volatile关键字解析

posted @ 2014-08-20 16:57 海 子 阅读(85182) 评论(40) 编辑 收藏

评论列表
#1楼2014-08-20 18:24 jim小子
我来给你顶一个,看了你的博客,写的很明了,赞
支持(3)反对(0)

#2楼2014-08-20 21:46 zhidan
前几天也在百度搜到楼主的文章,感谢楼主。楼主继续写好东西
支持(1)反对(0)

#3楼2014-08-20 22:37 Alexia(minmin)
写的太好了,非常有帮助
支持(2)反对(0)

#4楼2015-05-16 20:58 之奇一昂
感谢
支持(0)反对(0)

#5楼2015-08-04 20:44 candy_彩豆
首先来膜拜一下博主~~~啊哈~~~~
but呢。。。在用if(lock.tryLock())时,两个线程都得到了锁,和大神的结果有点儿不一样嘞。。。。
Thread-0得到了锁
Thread-0释放了锁
Thread-1得到了锁
Thread-1释放了锁
就在这时,在insert里加了一个sleep
if(lock.tryLock()){
try {
System.out.println(thread.getName()+"得到了锁");
System.out.println(System.currentTimeMillis() + ":sleep前");
try {
thread.sleep(new Long("2000"));
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i=0;i<5;i++) {
arrayList.add(i);
}
System.out.println(System.currentTimeMillis() + ":哈睡醒了,满血复活");
} catch (Exception e) {
// TODO: handle exception
}finally {
System.out.println(thread.getName()+"释放了锁");
lock.unlock();
}
}else{
System.out.println(thread.getName()+"获取锁失败");
}
哈~~~结果出来了:
Thread-0得到了锁
1438692215251:sleep前
Thread-1获取锁失败
1438692217251:哈睡醒了,满血复活
Thread-0释放了锁
支持(2)反对(0)

#6楼2015-09-04 20:53 LUCKYZHOUSTAR
膜拜一下,写的太好了
支持(0)反对(0)

#7楼2015-10-20 15:18 jamespei
学习了,非常感谢
支持(0)反对(0)

#8楼2015-11-15 20:33 sstong123
好文章,看了好几次了,每次看都有长进!
支持(0)反对(0)

#9楼2015-11-18 14:48 bookwed
首先感谢一下,写得不错。再问个问题
4.ReentrantReadWriteLock 这一块,改成用读写锁的代码,好像执行结果不是每次都是最后执行完毕啊?执行的结果和上面那段代码差不多。
支持(0)反对(0)

#10楼2016-01-01 20:51 放纵的卡尔
谢谢楼主分享的知识,再接再厉!
支持(0)反对(0)

#11楼2016-01-26 21:38 朝兮兮
不知道为什么, 例1 我测试结果
Thread-0得到了锁
Thread-0释放了锁
Thread-1得到了锁
Thread-1释放了锁
例2抛出异常
支持(0)反对(0)

#12楼2016-02-23 14:01 飞翔之鹏
写的不错!~
支持(0)反对(0)

#13楼2016-02-29 15:58 Ryan.Miao
学习学习。有点问题,就是中断锁。中断后确实打印出中断的信息,但是在idea中还在运行,没有结束运行啊
支持(0)反对(0)

#14楼2016-02-29 18:22 贝拉巴拉
4.ReentrantReadWriteLock中第一个代码块 少写了代码
支持(0)反对(0)

#15楼2016-04-20 09:41 little white
登录只为了赞楼主一下

看了楼主的文章

豁然开朗

支持(0)反对(0)

#16楼2016-05-06 00:05 cxxbro
有必要说明一下,在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock(差不多是两倍),但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态。楼主可以写一个测试用例测试一下
支持(0)反对(0)

#17楼2016-05-28 00:28 ymqq
@cxxbro
请问这个数据怎么来的呢?
支持(0)反对(0)

#18楼2016-05-30 18:35 DavieTiming
有个地方有误:
“在insert方法中的lock变量是局部变量,每个线程执行该方法时都会保存一个副本”

你把这个地方理解成工作线程和主存的同步关系去了,事实上这个地方不能这么解释,这两句话就是写在一起也是同样的结果因为调用了两次new在堆上分配了2次内存等于是两个对象,所以锁不一样不能同步

支持(3)反对(1)

#19楼2016-06-16 21:47 梨,性寒
不错,顶一下
支持(0)反对(0)

#20楼2016-06-28 11:17 piter
有些细节写的不清楚
支持(0)反对(0)

#21楼2016-08-03 15:57 DIAOSIYUAN
4.ReentrantReadWriteLock
中的第一个例子,如果把 把insert里面的内容System.currentTimeMillis() - start <= 10000,会发现,线程0和线程1交替执行,并非一个执行完成后另一个才执行;并且不加锁的时间比加锁时间要短,说明不加锁更快。
支持(2)反对(1)

#22楼2016-08-04 14:07 不满
@DIAOSIYUAN
对啊,那只读锁的意义何在呢?又有点蒙圈了...
支持(0)反对(0)

#23楼2016-08-25 10:29 Smin
@不满
加锁了当然效率要比不加锁差
我的理解是锁的作用在于多线程操作的时候,ReadLock能保证多线程同时读的时候不用担心被其他线程执行了写操作.一个线程获取了这个对象的readLock,其他线程读时不用等待获取锁而如果要写时则要等待释放锁。
----
LZ的原话
'''
如果有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁。
如果有一个线程已经占用了写锁,则此时其他线程如果申请写锁或者读锁,则申请的线程会一直等待释放写锁。
'''
支持(2)反对(1)

#24楼2016-09-07 10:51 奔跑de小猪
写的很详细,谢谢!
支持(0)反对(0)

#25楼2016-09-12 17:36 akka_li
@DIAOSIYUAN
已经使用了synchronized关键字同步了,不可能会出现两个线程同时获取到锁,交替打印的情况!!!
层主把代码贴出来
支持(0)反对(0)

#26楼2016-12-02 13:33 陈标
受益匪浅,特发感谢信
支持(0)反对(0)

#27楼2017-01-17 09:59 Dreamer-1
@candy_彩豆
结果是对的呀,如果是一个全局lock变量的话,
thread1尝试获取锁时thread0还没有释放锁对象则thread1获取锁失败呀?
支持(0)反对(0)

#28楼2017-01-17 10:06 Dreamer-1
@DavieTiming
那么这个局部的lock对象是在主存中创建还是线程的私有内存中创建呢?
支持(0)反对(0)

#29楼2017-04-17 22:19 gjhgjh
文章中的"注意,当一个线程获取了锁之后,是不会被interrupt()方法中断的。因为本身在前面的文章中讲过单独调用interrupt()方法不能中断正在运行过程中的线程,只能中断阻塞过程中的线程。"我个人认为是错的。测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Solution {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Thread thread1 = new Thread(new B("guo", lock));
        thread1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread1.interrupt();
    }
}
class B implements Runnable {
    private String name;
    Lock lock;
    B(String name, Lock lock) {
        this.name = name;
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            lock.lockInterruptibly();
            try {
                System.out.println("I am " + name);
                Thread.sleep(50000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("interrupted after acquiring lock!");
            } finally {
                lock.unlock();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("interrupted while polling!");
        }
    }
}

支持(0)反对(0)

#30楼2017-06-18 09:35 落叶已归根
@bookwed
哪里少了代码块呀,我觉得是对的啊,开始用synchronized同步,只能线程0先读然后线程1才能读,用了ReentrantReadWriteLock中的readLock()可以同时读
支持(0)反对(0)

#31楼2017-09-11 10:09 曾晓明
lz,为什么前面一篇文章你说interrupt方法可以中断阻塞的线程,而本篇中你却说使用synchronized时,等待的线程会一直等待下去,不能够响应中断。难道等待的线程不是处于阻塞状态?望楼主和各路大神能解释一下,小弟刚学习并发,很多还不懂
支持(0)反对(0)

#32楼2017-09-25 11:11 yzhym
@曾晓明
等待线程是处于一种不可中断的阻塞状态,类似于IO的阻塞状态,无法接收interrupt
来个实际的例子,你可以自己执行看看结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class TreadBlockTest {
     
    synchronized  public void waitMod(int id,long timeout) {
             try {
                 Thread.sleep(timeout);
                 System.out.println(id+":sleep end ");
             } catch (InterruptedException e) {
                 System.out.println(id+":interrupted ");
            }
    }
     
    public static void main(String[] args)throws Exception{
        final TreadBlockTest test = new TreadBlockTest();
        Thread  t1 = new Thread(){
            public void run () {
                test.waitMod(1,5000);
            }
        };
        Thread  t2 = new Thread(){
            public void run (){
                 test.waitMod(2,8000);
            }
        };
        System.out.println("1 start");
        t1.start();//t1占住锁
        Thread.sleep(1000);
        System.out.println("2 start");
        t2.start();//t2开始请求锁
        Thread.sleep(1000);
        System.out.println("2 interrupt");
        t2.interrupt();//t1尚未释放锁,t2一直处于请求锁的状态,无法及时interrupt
    }
}

支持(0)反对(0)

#33楼2017-10-10 20:41 huanglei2010
膜拜大神
支持(0)反对(0)

#34楼2017-11-01 14:01 ptt
赞一个
支持(0)反对(0)

#35楼2017-11-13 13:40 花溪的小石头
总结的非常好!希望能持续更新。
支持(0)反对(0)

#36楼2017-11-13 13:46 花溪的小石头
@DavieTiming
这样说确实更准确一些,不过并不影响作者表达的意思
支持(0)反对(0)

#37楼2017-12-06 13:54 薛小生
被Synchronized修饰的代码块,当线程sleep()是可以被打断吧?
支持(0)反对(0)

#38楼2017-12-21 20:57 o对酒当歌
讲得很好!赞一个!
支持(0)反对(0)

#39楼2018-01-10 14:46 书生侠客
厉害
支持(0)反对(0)

#40楼2018-02-07 15:46 查苏的吉古勒
读你的文章,就像人在旁边,娓娓道来
支持(0)反对(0)

刷新评论刷新页面返回顶部
注册用户登录后才能发表评论,请 登录 或 注册,访问网站首页。
【推荐】超50万VC++源码: 大型组态工控、电力仿真CAD与GIS源码库!【活动】2050 大会 - 年青人因科技而团聚(5.26-5.27 杭州·云栖小镇)【推荐】跟最课程陆敏技学Java,5个月高薪就业【推荐】华为云DevCloud精彩活动集结,重磅福利,免费领取!【活动】腾讯云云服务器新购特惠,5折上云【大赛】2018首届“顶天立地”AI开发者大赛
最新IT新闻:
· 瑞幸咖啡发公开信称遭遇星巴克不正当竞争
· 消息称ofo已拒绝了滴滴的潜在收购要约
· 犹他州特斯拉车祸司机证实:当时开启了Autopilot模式
· 联想新旗舰Z5渲染图曝光 真全面屏手机真要来了?
· 贝莱德收购LG电子5%股份 成为第三大股东
» 更多新闻...
最新知识库文章:

· 评审的艺术——谈谈现实中的代码评审
· 如何高效学习
· 如何成为优秀的程序员?
· 菜鸟工程师的超神之路 -- 从校园到职场
· 如何识别人的技术能力和水平?

» 更多知识库文章...

联系方式:

邮箱:dolphin0520@163.com

微信:scu_dolphin0520

昵称:海 子
园龄:7年1个月
粉丝:5026
关注:6

+加关注

< 2018年5月 >
29 30 1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31 1 2
3 4 5 6 7 8 9

搜索

最新随笔

  • 1. 金山快盘+TortoiseSVN构建版本控制仓库
  • 2. 在windows下安装配置Ulipad
  • 3. Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
  • 4. JAVA多线程和并发基础面试问答(转载)
  • 5. Java线程面试题 Top 50 (转载)
  • 6. Java并发编程:Timer和TimerTask(转载)
  • 7. Java并发编程:Callable、Future和FutureTask
  • 8. Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
  • 9. Java并发编程:线程池的使用
  • 10. Java并发编程:阻塞队列

随笔分类(193)

  • Android(14)
  • C/C++(30)
  • Java NIO(2)
  • Java并发编程(19)
  • Java代码之美
  • Java基础(18)
  • Java集合
  • Java网络编程
  • Java虚拟机
  • JS/CSS/Jquery
  • Linux/Shell(4)
  • Mysql
  • OJ(14)
  • PHP
  • Python/Web框架(16)
  • Redis
  • Web开发(2)
  • XML(1)
  • 计算机网络
  • 计算机系统(3)
  • 开发工具(7)
  • 设计模式(1)
  • 数据结构(11)
  • 数据库(2)
  • 数据挖掘(1)
  • 算法(27)
  • 无线传感器网络(1)
  • 信息检索
  • 业余娱乐(6)
  • 转载(14)
  • 自然语言处理

常用链接

  • C++ Reference
  • MSDN 主页
  • SOJ
  • 北大OJ
  • 并发编程网
  • 杭电OJ

积分与排名

  • 积分 - 435364
  • 排名 - 338

最新评论

  • 1. Re:Java并发编程:深入剖析ThreadLocal
  • 楼主上面说的不set的话get就会报空指针异常的解释有问题,正确原因是你定义的ThreadLocal longLocal = new ThreadLocal();这个是Long 的泛型,调用publi......
  • --北城以北OO
  • 2. Re:Java并发编程:线程池的使用
  • @稻草人Loki失传了,哈哈...
  • --hdu_huang
  • 3. Re:深入理解Java的接口和抽象类
  • 非常好!
  • --bst2000

阅读排行榜

  • 1. Java并发编程:线程池的使用(286484)
  • 2. 深入理解Java的接口和抽象类(247714)
  • 3. 浅析Java中的final关键字(226556)
  • 4. Dijkstra算法(单源最短路径)(184034)
  • 5. Java中的static关键字解析(178837)
  • 6. 二叉树的非递归遍历(156381)
  • 7. Java并发编程:Callable、Future和FutureTask(152848)
  • 8. Java并发编程:volatile关键字解析(142457)
  • 9. 将Sublime Text 2搭建成一个好用的IDE(141522)
  • 10. Java ConcurrentModificationException异常原因和解决方法(140515)

Copyright ©2018 海 子

http://www.cnblogs.com/dolphin0520/p/3923167.html相关推荐

  1. http://www.cnblogs.com/dolphin0520/p/3949310.html

    http://www.cnblogs.com/dolphin0520/p/3949310.html

  2. Java多线程:synchronized关键字和Lock

    一.synchronized synchronized关键字可以用于声明方法,也可以用来声明代码块,下面分别看一下具体的场景(摘抄自<大型网站系统与Java中间件实践>) 案例一:其中fo ...

  3. Lock和synchronized比较详解

    原文:http://www.cnblogs.com/handsomeye/p/5999362.html 今天看了并发实践这本书的ReentantLock这章,感觉对ReentantLock还是不够熟悉 ...

  4. Java并发编程:Lock和Synchronized 转

    在上一篇文章中我们讲到了如何使用关键字synchronized来实现同步访问.本文我们继续来探讨这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方 ...

  5. java线程(2)--同步和锁

    1.线程的内存模型 Java作为平台无关性语言,JLS(Java语言规范)定义了一个统一的内存管理模型JMM(Java Memory Model),JMM屏蔽了底层平台内存管理细节,在多线程环境中必须 ...

  6. Lock和synchronized比较详解(转)

    从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock. 也许有朋友会问,既然都可以通过synchronized来实现同步访问了 ...

  7. Java并发—锁的使用及原理

    原文地址:java并发之锁的使用以及原理浅析 本文将从下面几个方面介绍: 锁的相关概念 java.util.concurrent.locks下常用的几种锁 一.锁的相关概念 在学习或者使用Java的过 ...

  8. Lock和synchronized的选择

    学习资源:http://www.cnblogs.com/dolphin0520/p/3923167.html 一.java.util.concurrent.locks包下常用的类 1.Lock pub ...

  9. 【转载】Java方向如何准备BAT技术面试答案(汇总版)

    作者:微信公众号JavaQ 链接:https://www.nowcoder.com/discuss/31667?type=0&order=0&pos=11&page=1 来源: ...

  10. Java并发编程 基础知识学习总结

    Java并发编程一直是Java程序员必须懂但又是很难懂的技术内容,这部分的内容我也是反复学习了好几遍才能理解.本篇博客梳理一下最近从<Java 并发编程的艺术>和他人的博客学习Java并发 ...

最新文章

  1. Linq初级班 Linq To XML体验(基础篇)
  2. UIView层次管理(sendSubviewToBack,bringSubviewToFront)
  3. python调用函数出现未定义_python – 为什么函数参数之外的“self”会给出“未定义”的错误?...
  4. linux基础命令学习(五)目录或文件权限
  5. Make it run, make it right, make it fast
  6. 替换a链接的href和title
  7. Go的sync.Cond(四)
  8. 推荐一款生成SQL插入语句的软件
  9. Linux 主要的发行系统版本介绍
  10. 我们公司也实行了OKR
  11. CDH6 kafka如何彻底删除topic及数据
  12. Python使用for循环打印直角三角形
  13. Visio—如何画虚线?
  14. GitHub正则表达式学习笔记
  15. mysql查询表字段的个数
  16. mysql 循环语句语法_MySQL循环语句之while循环测试
  17. Qt_编辑器配色方案
  18. 气象数据产品下载网址汇总
  19. 高频电子线路实验 02 - | 振荡电路与调幅检波
  20. 小米之家真的是有效的渠道模式么?

热门文章

  1. Docker系列 WordPress系列 WordPress上传或更新Markdown的最佳实践
  2. Qt之Q_OBJECT
  3. Android N 的新特性
  4. Neo4j 图数据科学应用 - 图目录和图投影(二)
  5. 建设银行上海住房公积金业务网点
  6. U3D教程多摄像机协同运行
  7. EXCEL高级玩法之非常酷炫的动态数据分析报表
  8. Julia学习笔记(一)——入门
  9. 修改Worldpress主题的Footer/Header部分
  10. C++输入一串数值,逗号隔开,回车结束