Java多线程是如何解决同步的?

同步资源

同步资源,是对资源(类、方法、代码块、变量)进行同步控制。在java中的多线程操作(修改)同一个共享变量的时候,如果不进行同步控制,将会导致数据不准确,相互之间产生冲突。因此加入同步机制,来避免一个线程没有操作完资源之前,被另外几个线程调用,产生多种不一致的结果。
JVM需要对被线程共享的数据(保存在堆中的实例变量、保存在方法区的类变量)进行协调,不需要协调栈当中的数据(栈当中的数据被线程所私有)。常用的java多线程同步解决方案有:volatile、synchronized、ThreadLocal、ReentrantLock。

Synchronized

synchronized实现同步资源,有两种用法:修饰方法和修饰代码块;
修饰方法,每个java对象有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法,在调用方法前,需要获取内置锁,如果获取不到,线程就处于阻塞状态;当用synchronized修饰static方法时,锁住的是整个类;代码如下:

@Slf4j
@Data
@AllArgsConstructor
public class SynchronizedMethodBank implements Bank {
    
    private Double money;
    
    @Override
    public synchronized void saveMoney(double money) {
        System.err.println(format("save money %s", money));
        this.money += money;
    }
    
    @Override
    public synchronized void withdrawMoney(double money) {
        System.err.println(format("withdraw money %s", money));
        this.money -= money;
    }
    
    @Override
    public double queryMoney() {
        System.err.println(format("Current money is %s", this.money));
        return this.money;
    }
}

修饰代码块,被synchronized修饰的代码块会自动被加上内置锁。同步是一个资源开销大的动作,因此尽量缩小同步内容,通常做法是,如果没有必要同步整个方法,则使用synchronized代码块同步关键代码即可。

@Slf4j
@Data
@AllArgsConstructor
public class SynchronizedBlockBank implements Bank {
    
    private Double money;
    
    @Override
    public void saveMoney(double money) {
        System.err.println(format("save money %s", money));
        synchronized (this){
            this.money += money;
        }
    }
    
    @Override
    public synchronized void withdrawMoney(double money) {
        System.err.println(format("withdraw money %s", money));
        synchronized (this) {
            this.money -= money;
        }
    }
    
    @Override
    public double queryMoney() {
        System.err.println(format("Current money is %s", this.money));
        return this.money;
    }
}

Volatile

volatile比较有意思,当使用volatile修饰资源时,相当于告诉JVM该资源可能会被其他线程更新。因此每个线程在使用该资源当时候,都需要重新计算,而不是使用寄存器中的值(根据内存中的值再刷新下当前线程寄存器中的值,刷新完后再使用)。也就是常说的volatile保证了可见性,但是不保证原子性。

public class VolatileBank implements Bank {
    
    private volatile Double money;
    
    @Override
    public void saveMoney(double money) {
        synchronized (this){
            this.money +=money;
        }
    }
    
    @Override
    public void withdrawMoney(double money) {
        synchronized (this){
            this.money -= money;
        }
    }
    
    @Override
    public double queryMoney() {
        return this.money;
    }
}

ReentrantLock

ReentrantLock与使用synchronized方法具有相同的语义,再其基础上扩展了其能力;使用ReentrantLock要在finally中释放锁,否则会出现死锁;

@Data
@AllArgsConstructor
public class ReentrantLockBank implements Bank {
    
    private Double money;
    private ReentrantLock reentrantLock = new ReentrantLock();
    
    @Override
    public void saveMoney(double money) {
        reentrantLock.lock();
        try{
            this.money += money;
        }finally {
            reentrantLock.unlock();
        }
    }
    
    @Override
    public void withdrawMoney(double money) {
        reentrantLock.lock();
        try{
            this.money -= money;
        }finally {
            reentrantLock.unlock();
        }
    }
    
    @Override
    public double queryMoney() {
        return this.money;
    }
}

ThreadLocal

使用ThreadLocal管理变量,虽然有static修饰,但每个使用该变量的线程都获得该变量的副本,且副本之间相互独立;这样每个线程可以随意修改自己的变量副本,而不会对其他线程产生影响。

@Data
@AllArgsConstructor
public class ThreadLocalBank implements Bank {
    
    private static ThreadLocal<Double> doubleThreadLocal = new ThreadLocal<>();
    
    @Override
    public void saveMoney(double money) {
        doubleThreadLocal.set(doubleThreadLocal.get() + money);
    }
    
    @Override
    public void withdrawMoney(double money) {
        doubleThreadLocal.set(doubleThreadLocal.get() - money);
    }
    
    @Override
    public double queryMoney() {
        return doubleThreadLocal.get();
    }
}

完整版代码

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容