Java线程同步Lock同步锁代码示例

作者:超超boy 时间:2023-11-17 13:23:54 

java线程同步原理

java会为每个object对象分配一个monitor,当某个对象的同步方法(synchronizedmethods)被多个线程调用时,该对象的monitor将负责处理这些访问的并发独占要求。

当一个线程调用一个对象的同步方法时,JVM会检查该对象的monitor。如果monitor没有被占用,那么这个线程就得到了monitor的占有权,可以继续执行该对象的同步方法;如果monitor被其他线程所占用,那么该线程将被挂起,直到monitor被释放。

当线程退出同步方法调用时,该线程会释放monitor,这将允许其他等待的线程获得monitor以使对同步方法的调用执行下去。

注意:Java对象的monitor机制和传统的临界检查代码区技术不一样。java的一个同步方法并不意味着同时只有一个线程独占执行,但临界检查代码区技术确实会保证同步方法在一个时刻只被一个线程独占执行。Java的monitor机制的准确含义是:任何时刻,对一个指定object对象的某同步方法只能由一个线程来调用。

java对象的monitor是跟随object实例来使用的,而不是跟随程序代码。两个线程可以同时执行相同的同步方法,比如:一个类的同步方法是xMethod(),有a,b两个对象实例,一个线程执行a.xMethod(),另一个线程执行b.xMethod().互不冲突。

Lock-同步锁

Lock是java5提供的一个强大的线程同步机制--通过显示定义同步锁对象来实现同步。Lock可以显示的加锁、解锁。每次只能有一个线程对lock对象加锁。

Lock有ReadLock、WriteLock、ReentrantLock(可重入锁)

常用的就是ReentrantLock。代码如下:

代码逻辑:Account账户类,实现取钱的同步方法、DrawThread取钱的线程

Account:


package lock.reentrantlock2;
import java.util.concurrent.locks.*;
/**
*账户类,需保持同步
*/
public class Account
{
 //定义锁对象
 private final ReentrantLock lock = new ReentrantLock();
 private String accountNo;
 private double balance;
 public Account(){}
 public Account(String accountNo , double balance)
 {
   this.accountNo = accountNo;
   this.balance = balance;
 }
 public void setAccountNo(String accountNo)
 {
   this.accountNo = accountNo;
 }
 public String getAccountNo()
 {
    return this.accountNo;
 }
 public double getBalance()
 {
    return this.balance;
 }
 public void draw(double drawAmount)
 {
   lock.lock();
   try
   {
     //账户余额大于取钱数目
     if (balance >= drawAmount)
     {
       //吐出钞票
       System.out.println(Thread.currentThread().getName() +
         "取钱成功!吐出钞票:" + drawAmount);
       try
       {
         Thread.sleep(1);      
       }
       catch (InterruptedException ex)
       {
         ex.printStackTrace();
       }
       //修改余额
       balance -= drawAmount;
       System.out.println("\t余额为: " + balance);
     }
     else
     {
       System.out.println(Thread.currentThread().getName() +
         "取钱失败!余额不足!");
     }    
   }
   finally
   {
     lock.unlock();
   }
 }
 public int hashCode()
 {
   return accountNo.hashCode();
 }
 public boolean equals(Object obj)
 {
   if (obj != null && obj.getClass() == Account.class)
   {
     Account target = (Account)obj;
     return target.getAccountNo().equals(accountNo);
   }
   return false;
 }
}

DrawThread:


package lock.reentrantlock2;
/**
* 调用account取钱
*
*/
public class DrawThread extends Thread
{
 //模拟用户账户
 private Account account;
 //当前取钱线程所希望取的钱数
 private double drawAmount;
 public DrawThread(String name , Account account ,
   double drawAmount)
 {
   super(name);
   this.account = account;
   this.drawAmount = drawAmount;
 }
 //当多条线程修改同一个共享数据时,将涉及到数据安全问题。
 public void run()
 {
   account.draw(drawAmount);
 }
}

TestDraw:


package lock.reentrantlock2;
/**
*/
public class TestDraw
{
 public static void main(String[] args)
 {
   //创建一个账户
   Account acct = new Account("1234567" , 1000);
   //模拟两个线程对同一个账户取钱
   new DrawThread("甲" , acct , 800).start();
   new DrawThread("乙" , acct , 800).start();
 }
}

运行结果:

甲取钱成功!吐出钞票:800.0 
余额为:200.0 
乙取钱失败!余额不足! 

使用Lock同步与同步方法很相似,都是“加锁--修改公共变量--释放锁”的模式,代码很容易看懂。两个线程对应一个Account对象,保证了两个线程对应一个lock对象,保证了同一时刻只有一个线程进入临界区。Lock还包含太容易Lock(),以及试图获取可中断锁的lockInterruptibly(),获取超时失效锁的tryLock(long,TimeUnit)等方法。

ReentrantLock锁具有可重入性可以对已被加锁的ReentrantLock锁再次加锁,线程每次调用lock()加锁后,必须显示的调用unlock来释放锁,有几个lock就对应几个unlock。还有把unlock放在finally代码块中,Lock在发生异常时也是不释放锁的,所以在finally中释放更安全。

总结

创建并运行一个java线程方法介绍

Java编程之多线程死锁与线程间通信简单实现代码

Java多线程编程小实例模拟停车场系统

有什么问题可以随时留言,小编会及时回复大家的。感谢朋友们对本站的支持!

来源:http://www.cnblogs.com/jycboy/p/5623113.html

标签:java,线程同步,lock
0
投稿

猜你喜欢

  • IntelliJ IDEA最佳配置(推荐)

    2023-11-17 01:55:44
  • Android精灵动画用法实例

    2023-08-22 22:20:57
  • java web上传文件和下载文件代码片段分享

    2023-10-04 19:50:50
  • 在mybatis中使用mapper进行if条件判断

    2023-08-01 08:09:34
  • 详解Java编程中JavaMail API的使用

    2022-08-02 06:18:23
  • 七个Spring核心模块详解

    2021-07-25 15:32:18
  • C/C++ Qt 基本文件读写的基本使用(2种实现)

    2021-09-17 05:15:36
  • Java调用Oracle存储过程详解

    2022-09-09 03:19:45
  • 关于Flyweight模式应用实践的相关介绍

    2021-07-28 21:50:34
  • c++与c#的时间转换示例分享

    2022-08-22 07:21:40
  • 基于多网卡环境下Eureka服务注册IP的选择问题

    2022-09-16 17:59:15
  • Java Swing实现JTable检测单元格数据变更事件的方法示例

    2022-10-16 19:49:29
  • java中LinkedList使用迭代器优化移除批量元素原理

    2021-12-05 11:26:07
  • 基于Luhn算法的银行卡校验规则

    2022-06-05 00:56:15
  • C#对二进制数据进行base64编码的方法

    2023-09-02 23:26:12
  • 关于Java反编译字节码文件

    2021-10-07 01:41:32
  • Flutter 自定义Drawer 滑出位置的大小实例代码详解

    2021-09-18 23:18:19
  • Android图片添加水印图片并把图片保存到文件存储的实现代码

    2022-06-03 21:48:54
  • java 单例模式的实例详解

    2023-10-08 05:14:08
  • ViewDragHelper实现QQ侧滑效果

    2022-12-25 23:26:54
  • asp之家 软件编程 m.aspxhome.com