synchronized的缺陷

 
我们知道,可以利用synchronized关键字来实现共享资源的互斥访问。Java 5在java.util.concurrent.locks包下提供了另一种来实现线程的同步访问,那就是Lock。既然有了synchronized来实现线程同步,Java为什么还需要提供Lock呢?
synchronized是Java的一个关键字,当我们使用synchronized来修饰方法或代码块时,线程必须先获得对应的锁才能执行该段代码。而其他线程只能一直等待,直到当前线程释放锁并获得对应的锁才能进入该段代码。这里获取锁的线程释放锁只会有两种情况:
  • 获取锁的线程执行完该段代码,线程会释放占有的锁;
  • 线程执行发生异常,此时JVM会让线程自动释放锁。
那么如果这个占有锁的线程由于等待IO或其他原因(比如调用sleep方法)被阻塞,但是还没有释放锁,那么其他线程只能干巴巴的等着,试想这多么影响程序的执行效率。
当多个线程同时读写文件是,我们知道读操作和写操作会发生冲突,写操作和写操作也会发生冲突,但是读操作和读操作之间不会冲突。synchronized关键字对一段代码加锁,所有的线程必须先获得对应的锁才有该代码段的执行权限。如果多个线程同时进行读操作时,使用synchronized关键字会导致在任何时刻只有一个线程读,其他线程等待,大大降低执行效率。
 
Lock可以对以上种种情况作优化,提供更好的执行效率。另外,Lock方便了对锁的管理,可以自由的加锁和释放锁,还可以判断有没有成功获取锁。但是在使用Lock时要注意,Lock需要开发者手动去释放锁,如果没有主动释放锁,就要可能导致死锁出现。建议在finally语句块中释放Lock锁。
 
 
 

concurrent.locks包下常用类

 
1. Lock
 
首先要说明的是Lock,它是一个接口:
  1. public interface Lock {
  2. void lock();
  3. void lockInterruptibly() throws InterruptedException;
  4. boolean tryLock();
  5. boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
  6. void unlock();
  7. Condition newCondition();
  8. }
  • lock()方法用来获取锁。
  • tryLock()尝试获取锁,如果成功则返回true,失败返回false(其他线程已占有锁)。这个方法会立即返回,在拿不到锁时也不会等待。
  • tryLock(long time, TimeUnit unit)方法和tryLock()方法类似,只不过在拿不到锁时等待一定的时间,如果超过等待时间还拿不到锁就返回false。
  • lockInterruptibly()方法比较特殊,当通过这个方法获取锁时,如果该线程正在等待获取锁,则它能够响应中断。也就是说,当两个线程同时通过lockInterruptibly()获取某个锁时,假如线程A获得了锁,而线程B仍在等待获取锁,那么对线程B调用interrupt()方法可以中断B的等待过程。
  1. // lock()的使用
  2. Lock lock = ...;
  3. lock.lock();
  4. try{
  5. //处理任务
  6. }catch(Exception ex){
  7. }finally{
  8. lock.unlock();   //释放锁
  9. }
  1. // tryLock()的使用
  2. Lock lock = ...;
  3. if(lock.tryLock()) {
  4. try{
  5. //处理任务
  6. }catch(Exception ex){
  7. }finally{
  8. lock.unlock();   //释放锁
  9. }
  10. }else {
  11. //如果不能获取锁,则直接做其他事情
  12. }
  1. // lockInterruptibly()的使用
  2. public void method() throws InterruptedException {
  3. lock.lockInterruptibly();
  4. try {
  5. //.....
  6. }
  7. finally {
  8. lock.unlock();
  9. }
  10. }

使用synchronized关键字,当线程处于等待锁的状态时,是无法被中断的,只能一直等待。

 
2.ReentrantLock
 
ReentrantLock是可重入锁。如果所具备可重入性,则称为可重入锁,synchronized可ReentrantLock都是可重入锁。可重入锁也叫递归锁,当一个线程已经获得该代码块的锁时,再次进入该代码块不必重新申请锁,可以直接执行。
 
例1, lock()的使用方法:
  1. public class Test {
  2. private ArrayList<Integer> arrayList = new ArrayList<Integer>();
  3. private Lock lock = new ReentrantLock();    //注意这个地方
  4. public static void main(String[] args)  {
  5. final Test test = new Test();
  6. new Thread(){
  7. public void run() {
  8. test.insert(Thread.currentThread());
  9. };
  10. }.start();
  11. new Thread(){
  12. public void run() {
  13. test.insert(Thread.currentThread());
  14. };
  15. }.start();
  16. }
  17. public void insert(Thread thread) {
  18. lock.lock();
  19. try {
  20. System.out.println(thread.getName()+"得到了锁");
  21. for(int i=0;i<5;i++) {
  22. arrayList.add(i);
  23. }
  24. } catch (Exception e) {
  25. // TODO: handle exception
  26. }finally {
  27. System.out.println(thread.getName()+"释放了锁");
  28. lock.unlock();
  29. }
  30. }
  31. }

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

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

