Java多线程编程(2)-锁机制

1.互斥和同步

    线程锁机制是为了线程间通信的互斥问题,这里我们先说说什么是互斥和同步。

     互斥: 线程间的间接制约关系。线程A和线程B的执行都需要资源C,当线程A持有资源C的时候,线程B需要等到线程A执行完毕、释放资源C以后才能执行,这就是互斥关系。

     同步:线程间的直接制约关系。线程A依赖于线程B,只有等B执行了某操作以后,线程A才能执行。

     java的锁机制是通过synchronized关键字来实现的,它包括synchronized方法和synchronized块两种方式。

2.synchronized方法

     通过在方法声明中加入synchronized关键字来声明synchronized方法,语法如下。

public synchronized void process(){}

      当多个线程访问同一个synchronized方法的时候,必须获取调用该方法的类实例的锁才能执行,否则所属线程被阻塞。方法一旦执行,就独占该锁,直到该方法返回才把锁释放。此后,其它被阻塞的线程才能获得锁,并执行。

      这种机制,在同一时刻,对于同一个类实例,有且仅有一个声明为synchronized的成员方法被执行,不管这个类有多少synchronized方法。这带来了一个缺陷,若将一个运行时间较长的方法声明为synchronized,并且被调用,那么本类中其它的synchronized方法就不能运行。

      Java提供了synchronized块来规避这个问题。


3.synchronized块

       通过 synchronized 关键字来声明synchronized 块,语法如下:

synchronized(syncObject) {
//允许访问控制的代码
}

     synchronized 块必须获得syncObject对象的锁才能执行。由于可以针对任意代码块设置不同的上锁对象,因此  synchronized 块具有更高的灵活性。

      下面给出一个代码示例,可以通过注释掉synchronized 来观察效果。

4.示例代码

      过年的时候大家都玩发红包,这里我们就以红包来做个示例,一共3个类,分别是

        1.红包类(GiftMoney),包含了钱包数组,和钱包之间互相转账发红包的方法、获得所有钱包货币总额的方法。去掉synchronized后,可以通过观察控制台中输出的货币总额来体会synchronized的作用。

        2.红包任务类(MoneyTransferTask),实现Runnable接口,调用发红包的方法。

        3.测试类(Main),创建多个线程,执行发红包。

GiftMoney.java

/**
 * 红包系统, 过年了,大家都互相送红包(GiftMoney)。
 * 
 * 所有红包中的货币总量是不变的。
 *
 * * 红包不会凭空创造或者消息,只会从一个人发给另一个人。
 */
public class GiftMoney {

  // 钱包
  private final double[] wallets;
  
  private final Object lockObj = new Object();

  /**
   * 
   * @param n 钱包的数量(一个钱包就是代表一个人)
   * @param initialMoney 每个钱包中里初始的钱数
   */
  public GiftMoney(int n, double initialMoney) {
    wallets = new double[n];
    for (int i = 0; i < wallets.length; i++) {
      wallets[i] = initialMoney;
    }
  }

  /**
   * 发红包,钱从一个钱包转到另一个钱包
   * 
   * @param from 发红包的人
   * @param to 收红包的人
   * @param amount 红包钱数
   */
  public void transfer(int from, int to, double amount) {

    synchronized (lockObj) {
      while (wallets[from] < amount) {
        try {
          // 条件不满足,当前任务被放入Wait Set,释放CPU资源
          lockObj.wait();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
      System.out.print(Thread.currentThread().getName());
      wallets[from] -= amount;
      System.out.printf("\t %d给%d发了%10.2f的红包。", from, to, amount);
      wallets[to] += amount;
      System.out.printf("货币总量:\t %10.2f%n", getTotalMoney());

      // 唤醒lockObj上的所有等待线程
      lockObj.notifyAll();
    }
  }

  /**
   * 返回货币总量
   */
  public double getTotalMoney() {
    double sum = 0;
    for (int i = 0; i < wallets.length; i++) {
      sum += wallets[i];
    }
    return sum;
  }

  /**
   * 获得钱包的总数
   */
  public int getWalletsLength() {
    return wallets.length;
  }

}


MoneyTransferTask.java

public class MoneyTransferTask implements Runnable {

  // 红包系统
  private GiftMoney gm;

  // 红包来源的钱包index
  private int fromWallet;

  // 每次可以发的红包金额最大值
  private double maxAmount;

  public MoneyTransferTask(GiftMoney gm, int from, double max) {
    this.gm = gm;
    this.fromWallet = from;
    this.maxAmount = max;
  }

  public void run() {
    try {
      while (true) {
        //随机获取到收红包的toWallet, 随机产生红包中的金额amount
        int toWallet = (int) (gm.getWalletsLength() * Math.random());
        double amount = maxAmount * Math.random();
        //发红包
        gm.transfer(fromWallet, toWallet, amount);
        Thread.sleep(3000);
      }
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}


Main.java

public class Main{
  // 一共100个钱包
  public static final int WALLET_AMOUNT = 100;
  // 每个钱包中的初始金额是1000元
  public static final double INITIAL_AMOUNT = 1000;

  public static void main(String[] args) {
    GiftMoney gm = new GiftMoney(WALLET_AMOUNT, INITIAL_AMOUNT);
    for (int i = 0; i < WALLET_AMOUNT; i++) {
      MoneyTransferTask mtt = new MoneyTransferTask(gm, i, INITIAL_AMOUNT);
      Thread t = new Thread(mtt, "TransferThread_" + i);
      t.start();
    }
  }
}












你可能感兴趣的:(java,synchronized,同步,互斥,锁机制)