素材巴巴 > 程序开发 >

Java线程(八):锁对象Lock-同步问题更完美的处理方式

程序开发 2023-09-19 09:46:58

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


 Lock是java.util.concurrent.locks包下的接口,Lock 实现提供了比使用synchronized 方法和语句可获得的更广泛的锁定操作,它能以更优雅的方式处理线程同步问题,我们拿Java线程(二)中的一个例子简单的实现一下和sychronized一样的效果,代码如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class LockTest {  
  2.     public static void main(String[] args) {  
  3.         final Outputter1 output = new Outputter1();  
  4.         new Thread() {  
  5.             public void run() {  
  6.                 output.output("zhangsan");  
  7.             };  
  8.         }.start();        
  9.         new Thread() {  
  10.             public void run() {  
  11.                 output.output("lisi");  
  12.             };  
  13.         }.start();  
  14.     }  
  15. }  
  16. class Outputter1 {  
  17.     private Lock lock = new ReentrantLock();// 锁对象  
  18.     public void output(String name) {  
  19.         // TODO 线程输出方法  
  20.         lock.lock();// 得到锁  
  21.         try {  
  22.             for(int i = 0; i < name.length(); i++) {  
  23.                 System.out.print(name.charAt(i));  
  24.             }  
  25.         } finally {  
  26.             lock.unlock();// 释放锁  
  27.         }  
  28.     }  
  29. }  
        这样就实现了和sychronized一样的同步效果,需要注意的是,用sychronized修饰的方法或者语句块在代码执行完之后锁自动释放,而用Lock需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区放在try内,释放锁放在finally内。

        如果说这就是Lock,那么它不能成为同步问题更完美的处理方式,下面要介绍的是读写锁(ReadWriteLock),我们会有一种需求,在对数据进行读写的时候,为了保证数据的一致性和完整性,需要读和写是互斥的,写和写是互斥的,但是读和读是不需要互斥的,这样读和读不互斥性能更高些,来看一下不考虑互斥情况的代码原型:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class ReadWriteLockTest {  
  2.     public static void main(String[] args) {  
  3.         final Data data = new Data();  
  4.         for (int i = 0; i < 3; i++) {  
  5.             new Thread(new Runnable() {  
  6.                 public void run() {  
  7.                     for (int j = 0; j < 5; j++) {  
  8.                         data.set(new Random().nextInt(30));  
  9.                     }  
  10.                 }  
  11.             }).start();  
  12.         }         
  13.         for (int i = 0; i < 3; i++) {  
  14.             new Thread(new Runnable() {  
  15.                 public void run() {  
  16.                     for (int j = 0; j < 5; j++) {  
  17.                         data.get();  
  18.                     }  
  19.                 }  
  20.             }).start();  
  21.         }  
  22.     }  
  23. }  
  24. class Data {      
  25.     private int data;// 共享数据      
  26.     public void set(int data) {  
  27.         System.out.println(Thread.currentThread().getName() + "准备写入数据");  
  28.         try {  
  29.             Thread.sleep(20);  
  30.         } catch (InterruptedException e) {  
  31.             e.printStackTrace();  
  32.         }  
  33.         this.data = data;  
  34.         System.out.println(Thread.currentThread().getName() + "写入" + this.data);  
  35.     }     
  36.     public void get() {  
  37.         System.out.println(Thread.currentThread().getName() + "准备读取数据");  
  38.         try {  
  39.             Thread.sleep(20);  
  40.         } catch (InterruptedException e) {  
  41.             e.printStackTrace();  
  42.         }  
  43.         System.out.println(Thread.currentThread().getName() + "读取" + this.data);  
  44.     }  
  45. }  
        部分输出结果:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. Thread-1准备写入数据  
  2. Thread-3准备读取数据  
  3. Thread-2准备写入数据  
  4. Thread-0准备写入数据  
  5. Thread-4准备读取数据  
  6. Thread-5准备读取数据  
  7. Thread-2写入12  
  8. Thread-4读取12  
  9. Thread-5读取5  
  10. Thread-1写入12  
        我们要实现写入和写入互斥,读取和写入互斥,读取和读取互斥,在set和get方法加入sychronized修饰符:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public synchronized void set(int data) {...}      
  2. public synchronized void get() {...}  
        部分输出结果:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. Thread-0准备写入数据  
  2. Thread-0写入9  
  3. Thread-5准备读取数据  
  4. Thread-5读取9  
  5. Thread-5准备读取数据  
  6. Thread-5读取9  
  7. Thread-5准备读取数据  
  8. Thread-5读取9  
  9. Thread-5准备读取数据  
  10. Thread-5读取9  
        我们发现,虽然写入和写入互斥了,读取和写入也互斥了,但是读取和读取之间也互斥了,不能并发执行,效率较低,用读写锁实现代码如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. class Data {      
  2.     private int data;// 共享数据  
  3.     private ReadWriteLock rwl = new ReentrantReadWriteLock();     
  4.     public void set(int data) {  
  5.         rwl.writeLock().lock();// 取到写锁  
  6.         try {  
  7.             System.out.println(Thread.currentThread().getName() + "准备写入数据");  
  8.             try {  
  9.                 Thread.sleep(20);  
  10.             } catch (InterruptedException e) {  
  11.                 e.printStackTrace();  
  12.             }  
  13.             this.data = data;  
  14.             System.out.println(Thread.currentThread().getName() + "写入" + this.data);  
  15.         } finally {  
  16.             rwl.writeLock().unlock();// 释放写锁  
  17.         }  
  18.     }     
  19.     public void get() {  
  20.         rwl.readLock().lock();// 取到读锁  
  21.         try {  
  22.             System.out.println(Thread.currentThread().getName() + "准备读取数据");  
  23.             try {  
  24.                 Thread.sleep(20);  
  25.             } catch (InterruptedException e) {  
  26.                 e.printStackTrace();  
  27.             }  
  28.             System.out.println(Thread.currentThread().getName() + "读取" + this.data);  
  29.         } finally {  
  30.             rwl.readLock().unlock();// 释放读锁  
  31.         }  
  32.     }  
  33. }  

        部分输出结果:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. Thread-4准备读取数据  
  2. Thread-3准备读取数据  
  3. Thread-5准备读取数据  
  4. Thread-5读取18  
  5. Thread-4读取18  
  6. Thread-3读取18  
  7. Thread-2准备写入数据  
  8. Thread-2写入6  
  9. Thread-2准备写入数据  
  10. Thread-2写入10  
  11. Thread-1准备写入数据  
  12. Thread-1写入22  
  13. Thread-5准备读取数据  

        从结果可以看出实现了我们的需求,这只是锁的基本用法,锁的机制还需要继续深入学习。

        本文来自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/7461369,转载请注明。

