挂面03

作者: 盼旺 | 来源:发表于2019-09-26 20:20 被阅读0次

    线程同步的方法

    1.有synchronized关键字修饰的方法

    由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,
    内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。 如:public synchronized void save(){}synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类

    2.同步代码块
    即有synchronized关键字修饰的语句块。
    被该关键字修饰的语句块会自动被加上内置锁,从而实现同步

    代码如:
        synchronized(object){
        }
    

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

    a.volatile关键字为域变量的访问提供了一种免锁机制,
    b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
    c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
    d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量

    4.使用重入锁实现线程同步
    ReentrantLock类是可重入、互斥、实现了Lock接口的锁,
    ReenreantLock类的常用方法有:

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

    例子

    class Bank {
    
    private int account = 100;
    //需要声明这个锁
    private Lock lock = new ReentrantLock();
    public int getAccount() {
    return account;
    }
    //这里不再需要synchronized 
    public void save(int money) {
    lock.lock();
    try{
        account += money;
    }finally{
        lock.unlock();
    }
    
    }
    

    5.使用局部变量实现线程同步
    使用ThreadLocal管理变量,每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

    public class Bank{
                //使用ThreadLocal类管理共享变量account
                private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){
                    @Override
                    protected Integer initialValue(){
                        return 100;
                    }
                };
                public void save(int money){
                    account.set(account.get()+money);
                }
                public int getAccount(){
                    return account.get();
                }
            }
    

    6.使用阻塞队列实现线程同步
    LinkedBlockingQueue 类常用方法

        LinkedBlockingQueue() : 创建一个容量为Integer.MAX_VALUE的LinkedBlockingQueue 
        put(E e) : 在队尾添加一个元素,如果队列满则阻塞 
        size() : 返回队列中的元素个数 
        take() : 移除并返回队头元素,如果队列空则阻塞 
    

    BlockingQueue<E>定义了阻塞队列的常用方法,尤其是三种添加元素的方法,我们要多加注意,当队列满时:

      add()方法会抛出异常
      offer()方法返回false
      put()方法会阻塞
    

    7.使用原子变量实现线程同步
    什么是原子操作呢?
    原子操作就是指将读取变量值、修改变量值、保存变量值看成一个整体来操作
    即-这几种行为要么同时完成,要么都不完成。
    在java的util.concurrent.atomic包中提供了创建了原子类型变量的工具类,举例:其中AtomicInteger 表可以用原子方式更新int的值
    AtomicInteger类常用方法:

    AtomicInteger(int initialValue) : 创建具有给定初始值的新的AtomicInteger
    addAddGet(int dalta) : 以原子方式将给定值与当前值相加
    get() : 获取当前值
    
    class Bank {
            private AtomicInteger account = new AtomicInteger(100);
    
            public AtomicInteger getAccount() {
                return account;
            }
    
            public void save(int money) {
                account.addAndGet(money);//读写一致
            }
        }
    

    说说volatile

    当一个变量被volatile关键字修饰后,它将具备两种特性:
    1、保证此变量对所有线程的可见性。
    2、禁止指令重排序

    第一条特性:
    保证此变量对所有线程的可见性,这里的可见性是指当一条线程修改了这个变量的值,新值对于其他线程来说
    是立即得知的。普通变量是做不到的。普通变量的值在线程间传递需要主内存来完成。例如,线程A修改一个普通
    变量的值,然后回写到主内存,另外一条线程B在线程A回写完成后再从主内存进行读取,新变量值才会对线程B可见。

    第二个特性:
    指令重排序是指在不改变运行结果的前提下,对指令进行重排优化。


    对于上述代码,如果线程1执行write方法,线程2执行multiply方法
    如果线程1进行了指令重排序,即先进行flag=true,在进行a=2
    这时候ret就不是4了,加上volatie关键字后就不会出现这问题了
    还有一个就是双重校验锁的单例模式

    这段代码看起来很完美,很可惜,它是有问题。主要在于instance = new Singleton()这句,这并非是一个原子操作,事实上
    在 JVM 中这句话大概做了下面 3 件事情。
    1.给 instance 分配内存
    2.调用 Singleton 的构造函数来初始化成员变量
    3.将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)
    但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可 能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错

    给变量加上volatie关键字后,然后对该变量进行写操作,然后查看生成的汇编指令,它在会多出一行Lock汇编代码

    Spring IoC有什么好处呢

    第一,资源集中管理,实现资源的可配置和易管理。
    第二,降低了使用资源双方的依赖程度,也就是我们说的耦合度

    也就是说,甲方要达成某种目的不需要直接依赖乙方,它只需要达到的目的告诉第三方机构就可以了,比如甲方需要一双袜子,而乙方它卖一双袜子,它要把袜子卖出去,并不需要自己去直接找到一个卖家来完成袜子的卖出。它也只需要找第三方,告诉别人我要卖一双袜子。这下好了,甲乙双方进行交易活动,都不需要自己直接去找卖家,相当于程序内部开放接口,卖家由第三方作为参数传入。甲乙互相不依赖,而且只有在进行交易活动的时候,甲才和乙产生联系。反之亦然。这样做什么好处么呢,甲乙可以在对方不真实存在的情况下独立存在,而且保证不交易时候无联系,想交易的时候可以很容易的产生联系。甲乙交易活动不需要双方见面,避免了双方的互不信任造成交易失败的问题。因为交易由第三方来负责联系,而且甲乙都认为第三方可靠。那么交易就能很可靠很灵活的产生和进行了。支付宝在整个淘宝体系里就是庞大的ioc容器,交易双方之外的第三方,提供可靠性可依赖可灵活变更交易方的资源管理中心。

    在以上的描述中,诞生了两个专业词汇,依赖注入控制反转
    所谓的依赖注入,则是,甲方开放接口,在它需要的时候,能够将乙方传递进来(注入)
    所谓的控制反转,甲乙双方不相互依赖,交易活动的进行不依赖于甲乙任何一方,整个活动的进行由第三方负责管理。

    原文参考:
    https://www.cnblogs.com/xhjt/p/3897440.html
    https://blog.csdn.net/yanghan1222/article/details/80156156

    相关文章

      网友评论

          本文标题:挂面03

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