服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - Java使用Unsafe类的示例详解

Java使用Unsafe类的示例详解

2022-01-18 16:47Dongguo丶 Java教程

java不能直接访问操作系统底层,而是通过本地方法来访问。Unsafe类提供了硬件级别的原子操作,这篇文章主要介绍了Java使用Unsafe类,需要的朋友可以参考下

Unsafe 对象提供了非常底层的,操作内存、线程的方法,相当于开了后门。

在atomic类中CAS实现、LockSupport中park unpark的底层都调用了UnSafe中的方法。

UnSafe并不是说线程不安全,而是说操作内存有可能会造成不安全问题。

当然对于开发人员来说

Unsafe 对象不能直接调用,只能通过反射获得

Java使用Unsafe类的示例详解

通过反射获得Unsafe对象

package com.dongguo.unsafe;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
* @author Dongguo
* @date 2021/9/12 0012-21:32
* @description:
*/
public class UnsafeAccessor {
  static Unsafe unsafe;
  static {
      try {
          Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
          theUnsafe.setAccessible(true);
          unsafe = (Unsafe) theUnsafe.get(null);
      } catch (NoSuchFieldException | IllegalAccessException e) {
          throw new Error(e);
      }
  }
  static Unsafe getUnsafe() {
      return unsafe;
  }

  public static void main(String[] args) {
      Unsafe unsafe = getUnsafe();
      System.out.println(unsafe);
  }
}

运行结果

sun.misc.Unsafe@7ea987ac

使用Unsafe实现 CAS 操作

package com.dongguo.unsafe;

import lombok.Data;
import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
* @author Dongguo
* @date 2021/9/12 0012-21:32
* @description:
*/
public class UnsafeAccessor {
  static Unsafe unsafe;
  static {
      try {
          Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
          theUnsafe.setAccessible(true);
          unsafe = (Unsafe) theUnsafe.get(null);
      } catch (NoSuchFieldException | IllegalAccessException e) {
          throw new Error(e);
      }
  }
  static Unsafe getUnsafe() {
      return unsafe;
  }

  public static void main(String[] args) throws NoSuchFieldException {
      Unsafe unsafe = getUnsafe();
      System.out.println(unsafe);

      Field id = Student.class.getDeclaredField("id");
      Field name = Student.class.getDeclaredField("name");
      // 获得成员变量的偏移量
      long idOffset = unsafe.objectFieldOffset(id);
      long nameOffset = unsafe.objectFieldOffset(name);
      Student student = new Student();
      // 使用 cas 方法替换成员变量的值
      unsafe.compareAndSwapInt(student, idOffset, 0, 20); // 返回 true   0为旧值 20为新值
      unsafe.compareAndSwapObject(student, nameOffset, null, "张三"); // 返回 true 旧值为null,新值为张三
      System.out.println(student);
  }
}
@Data
class Student {
  volatile int id;
  volatile String name;
}

运行结果

sun.misc.Unsafe@7ea987ac
Student(id=20, name=张三)

直接使用Unsafe类实现之前AtomicIntegerFieldUpdater中线程安全的原子整数 BankAccount

在atomic中使用AtomicIntegerFieldUpdater实现money线程安全的原子整数

package com.dongguo.unsafe;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
* @author Dongguo
* @date 2021/9/7 0007-14:41
* 以一种线程安全的方式操作非线程安全对象的某些字段。
* 需求:
* 1000个人同时向一个账号转账一元钱,那么累计应该增加1000元,
* 除了synchronized和CAS,还可以使用AtomicIntegerFieldUpdater来实现。
*/
class BankAccount {
  private String bankName = "ACBC";
  public volatile int money = 0;
  AtomicIntegerFieldUpdater<BankAccount> fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "money");

  public void transferMoney(BankAccount bankAccount) {
      fieldUpdater.incrementAndGet(bankAccount);
  }
}