25
2

我的同类文章

  更多文章
猜你在找
Java面向对象编程(高手养成记)
JavaSE之多线程实战视频课程
Java基础核心技术:多线程(day16-day17)
ArcGIS for javascript 项目实战(环境监测系统)
ArcGIS for JavaScript
锁对象Lock-同步问题更完美的处理方式
JNI_编程技术__网文整理
JNI详解---从不懂到理解
Jni函数调用
JNI 编程文库
查看评论
8楼  guzhichongshi 2017-01-18 11:48发表 [回复]
你好,我想请问个锁的问题,现在有一个类a,实例化出了多个对象A,B,C,D……每个对象都是被多线程操作,单独操作一个对象的时候加锁住对象即可,但如果会同时操作多个对象要如何处理
Re:  三千分之一的爱 2017-03-13 10:41发表 [回复]
回复guzhichongshi:锁住类
synchronized (a.class) {
}
7楼  pandajava 2016-05-31 14:15发表 [回复]
学习学习学习学习学习
6楼  思念叨火车 2016-03-04 14:00发表 [回复]
写的很好,受益良多。
但有一个疑惑:
文章中间例子提到:”我们要实现写入和写入互斥,读取和写入互斥,读取和读取互斥,在set和get方法加入sychronized修饰符“
只是这样操作就能做到读和写互斥吗?
能否解释一下?
Re:  张君君 2017-03-11 21:25发表 [回复]
[reply]liyuchong2537631[/reply

已经调了一个对象的synchronize方法,再调用另一synchronize方法不可以,再调用另一非synchronize方法可以,再调用另一synchronize static方法可以
Re:  三千分之一的爱 2016-03-05 07:43发表 [回复]
回复思念叨火车:synchronized锁的是对象,而不是某个方法。
5楼  秦子 2016-01-25 21:28发表 [回复]
请问, private int data. 这个属性不加volatile关键字会不会有问题啊
Re:  三千分之一的爱 2016-01-27 08:48发表 [回复]
回复秦子:不会
4楼  zsjxq_gg 2015-06-05 10:14发表 [回复]
你举的最后一个例子里面,线程写的数据不一定能被读到,怎么保证线程写的数据一定能被另外一个线程读到呢
Re:  三千分之一的爱 2015-06-06 14:33发表 [回复]
回复zsjxq_gg:肯定要先写入才能读到
3楼  程序员小董 2015-01-12 21:51发表 [回复]
说实话 今天在你的并发专栏里 看的头都大了
一下子看的太多了 接受不了了
但我得说 你写的很好 已关注
Re:  三千分之一的爱 2015-01-16 11:21发表 [回复]
回复程序员小董:呵呵,循序渐进
2楼  diquren 2013-12-11 16:54发表 [回复]
get()方法里面,不写rwl.readLock().lock()也可以达到同样并发的效果吧
Re:  三千分之一的爱 2013-12-12 07:48发表 [回复]
回复diquren:读和写也需要互斥。
Re:  diquren 2013-12-13 15:52发表 [回复]
回复三千分之一的爱:嗯明白了,有没办法,让方法同步,又是并发(不互斥)。比如:A线程调用了 对象obj的 get()的同步方法,同时B线程也可以调用对象obj的hello()同步方法。
Re:  三千分之一的爱 2013-12-16 12:24发表 [回复]
回复diquren:用本文提到的读写锁就可以实现你说的这个效果啊,传统的synchronized是不行的。
Re:  diquren 2013-12-17 12:18发表 [回复]
回复三千分之一的爱:比如你文章最后输出:Thread-4准备读取数据 Thread-3准备读取数据 Thread-5准备读取数据 读与读并不是同步的。一个同步方法用一个单独的Lock对象(两个同步,就要两个Lock),我试了,可以达到效果。
1楼  nuonuodaodao 2012-04-14 22:35发表 [回复]
顶,并发包还没掌握
Re:  三千分之一的爱 2012-04-14 22:38发表 [回复]
回复nuonuodaodao:呵呵,我也是初步学习。
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
核心技术类目
全部主题  Hadoop  AWS  移动游戏  Java  Android  iOS  Swift  智能硬件  Docker  OpenStack VPN  Spark  ERP  IE10  Eclipse  CRM  JavaScript  数据库  Ubuntu  NFC  WAP  jQuery BI  HTML5  Spring  Apache  .NET  API  HTML  SDK  IIS  Fedora  XML  LBS  Unity Splashtop  UML  components  Windows Mobile  Rails  QEMU  KDE  Cassandra  CloudStack FTC  coremail  OPhone  CouchBase  云计算  iOS6  Rackspace  Web App  SpringSide  Maemo Compuware  大数据  aptech  Perl  Tornado  Ruby  Hibernate  ThinkPHP  HBase  Pure  Solr Angular  Cloud Foundry  Redis  Scala  Django  Bootstrap
  • 原创:95篇
  • 转载:7篇
  • 译文:3篇
  • 评论:906条
  • Design Pattern

    文章:6篇

    阅读:29784 Java线程

    文章:15篇

    阅读:628092


标签:

上一篇: [前端]JavaScript图片切换 下一篇:
素材巴巴 Copyright © 2013-2021 http://www.sucaibaba.com/. Some Rights Reserved. 备案号:备案中。