synchronized锁对象的坑

本文探讨了Java中synchronized关键字的使用误区,通过实例解释了为何直接在可变对象上加锁会导致问题,并提出了两种解决方案:锁定this对象和封装可变对象。深入分析了对象的不可变性对于并发控制的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    今天本来写点其他东西,碰巧写了一下synchronized,没想到掉坑里面了,大佬别笑。

    起初代码大概是这样的:

package com.ripplechan.part_1_2_3;

import java.util.concurrent.CountDownLatch;

/**
 * @author RippleChan
 * @date 2018-09-20
 * @time 18:05
 */
public class DataTest {

    public static void main(String[] args) throws InterruptedException {
        int count = 10000;
        CountDownLatch countDownLatch = new CountDownLatch(count * 1);
        Cal cal = new Cal(countDownLatch);
        for (int i = 0; i < count; i++) {
            new Thread(cal).start();
        }
        countDownLatch.await();
        System.out.println(cal.getSum());
    }

}

class Cal extends Thread {

    private Long sum = 0L;
    CountDownLatch countDownLatch;

    public Cal(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        synchronized (sum) {
            try {
                this.sum = sum + 1;
            }  finally {
                countDownLatch.countDown();
            }
        }
    }

    public Long getSum() {
        return sum;
    }

}

    你觉得答案是多少?为什么会这样?如何避免这种情况?

    看下如下代码,相信你会明白为什么了。

public class DatSalfByObject {

    public static void main(String[] args) throws InterruptedException {
        Long a = 1L;
        int i = System.identityHashCode(a);
        System.out.println(i);
        a = a + 1L;
        int i1 = System.identityHashCode(a);
        System.out.println(i1);
    }

}

    这里做个解释吧,this.sum = sum + 1;后,sum还是以前的sum么?为什么会这样?关键词,不可变对象。通过System.identityHashCode也看出来了,变了。既然对象都变了,sychronized能锁住么?锁的是之前的对象,没有丝毫意义。

    通过某大佬的指点,我得出一下集中方案:

1.直接锁this(锁方法和锁this本质一样);
2.将需要修改的对象再次封装;

    方案1:

class MyCal5 extends Thread {

    private Long sum = 0L;
    CountDownLatch countDownLatch;

    public MyCal5(CountDownLatch countDownLatch) {
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        try {
            synchronized (this) {
                this.sum = sum + 1;
            }
        }finally {
            countDownLatch.countDown();
        }
    }

    public Long getSum() {
        return sum;
    }

}

    方案2:

@Data
class User {

    private Long id = 0L;

}

class UserThread extends Thread {

    private User user;
    private CountDownLatch countDownLatch;

    public UserThread(User user,CountDownLatch countDownLatch) {
        this.user = user;
        this.countDownLatch = countDownLatch;
    }


    @Override
    public void run() {
        try {
            synchronized (user) {
                Long id = user.getId();
                user.setId(id + 1);
            }
        }finally {
            countDownLatch.countDown();
        }
    }

}

    最终,因为锁this消耗太大,所以还是推荐锁对象,但如何避免对象被修改呢?是考验JavaSE基础的时候了,代码如下:

package com.ripplechan.part_1_2_3;

import lombok.Data;

import java.util.concurrent.CountDownLatch;

/**
 * @author RippleChan
 * @date 2018-09-20
 * @time 17:38
 */
public class DatSalfByObject {

    public static void main(String[] args) throws InterruptedException {
        int count = 10000;
        User user = new User();
        CountDownLatch countDownLatch = new CountDownLatch(count);
        UserThread userThread = new UserThread(user, countDownLatch);
        for (int c = 0; c < 10000; c++) {
            new Thread(userThread).start();
        }
        countDownLatch.await();
        System.out.println(user.getId());
    }

}


@Data
class User {

    private Long id = 0L;

}

class UserThread extends Thread {

    private final User user;
    private final CountDownLatch countDownLatch;

    public UserThread(User user,CountDownLatch countDownLatch) {
        this.user = user;
        this.countDownLatch = countDownLatch;
    }


    @Override
    public void run() {
        try {
            synchronized (user) {
                Long id = user.getId();
                user.setId(id + 1);
            }
        }finally {
            countDownLatch.countDown();
        }
    }

}

    对,finnal修饰,哈哈,这是一把防止出错的锁。

转载于:https://my.oschina.net/vright/blog/2088305

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值