public class AtomicIntegerFieldUpdaterDemo {
  public static void main(String[] args) {
      BankAccount bankAccount = new BankAccount();

      for (int i = 1; i <= 1000; i++) {
          new Thread(() -> {
              bankAccount.transferMoney(bankAccount);
          }, String.valueOf(i)).start();
      }
      //暂停毫秒
      try {
          TimeUnit.MILLISECONDS.sleep(500);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
      System.out.println(bankAccount.money);
  }

}

改为使用UnSafe实现money线程安全的原子整数

package com.dongguo.unsafe;

import sun.misc.Unsafe;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
* @author Dongguo
* @date 2021/9/7 0007-14:41
*/
class BankAccount {
  private String bankName = "ACBC";
  public volatile int money;
  static final Unsafe unsafe;
  static final long DATA_OFFSET;

  static {
      unsafe = UnsafeAccessor.getUnsafe();
      try {
          // money 属性在 BankAccount 对象中的偏移量,用于 Unsafe 直接访问该属性
          DATA_OFFSET = unsafe.objectFieldOffset(BankAccount.class.getDeclaredField("money"));
      } catch (NoSuchFieldException e) {
          throw new Error(e);
      }
  }

  public BankAccount(int money) {
      this.money = money;
  }

  public void transferMoney(int amount) {
      int oldValue;
      while (true) {
          // 获取共享变量旧值,可以在这一行加入断点,修改 data 调试来加深理解
          oldValue = money;
          // cas 尝试修改 data 为 旧值 + amount,如果期间旧值被别的线程改了,返回 false
          if (unsafe.compareAndSwapInt(this, DATA_OFFSET, oldValue, oldValue + amount)) {
              return;
          }
      }
  }
}
public class AtomicIntegerFieldUpdaterDemo {
  public static void main(String[] args) {
      BankAccount bankAccount = new BankAccount(0);

      for (int i = 1; i <= 1000; i++) {
          new Thread(() -> {
              bankAccount.transferMoney(1);
          }, String.valueOf(i)).start();
      }
      //暂停毫秒
      try {
          TimeUnit.MILLISECONDS.sleep(500);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
      System.out.println(bankAccount.money);
  }
}
运行结果
1000
/暂停毫秒
      try {
          TimeUnit.MILLISECONDS.sleep(500);
      } catch (InterruptedException e) {
          e.printStackTrace();
      }
      System.out.println(bankAccount.money);
  }
}

运行结果

1000

到此这篇关于Java使用Unsafe类的文章就介绍到这了,更多相关Java使用Unsafe类内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/m0_37450089/article/details/120462281

延伸 · 阅读

精彩推荐
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    这篇文章主要介绍了Java使用SAX解析xml的示例,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java实现抢红包功能

    Java实现抢红包功能

    这篇文章主要为大家详细介绍了Java实现抢红包功能,采用多线程模拟多人同时抢红包,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java8中Stream使用的一个注意事项

    Java8中Stream使用的一个注意事项

    最近在工作中发现了对于集合操作转换的神器,java8新特性 stream,但在使用中遇到了一个非常重要的注意点,所以这篇文章主要给大家介绍了关于Java8中S...

    阿杜7482021-02-04
  • Java教程Java BufferWriter写文件写不进去或缺失数据的解决

    Java BufferWriter写文件写不进去或缺失数据的解决

    这篇文章主要介绍了Java BufferWriter写文件写不进去或缺失数据的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望...

    spcoder14552021-10-18
  • Java教程升级IDEA后Lombok不能使用的解决方法

    升级IDEA后Lombok不能使用的解决方法

    最近看到提示IDEA提示升级,寻思已经有好久没有升过级了。升级完毕重启之后,突然发现好多错误,本文就来介绍一下如何解决,感兴趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程20个非常实用的Java程序代码片段

    20个非常实用的Java程序代码片段

    这篇文章主要为大家分享了20个非常实用的Java程序片段,对java开发项目有所帮助,感兴趣的小伙伴们可以参考一下 ...

    lijiao5352020-04-06
  • Java教程xml与Java对象的转换详解

    xml与Java对象的转换详解

    这篇文章主要介绍了xml与Java对象的转换详解的相关资料,需要的朋友可以参考下...

    Java教程网2942020-09-17
  • Java教程小米推送Java代码

    小米推送Java代码

    今天小编就为大家分享一篇关于小米推送Java代码,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...

    富贵稳中求8032021-07-12