山东大学软件学院项目实训-创新实训-基于大模型的旅游平台(十八)- JUC(4)

线程安全分析

成员变量和静态变量是否线程安全?

  1. 如果它们没有共享,则线程安全

  2. 如果它们被共享了,根据它们的状态是否能够改变,又分两种情况

    如果只有读操作,则线程安全如果有读写操作,则这段代码是临界区,需要考虑线程安全共享且有读写操作不一定安全。

局部变量是否线程安全?

局部变量是线程安全的

但局部变量引用的对象则未必

​ 如果该对象没有逃离方法的作用访问,它是线程安全的

​ 如果该对象逃离方法的作用范围,需要考虑线程安全

常见线程安全类

  1. Integer

  2. HashTable

  3. String

  4. Rando

  5. Vector

  6. JUC下的类

它们单个方法是线程安全的,但是如果多个方法组合的时候就不一样了。下面的代码出现的问题就是线程1判断成功之后切换,刚好释放了锁,然后就是线程2获取锁进行判断,再次切换线程1获取锁处理put,切换线程2也可以获取锁处理put。因为单个方法执行完就会释放锁。所以这样还是需要整体上加锁才能够继续处理

不可变类线程安全

StringString和Integer都是不可变的,String本质上就是一个char[]数组。如果是substring方法实际上就是复制一个新的数组出来,然后再给String的char数组进行赋值。replace也实际上只是创建数组,然后对比原数组的旧值,如果是旧值那么直接给新的数组的那个位置赋值新值

卖票案例

  
  @Slf4j(topic = "c.ExerciseSell")
  public class Test {
      public static void main(String[] args) throws InterruptedException {
          // 模拟多人买票
          TicketWindow window = new TicketWindow(1000);
  ​
          // 所有线程的集合
          List<Thread> threadList = new ArrayList<>();
  ​
          // 卖出的票数统计
          List<Integer> amountList = new Vector<>();
  ​
          for (int i = 0; i < 2000; i++) {
              Thread thread = new Thread(() -> {
                  // 买票
                  try {
                      Thread.sleep(random(10));
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  int amount = window.sell(random(5));
                  // 统计买票数
                  amountList.add(amount);
              });
              threadList.add(thread);
              thread.start();
          }
  ​
          // 等所有线程执行结束
          for (Thread thread : threadList) {
              thread.join();
          }
  ​
          // 统计卖出的票数和剩余票数
          log.debug("余票:{}",window.getCount());
          log.debug("卖出的票数:{}", amountList.stream().mapToInt(i-> i).sum());
      }
  ​
      // Random 为线程安全
      static Random random = new Random();
  ​
      // 产生随机 1~5
      public static int random(int amount) {
          return random.nextInt(amount) + 1;
      }
  }
  ​
  // 售票窗口
  class TicketWindow {
      private int count;
  ​
      public TicketWindow(int count) {
          this.count = count;
      }
  ​
      // 获取余票数量
      public int getCount() {
          return count;
      }
  ​
      // 售票 synchronized
      public  int sell(int amount) {
          if (this.count >= amount) {
              this.count -= amount;
              return amount;
          } else {
              return 0;
          }
      }
  }

解决 :

  
      // 售票 synchronized
      public synchronized int sell(int amount) {
          if (this.count >= amount) {
              this.count -= amount;
              return amount;
          } else {
              return 0;
          }
      }

转账案例

  
  @Slf4j(topic = "c.ExerciseTransfer")
  public class ExerciseTransfer {
      public static void main(String[] args) throws InterruptedException {
          Account a = new Account(1000);
          Account b = new Account(1000);
          Thread t1 = new Thread(() -> {
              for (int i = 0; i < 1000; i++) {
                  a.transfer(b, randomAmount());
              }
          }, "t1");
          Thread t2 = new Thread(() -> {
              for (int i = 0; i < 1000; i++) {
                  b.transfer(a, randomAmount());
              }
          }, "t2");
          t1.start();
          t2.start();
          t1.join();
          t2.join();
          // 查看转账2000次后的总金额
          log.debug("total:{}", (a.getMoney() + b.getMoney()));
      }
  ​
      // Random 为线程安全
      static Random random = new Random();
  ​
      // 随机 1~100
      public static int randomAmount() {
          return random.nextInt(100) + 1;
      }
  }
  ​
  // 账户
  class Account {
      private int money;
  ​
      public Account(int money) {
          this.money = money;
      }
  ​
      public int getMoney() {
          return money;
      }
  ​
      public void setMoney(int money) {
          this.money = money;
      }
  ​
      // 转账
      public void transfer(Account target, int amount) {
          synchronized(Account.class) {
              if (this.money >= amount) {
                  this.setMoney(this.getMoney() - amount);
                  target.setMoney(target.getMoney() + amount);
              }
          }
      }
  }

Moniter

Java对象头

包括了markword主要就是存储hashcode,age(gc生命值),biase_lock是不是偏向锁,01加锁的情况

还有就是klassword只要就是指向类对象(类的信息)。

如果是数组那么就还包括了数组的长度。

Moniter(锁)

Thread1此时进入阻塞队列

工作原理

实际上就是把obj的markword前面30bit记录monitor的地址,指向monitor。然后如果有线程要执行临时区的时候那么就把monitor的owner指向对应的线程。如果又有线程进来,那么会看看obj是否关联锁,然后再看看锁是否有owner,如果有那么就进入到EntryList阻塞等待。等待线程释放锁之后,唤醒entryList然后重新开始竞争。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值