3. ReadWriteLock

 
ReadWriteLock也是一个接口,它只定义了两个方法:
  1. public interface ReadWriteLock {
  2. /**
  3. * Returns the lock used for reading.
  4. */
  5. Lock readLock();
  6. /**
  7. * Returns the lock used for writing.
  8. */
  9. Lock writeLock();
  10. }

readLock()用来获取读锁,writeLock()用来获取写锁。也就是将文件的读写操作分开,分成两个锁来分配给线程,从而使多个线程可以同时进行读操作。ReentrantReadWriteLock是它的实现类。

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

ConCurrent并发包 - Lock详解(转)的更多相关文章

  1. 【java多线程】ConCurrent并发包 - Lock详解

    synchronized的缺陷   我们知道,可以利用synchronized关键字来实现共享资源的互斥访问. Java 5在java.util.concurrent.locks包下提供了另一种来实现 ...

  2. C++11 并发指南三(Lock 详解)(转载)

    multithreading 多线程 C++11 C++11多线程基本使用 C++11 并发指南三(Lock 详解) 在 <C++11 并发指南三(std::mutex 详解)>一文中我们 ...

  3. C++11 并发指南三(Lock 详解)

    在 <C++11 并发指南三(std::mutex 详解)>一文中我们主要介绍了 C++11 标准中的互斥量(Mutex),并简单介绍了一下两种锁类型.本节将详细介绍一下 C++11 标准 ...

  4. java.util.concurrent.atomic 类包详解

    java.util.concurrent包分成了三个部分,分别是java.util.concurrent.java.util.concurrent.atomic和java.util.concurren ...

  5. JAVA中synchronized和lock详解

         目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea.本文并不比较synchronize ...

  6. Lock详解

    在JDK1.5后,并发包里新增了Lock接口以及其实现类来实现锁功能,它提供了与synchronized关键字类似的锁功能,但它需要手动开启.关闭锁.虽然看起来没有synchronized方便,但它可 ...

  7. FLUSH TABLE WITH READ LOCK详解

    FLUSH TABLES WITH READ LOCK简称(FTWRL),该命令主要用于备份工具获取一致性备份(数据与binlog位点匹配).由于FTWRL总共需要持有两把全局的MDL锁,并且还需要关 ...

  8. MySQL Metadata Lock详解

    Metadata Lock 的作用: 要直接说出Metadata Lock 的作用.以我目前的文字功底是不行的.好在我可以通过一个例子来说明. 假设session 1 在正在执行如下的SQL语句 se ...

  9. 详解synchronized与Lock的区别与使用

    知识点 1.线程与进程 在开始之前先把进程与线程进行区分一下,一个程序最少需要一个进程,而一个进程最少需要一个线程.关系是线程–>进程–>程序的大致组成结构.所以线程是程序执行流的最小单位 ...

随机推荐

  1. MVC 从控制器将数据对象赋值给前端JS对象

    @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport&quo ...

  2. ora11g listener.ora

    配置内容方式1: LISTENER = (DESCRIPTION_LIST = (DESCRIPTION = (ADDRESS = (PROTOCOL = IPC) (KEY = EXTPROC152 ...

  3. elasticsearch批量索引数据示例

    示例数据文件document.json(index表示在索引中增加或替换现有文档,create表示如果文档不存在则添加文档,delete表示删除文档): { "index": { ...

  4. [图解算法]线性时间选择Linear Select——<递归与分治策略>

    #include <ctime> #include <iostream> using namespace std; template <class Type> vo ...

  5. vivo手机的坑-禁止微信浏览器网页点击图片,图片会自动放大

    这个坑让我郁闷极了,之前我的手机在微信浏览器打开网页点击图片时,啥事也没有 现在,咋回事,变了呢! 现在我打开微信浏览器网页面html,点击一个img标签的图片,图片会自动满屏放大,吓死宝宝了.其他人 ...

  6. vue不用webpake等环境及脚手架也可以玩

    初学时,搭环境,es6也来了,vuecil脚手架也弄了,调错,照着教程一遍一遍kei着... 然尔,实际开发中,所写的东西最后是要打包封装成软件的,为了方便其他人修改查看,不能打包成js文件... 难 ...

  7. 微软企业库5.0 学习之路——第四步、使用缓存提高网站的性能(EntLib Caching)

    首先先补习下企业库的Caching Application Block的相关知识: 1.四大缓存方式,在Caching Application Block中,主要提供以下四种保存缓存数据的途径,分别是 ...

  8. oracle导入DMP步骤

    oracle导入DMP步骤如下:1.已经存在的数据库需要进行以下的操作,如果不存在,可略过:    删除用户     drop user SUDMDB cascade;     删除表空间和数据文件 ...

  9. centos xampp 隐藏phpmyadmin地址

    /opt/lampp/etc/extra/httpd-xampp.conf Alias /phpmyadmin "/opt/xampp/phpMyAdmin/" 改为 Alias ...

  10. RecyclerView混合布局

    本来想把公司的UI图放上来,考虑到版权等未知因素,就拿网上的图来说了: 类似的这种布局,有的一行只有一张图片,有的一行有两个元素,有个一行有三个元素..就是混合的布局方式 参考文献: https:// ...