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.  
  8. }finally{
  9. lock.unlock(); //释放锁
  10. }
  1. // tryLock()的使用
  2. Lock lock = ...;
  3. if(lock.tryLock()) {
  4. try{
  5. //处理任务
  6. }catch(Exception ex){
  7.  
  8. }finally{
  9. lock.unlock(); //释放锁
  10. }
  11. }else {
  12. //如果不能获取锁,则直接做其他事情
  13. }
  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.  
  7. new Thread(){
  8. public void run() {
  9. test.insert(Thread.currentThread());
  10. };
  11. }.start();
  12.  
  13. new Thread(){
  14. public void run() {
  15. test.insert(Thread.currentThread());
  16. };
  17. }.start();
  18. }
  19.  
  20. public void insert(Thread thread) {
  21. lock.lock();
  22. try {
  23. System.out.println(thread.getName()+"得到了锁");
  24. for(int i=0;i<5;i++) {
  25. arrayList.add(i);
  26. }
  27. } catch (Exception e) {
  28. // TODO: handle exception
  29. }finally {
  30. System.out.println(thread.getName()+"释放了锁");
  31. lock.unlock();
  32. }
  33. }
  34. }

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

3. ReadWriteLock

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

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

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

【java多线程】ConCurrent并发包 - Lock详解的更多相关文章

  1. ConCurrent并发包 - Lock详解(转)

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

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

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

  3. 【Java多线程】Executor框架的详解

    在Java中,使用线程来异步执行任务.Java线程的创建与销毁需要一定的开销,如果我们为每一个任务创建一个新线程来执行,这些线程的创建与销毁将消耗大量的计算资源.同时,为每一个任务创建一个新线程来执行 ...

  4. Java多线程Callable和Future类详解

         public interface Callable<V>    返回结果并且可能抛出异常的任务.实现者定义了一个不带任何参数的叫做 call 的方法      public in ...

  5. Java线程创建形式 Thread构造详解 多线程中篇(五)

    Thread作为线程的抽象,Thread的实例用于描述线程,对线程的操纵,就是对Thread实例对象的管理与控制. 创建一个线程这个问题,也就转换为如何构造一个正确的Thread对象. 构造方法列表 ...

  6. Java并发:多线程和java.util.concurrent并发包总结

    多线程和java.util.concurrent并发包 转载:

  7. java的集合框架最全详解

    java的集合框架最全详解(图) 前言:数据结构对程序设计有着深远的影响,在面向过程的C语言中,数据库结构用struct来描述,而在面向对象的编程中,数据结构是用类来描述的,并且包含有对该数据结构操作 ...

  8. java线程池的使用与详解

    java线程池的使用与详解 [转载]本文转载自两篇博文:  1.Java并发编程:线程池的使用:http://www.cnblogs.com/dolphin0520/p/3932921.html   ...

  9. Java多线程--JDK并发包(2)

    Java多线程--JDK并发包(2) 线程池 在使用线程池后,创建线程变成了从线程池里获得空闲线程,关闭线程变成了将线程归坏给线程池. JDK有一套Executor框架,大概包括Executor.Ex ...

随机推荐

  1. 商品的spu、sku及其之间的关系

    今日来总结一下,电商系统中涉及到商品时必然会遇到的几个概念,SPU.SKU.单品等.彻底搞懂和明白了这几个概念对我们设计商品表是十分必要的前提条件. SPU:标准化产品单元 SPU = Standar ...

  2. DB杂记

    1. mybatits 批量插入: <insert id="insertColumnitem2"> INSERT INTO REPORT_COLUMNITEM (COL ...

  3. selenium-python读取XML文件

    首先这是我们要读取的XML文件 <?xml version="1.0" encoding="utf-8" ?><info> <ba ...

  4. HTTP从入门到入土(4)——URI、URL和URN

    URI URI全称:Uniform Resource Identifier,中文名为统一资源标识符.用来标识唯一标识互联网上的信息资源. Web上可用的所有资源,比如html.图像.视频等,都是由UR ...

  5. 《Effective Java 2nd》第7章 方法

    目录 第38条 检查参数的有效性 第39条 必要时进行保护性拷贝 第40条 谨慎设计方法签名 第41条 慎用重载 第42条 慎用可变参数 第43条 返回零长度的数组或集合,而不是null 第44条 为 ...

  6. Vmware 设置NAT模式

    NAT模式,就是让虚拟系统借助NAT(网络地址转换)功能,通过宿主机器所在的网络来访问公网.也就是说,使用NAT模式可以实现在虚拟系统里访问互联网. NAT模式下的虚拟系统的TCP/IP配置信息是由V ...

  7. supervisor安装与问题

    [转]安装supervisor以及可能碰到的问题 单击此处查看原文 supervisor作为一个进程管理的python软件非常的给力 但是一不小心就会遇到一些问题 就比如下面这个: unix:///v ...

  8. [Opencv]图像的梯度与边缘检测(转)

    文章来源:https://blog.csdn.net/on2way/article/details/46851451 梯度简单来说就是求导,在图像上表现出来的就是提取图像的边缘(不管是横向的.纵向的. ...

  9. 【eclipse】查看jar乱码问题解决

  10. LightOJ 1027 A Dangerous Maze(期望)

    https://cn.vjudge.net/problem/LightOJ-1027 题意:有n扇门,每扇门有个时间ti,选择正数的门可以在ti后带你走出迷宫,负数的门会在ti后带你回到起点,然后重新 ...