java++锁的对象_你竟然用Integer作为synchronized的锁对象?

在使用多线程编程时,往往会使用一些手段保证线程安全,也就是加锁,但是加锁也必须合理,如使用synchronized对对象加锁时,如果不注意,还可能发生错误的加锁。

先看一段小测试,在这个小测试中,启动了1000个线程,每个线程在对integer加1前都先获得integer的锁,这看似是线程安全的,并且预期可能会得到1000这个值,而然并不然,在运行多次之后他总是输出<=1000的值,那么,这犊子是哪出问题了。

static Integer integer =new Integer("0");

static CountDownLatch mCountDownLatch =new CountDownLatch(1000);

public static void main(String[] args) throws InterruptedException {

for (int i = 1; i <=1000; i++) {

new Thread(()->{

synchronized (integer){

integer++;

mCountDownLatch.countDown();

}

}).start();

}

mCountDownLatch.await();

System.out.println(integer);

}

这其实就是上述所说:"错误的加锁对象",也就是Integer作为锁对象这是个不正确的选择。

那在说Integer,不少人可能会见过这样的题。

Integer i1 =125;

Integer i2 =125;

System.out.println(i1==i2);

Integer i3 =135;

Integer i4 =135;

System.out.println(i3==i4);

答案是true,false,这样的结果是由Integer的缓存导致,在直接对Integer=xxx时候,其实调用了Integer.valueOf,(可以使用javap命令反编译查看)

e03e3a223ed20701714a7de3281aeb48.png

而Integer.valueOf中,如果参数值在一定范围内,就从IntegerCache缓存中返回,也就是说在一定范围内多个相同的值是同一个对象,超出的话则return new Integer(i)返回一个新的Integer。而这个范围在-128-127,

9baacd3de17785a572d231cf90394ee4.png

在上面的135显然不在范围,则返回的是新对象,又由于==是比较内存地址,所以上述会出现false。如果要比较包装类是否相等,正确的做法是equals方法,Integer也重写了equals,判断内部基本数据类型是否一样。

5cf252afda8dc208ad5613a47777aeb8.png

这样一来,synchronized(integer)原因就知道了, integer++是创建了一个新的Integer对象,并将引用赋值给integer。因为integer一直在变,线程每次加锁可能都加在了不同对象的实例上,导致临界区控制出现问题。

解决办法也很容易,只要加在一个不变的对象上即可。

static Integer integer =new Integer("0");

static CountDownLatch mCountDownLatch =new CountDownLatch(1000);

static Object sObject= new Object();

public static void main(String[] args) throws InterruptedException {

for (int i = 1; i <=1000; i++) {

new Thread(()->{

synchronized (sObject){

integer++;

mCountDownLatch.countDown();

}

}).start();

}

mCountDownLatch.await();

System.out.println(integer);

}

(不只Integer,Character、Short、Long也有缓存,但是Float和Double却没有)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatis-Plus 是一个在 MyBatis 基础上进行扩展的持久层框架,它提供了许多便捷的功能和增强的特性,其中包括乐观。 乐观是一种在并发环境下解决数据冲突的机制,它通过在更新数据时比较版本号来判断数据是否被其他线程修改过。在 MyBatis-Plus 中,你可以通过注解和实体字段来实现乐观。 下面是使用 MyBatis-Plus 乐观的步骤: 1. 在实体类中添一个用于存储版本号的字段,通常是一个整型字段。 ```java public class User { // 其他字段... @Version private Integer version; // Getter 和 Setter 方法... } ``` 2. 在数据库表中添一个与版本号字段对应的列。 3. 在执行更新操作时,通过 `@Version` 注解告诉 MyBatis-Plus 使用乐观。例如: ```java @Mapper public interface UserMapper extends BaseMapper<User> { @Update("UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id} AND version = #{version}") int updateWithVersion(User user); } ``` 在上面的示例中,`@Update` 注解中的 SQL 语句中包含了 `version = #{version}` 条件,表示只有当版本号匹配时才执行更新操作。 4. 在代码中调用更新操作时,需要先查询出要更新的数据,然后将版本号一,并更新到数据库中。例如: ```java User user = userMapper.selectById(userId); user.setName("New Name"); user.setAge(25); int result = userMapper.updateWithVersion(user); if (result == 0) { // 更新失败,说明数据已被其他线程修改 // 可以进行相应的处理,如抛出异常或者重试操作 } else { // 更新成功 } ``` 在上述代码中,通过 `selectById` 方法查询出要更新的数据,然后将版本号一,并将更新后的数据传入 `updateWithVersion` 方法中执行更新操作。如果更新失败(`result` 返回值为 0),则说明数据已被其他线程修改,可以根据实际需求进行相应的处理。 这就是使用 MyBatis-Plus 实现乐观的基本步骤。请注意,在使用乐观时,需要保证所有涉及到的操作都是通过 MyBatis-Plus 提供的方法进行,以确保乐观机制生效。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值