美文网首页
Java多线程是如何解决同步的?

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

作者: 育王净量 | 来源:发表于2019-03-06 18:15 被阅读0次

同步资源

同步资源,是对资源(类、方法、代码块、变量)进行同步控制。在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();
    }
}

完整版代码

相关文章

  • Java多线程干货系列—(二)synchronized

    前言 本篇主要介绍Java多线程中的同步,也就是如何在Java语言中写出线程安全的程序,如何在Java语言中解决非...

  • java多线程基础(二)

    简介 本次主要介绍java多线程中的同步,也就是如何在java语言中写出线程安全的程序。如何在java语言中解决非...

  • Java之同步代码块

    Java多线程的同步代码块 synchronized(对象){ 需要同步的代码 } 同步代码块可以解决安全...

  • 5月份第一周学习安排

    学习内容: java多线程及线程同步的方法(使用) java多线程各种同步方法的原理和优缺点 java多线程设计模...

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

    同步资源 同步资源,是对资源(类、方法、代码块、变量)进行同步控制。在java中的多线程操作(修改)同一个共享变量...

  • 线程安全实现与CLH队列

    阻塞同步 在 Java 中,我们经常使用 synchronized 关键字来做到互斥同步以解决多线程并发访问共享数...

  • 线程池

    Java多线程 线程的同步是Java多线程编程的重点和难点,往往让人搞不清楚什么是竞争资源、什么时候需要考虑同步,...

  • UI数据源同步

    数据源同步问题多线程对共享数据的访问,需要考虑数据源的同步问题,如何解决tableView在多线程环境下的修改或者...

  • 锁与多线程同步的实现

    Java当中的锁都是为了保证多线程同步执行。如果没有锁的话,多线程是异步执行的。 什么是多线程同步? 请看下面的代...

  • Java Synchronized 的用法

    Synchronized 是由Java SDK自带的轻量级的同步锁,可以非常方便的解决在多线程环境下的资源竞争问题...

网友评论

      本文标题:Java多线程是如何解决同步的?

      本文链接:https://www.haomeiwen.com/subject/coqtpqtx.html