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

时间:2022-04-03 10:05:12

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:

?
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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线程同步Lock同步锁代码示例的全部内容,希望对大家有所帮助。有什么问题可以随时留言,小编会及时回复大家的。感谢朋友们对本站的支持!

原文链接:http://www.cnblogs.com/jycboy/p/5623113.html