Java基础学习之线程同步方法总结

        引入线程同步的原因:当有多个线程要同时访问一个变量或对象时,如果这些线程中既有读又有写操作时,就会导致变量值或对象的状态出现混乱,从而导致程序异常,用户也得不到想要的结果。比如,如果一个银行账户同时被两个线程操作,一个取100块,一个存钱100块。假设账户原本有0块,如果取钱线程和存钱线程同时发生,会出现什么结果呢?取钱不成功,账户余额是100;取钱成功了,账户余额是0。那到底是哪个呢?很难说清楚。有了多线程同步就可以轻松解决这个问题。多线程同步主要有以下几种方法:

1.同步方法

  同步方法即用synchronized关键字修饰的方法。
  由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。代码如:

public synchronized void save(){}

  PS:synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类。
  synchronized既可以保证可见性,又能够保证原子性:
  (1)、可见性体现在:通过synchronized或者Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存中;
  (2)、原子性表现在:要么不执行,要么执行到底。

2.同步代码块

  同步代码块即用synchronized关键字修饰的语句块。被该关键字修饰的语句块会自动被加上内置锁,从而实现同步。代码如下单例模式双重检查锁定方式实现的实例方法:

    //双重检查锁机制
    public static SingleTon getInstance(){
        if (singleTonInstance==null){
            synchronized (SingleTon.class){
                if (singleTonInstance==null){
                    singleTonInstance = new SingleTon();
                }
            }
        }
        return singleTonInstance;
    }

  PS:同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

3.使用特殊域变量(volatile)实现线程同步

  使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,因此每次使用该域要从主存获取,而不是使用寄存器中的值。volatile不会提供任何原子操作,它也不能用来修饰final类型的变量。
  volatile是变量修饰符,其修饰的变量具有可见性。可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,可以立即获取修改之后的值。在Java中为了加快程序的运行效率,对一些变量的操作通常是在该线程的寄存器或是CPU缓存上进行的,之后才会同步到主存中,而加了volatile修饰符的变量则是直接读写主存。
  PS:多线程中的非同步问题主要出现在对域的读写上,如果让域自身避免这个问题,则就不需要修改操作该域的方法。用final域,有锁保护的域和volatile域可以避免非同步的问题。

4.使用重入锁实现线程同步

  ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和具有相同的基本行为和语义,并且扩展了其能力 ReenreantLock类的常用方法有:

ReentrantLock():创建一个ReentrantLock实例
lock() : 获得锁 
unlock() : 释放锁

  PS:关于Lock对象和synchronized关键字的选择:
  a.如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码;
  b.如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁。

5.使用局部变量实现线程同步

  使用ThreadLocal管理变量时,每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
  PS:ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题,前者采用以"空间换时间"的方法,后者采用以"时间换空间"的方式。

6.使用阻塞队列实现线程同步

  LinkedBlockingQueue<E>是一个基于已连接节点的,范围任意的blocking queue。队列是先进先出的顺序(FIFO)

7.使用原子变量实现线程同步

  需要使用线程同步的根本原因在于对普通变量的操作不是原子的。
  原子操作就是指将读取变量值、修改变量值、保存变量值看成一个整体来操作 即-这几种行为要么同时完成,要么都不完成。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值