文档章节

synchronized锁对象的坑

RippleChan
 RippleChan
发布于 09/20 18:23
字数 606
阅读 23
收藏 0

    今天本来写点其他东西,碰巧写了一下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修饰,哈哈,这是一把防止出错的锁。

© 著作权归作者所有

共有 人打赏支持
RippleChan
粉丝 49
博文 92
码字总数 25918
作品 0
朝阳
程序员
私信 提问
java线程的同步代码块关键字synchronized

Java 慎用方法级别的synchronized关键字 为什么要这么说呢, 因为笔者被这个坑过(其实是自己坑自己)╮(╯_╰)╭ 先看一段synchronized 的详解: synchronized 是 java语言的关键字,当它用...

Jasonisoft
2016/05/30
0
0
Java多线程 - 各种线程锁

多个线程同时对同一个对象进行读写操作,很容易会出现一些难以预料的问题。所以很多时候我们需要给代码块加锁,同一时刻只允许一个线程对某个对象进行操作。多线程之所以会容易引发一些难以发现...

嘉伟咯
2017/09/01
0
0
synchronized锁机制 之 代码块锁

synchronized同步代码块 用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个较长时间的任务,那么B线程必须等待比较长的时间。这种情况下可以尝试使用syn...

harries
06/27
0
0
Java多线程基础篇(03)-synchronized关键字

1.引言 基本上所有的并发模式再解决线程冲突的时候,都是采用序列化访问共享资源的方案。这意味着在给定时刻只允许一个任务访问共享资源。通常这是通过在代码前面添加一条锁语句来实现的,这...

kukudeku
2016/09/20
53
0
面试系列-高并发之synchronized

本章,会对synchronized关键字进行介绍。涉及到的内容包括: synchronized原理 synchronized基本规则 synchronized方法 和 synchronized代码块 实例锁 和 全局锁 synchronized原理 在java中,...

技术小能手
07/05
0
0

没有更多内容

加载失败,请刷新页面

加载更多

mybatis批量update操作的写法,及批量update报错的问题解决方法

mybatis的批量update操作写法很简单,如下: 如果想学习Java工程化、高性能及分布式、深入浅出。微服务、Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿...

编程SHA
27分钟前
2
0
EOS怎样删除钱包

在使用Eos的keosd钱包软件时,如果要删除EOS中指定名称的钱包,最简单的办法是 直接删除钱包文件,不过在删除钱包之前,需要先停止钱包软件的运行。 学习EOS应用开发要选这个:【EOS智能合约...

汇智网教程
34分钟前
5
0
Java语言快速实现简单MQ消息队列服务

使用 JAVA 语言自己动手来写一个MQ (类似ActiveMQ,RabbitMQ) 主要角色 首先我们必须需要搞明白 MQ (消息队列) 中的三个基本角色 ProducerBrokerConsumer 整体架构如下所示 自定义协议 首...

微笑向暖wx
43分钟前
5
0
ES5和ES6那些你必须知道的事儿

  ES5和ES6那些你必须知道的事儿      ES5新增的东西      一、数组方法      1、forEach      用途:遍历,循环      对于空数组不会执行回调函数      复制代码...

SEOwhywhy
今天
11
0
转:[windows]DOS批处理添加任务计划

[windows]DOS批处理添加任务计划 博客分类: Windows 转自:http://gwmold.blog.163.com/blog/static/1553319892010117113457232/ 自动创建每周运行一次的计划任务 创建计划任务可用at,sch...

SamXIAO
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部