锁相关

作者: 双囍_小赵 | 来源:发表于2021-11-03 12:07 被阅读0次

对于非原子变量(count++),多个线程同时操作它的值的时候,如果不想每次结果不一致,可以使用加锁例如:synchronized(obj){ //被操作变量 };

对于原子变量(例如int a = 0),多个线程同时操作它的值的时候,如果不想每次线程使用时值不一致,可以使用volatile修饰,这样当一个线程修改了值存进了主内存,另一个线程在使用期间会将正在使用的值进行失效处理,再重新在主内存中拿新的值使用,就是有一个总线嗅探机制。

任意的对象都能成为锁:锁有两种状态

1、有锁状态:

2、无锁状态:

锁演变流程及不同应对方案:
方案一:一个线程碰到synchronized之后对象变成偏向锁,直接标记在对象的markword上;
锁的三种方式:
public class Person{

    //充当一把锁
    private Object lock = new Object();

    //也可以这样
    private byte[] b = new byte[0];//表示在内存中开辟一个单位的字节+128位的头数据

    public void a(){
            synchronized(lock){    
                    //do something
            }
          //或者
              synchronized(b){    
                    //do something
            }


    }
}


2.锁住当前this(this也是同下面——不要直接这样做,这样做是将整个class作为锁对象,会存在内存泄露的风险)

public class Person{   

    public void a(){

         synchronized(this){   

                //do something            

         } 

    }             

}

    3.直接锁非静态方法(不要直接这样做,这样做是将整个class作为锁对象,会存在内存泄露的风险)

public synchronized void a(){

}

注:使用对象锁的情况,只有使用同一实例的线程才会受锁的影响,多个实例调用同一方法也不会受影响。
方案二:二个线程碰到synchronized之后作为对象变成轻量级锁,数据记录在栈帧内;
方案三:三个线程来到就使用monitor,里面三个线程排队,每个线程在使用的时候就到owner区域;
公平锁是monitor有的说法,在排队的过程中线程依次进入Owner中运行,不会有插队的情况就是公平锁;

非公平锁就是线程在entrylist列表中都可以抢占Owner,进行运行;

无锁方案

方案四:CAS方案(也用于多线程共享变量)

CAS必须和volatile配合使用,保证多线程安全性同时保证可见性;

可以使用JUC中AQS来实现CAS(CompareAndSet)方案:

使用案例:

//CAS无锁状态

ReentrantLock lock = new ReentranLock();//源码中lock的使用就是 值.CompareAndSet(当前值,预期值,到达预期值就跳出结束,否则一直循环)

lock.lock();

//要用的变量

lock.unlock();

//使用原子操作Atomic

static int i = 0;

static AtomicInteger inte = new AtomicInteger();

public static void main(String[] args) {

    final Object object = new Object();

    Thread t1 = new Thread(new Runnable() {

        @Override public void run() {

                for (int j = 0; j < 10; j++) {

                    /** * 两者等同效果 */

                    synchronized (object) { i++; }

                    //-----------------------

                    inte.incrementAndGet();

                    }

                }

            });

注:这里涉及到锁膨胀的问题,锁膨胀就是根据线程开辟数量升级不同的应对方法;

综上所述:
java锁不是唯一处理线程互斥的方案;
也可以使用CAS方案解决;
CAS和Synchronized的区别:
无锁状态下,CAS会一直高速旋转没有停歇,而Synchronized会在线程没有抢到锁的时候进入阻塞状态,发生上下文切换;

由此可以知道CAS中线程一直在跑就需要CPU的支持,所以在使用CAS时线程的数量和CPU对应的话,效率最高。假设有10000个线程,会出现线程上下文切换(换CPU运行线程),切换的话会导致消耗,效率很低。
CAS原子变量:Atomic包
对于单个数据的多线程调用情况下,提供了一套Atomic包——原子操作

* 所有的原子操作,实际是在顶层进行了一个CAS的循环比较,只有达成目的才会退出;

* 在并发场景中大部分是某个属性的数据安全处理,所以搞了一组Atomic包,利用CAS不加锁的方式处理,在当前线程不多,核数合适的情况下,这种方案优于synchronized;
原子变量分为:
基本数据类型
引用类型:AtomicRefrence处理的是引用类型的值-->地址
原子数组
对象
compareAndSet:比较并且设值
* 当前数值比较如果相等就设入新的值
* 如果不等就一直自旋
CAS的ABA问题
* ABA指的是:线程1更改值将A修改成B, 线程2再次将值B修改为A ,使用AtomicString就无法知道是否有修改过值,这就是ABA问题;

* CAS引起的ABA问题,可以用AtomicStampedReference和AtomicMarkableReference去解决,可以知道是否有修改值
CAS(Compare And Set 比较并且替换)机制中使用了三个基本操作数:
  • 内存地址V
  • 旧的预期值A
  • 要修改的值B
    只有当内存地址V中实际的值与旧的预期值A相等的时候,才能将内存地址对应的值修改为B;
下面Atomic相关使用示例:
/**
 * Created by:zx
 * on 11/2/21
 * 对于单个数据的多线程调用情况下,提供了一套Atomic包——原子操作
 * 所有的原子操作,实际是在顶层进行了一个CAS的循环比较,只有达成目的才会退出;
 * 在并发场景中大部分是某个属性的数据安全处理,所以搞了一组Atomic包,利用CAS不加锁的方式处理,在当前线程不多,核数合适的情况下,这种方案优于synchronized
 * <p>
 * <p>
 * compareAndSet:比较并且设值
 * 当前数值比较如果相等就设入新的值
 * 如果不等就一直自旋
 * <p>
 * CAS引起的ABA问题,可以用AtomicStampedReference和AtomicMarkableReference去解决,可以知道是否有修改值
 * ABA指的是:线程1更改值为A->B 线程2再次更改值为B->A 使用AtomicString就无法知道是否有修改过值,这就是ABA问题
 **/
public class Atomic {
    static int i = 0;
    static AtomicInteger inte = new AtomicInteger(0);

    static AtomicReference<String> atomicReference = new AtomicReference<>("A");

    /**
     * 如果数据在使用过程中从A变成了B,然后另外一个线程将值从B又变回了A,这时在业务角度来看是没有变动的,
     * 但是内部运行是有所变动的,进行了两次赋值,这个时候如果你想知道这个值是不是有变动的情况,就提供了下面这个版本记录,
     * 就可以知道A变成B,B在变成A是否是经过这个步骤才回到A还是说一直没有变化
     */
    //版本记录参数1:当前值,参数2:版本号,改一次就会加一    能追溯轨迹,当前值更新了第几个版本了
    static AtomicStampedReference<String> atomicStampedReference = new AtomicStampedReference<>("A", 0);
    //这个表示我只知道是否更改过这个值,如果改过就是true,没有改过就是false
    static AtomicMarkableReference<String> atomicMarkableReference = new AtomicMarkableReference<>("A", false);


    public static void main(String[] args) {

        //对象中某个属性将它保持原子操作
        /**
         * 参数一:某个model类
         * 参数二:当前这个属性的所属类型
         * 参数三:要修改的这个值的key
         */
        PersonBean bean = new PersonBean();
        AtomicReferenceFieldUpdater atomicReferenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(PersonBean.class, String.class, "name");
        /**
         * 参数一:bean类
         * 参数二:对它预期的值
         * 参数三:要将它的值修改成这个结果
         */
        atomicReferenceFieldUpdater.compareAndSet(bean, null, "赵欣");
        System.out.println("AtomicReferenceFieldUpdater:"+ bean.toString());
        //打印:
        // AtomicReferenceFieldUpdater:PersonBean{name='赵欣', age='null'}


        final Object object = new Object();

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {

                for (int j = 0; j < 10; j++) {

                    /**
                     *  两者等同效果
                     */
                    synchronized (object) {
                        i++;
                    }
                    //AtomicInteger
                    inte.incrementAndGet();


                    //atomicStampedReference
                    String ast = atomicStampedReference.getReference();
                    System.out.println("atomicStampedReference1 : " + atomicStampedReference.compareAndSet(ast, "B" + j, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
                    System.out.println("atomicStampedReference新值1:" + atomicStampedReference.getStamp() + " " + atomicStampedReference.getReference());


                }


                //AtomicRefrence
                String aR = atomicReference.get();
                System.out.println("atomicReference1 : " + atomicReference.compareAndSet(aR, "D"));
                System.out.println("atomicReference新值1:" + atomicReference.get());


                //atomicMarkableReference
                String am = atomicMarkableReference.getReference();
                System.out.println("atomicMarkableReference1:" + atomicMarkableReference.compareAndSet(am, "E", atomicMarkableReference.isMarked(), true));
                System.out.println("atomicMarkableReference1值:" + atomicMarkableReference.isMarked() + " " + atomicMarkableReference.getReference());

            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int j = 0; j < 10; j++) {
                    /**
                     *  两者等同效果
                     */
                    synchronized (object) {
                        i++;
                    }

                    //AtomicInteger
                    inte.incrementAndGet();
                }

                //AtomicRefrence
                String aR = atomicReference.get();
                System.out.println("atomicReference2 : " + atomicReference.compareAndSet(aR, "C"));
                System.out.println("atomicReference新值2:" + atomicReference.get());


//                //atomicStampedReference
                String ast = atomicStampedReference.getReference();
                System.out.println("atomicStampedReference2 : " + atomicStampedReference.compareAndSet(ast, "A", atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
                System.out.println("atomicStampedReference新值2:" + atomicStampedReference.getStamp() + " " + atomicStampedReference.getReference());

                //atomicMarkableReference
                String am = atomicMarkableReference.getReference();
                System.out.println("atomicMarkableReference2:" + atomicMarkableReference.compareAndSet(am, "A", atomicMarkableReference.isMarked(), true));
                System.out.println("atomicMarkableReference2值:" + atomicMarkableReference.isMarked() + " " + atomicMarkableReference.getReference());


            }
        });

        /**
         * 打印:每次运行两个线程互相修改,值一致
         * atomicReference2 : false
         * atomicReference新值2:D
         * atomicReference1 : true
         * atomicReference新值1:D
         *
         * atomicReference2 : false
         * atomicReference1 : true
         * atomicReference新值2:D
         * atomicReference新值1:D
         *
         * atomicReference1 : true
         * atomicReference2 : true
         * atomicReference新值1:C
         * atomicReference新值2:C
         *
         * atomicReference1 : true
         * atomicReference2 : false
         * atomicReference新值2:D
         * atomicReference新值1:D
         */


        /**
         * 打印:atomicStampedReference  获取当前的版本,然后自加1,最终获得的修改的值就是最终修改过的多少次
         *
         * atomicStampedReference1 : true
         * atomicStampedReference2 : true
         * atomicStampedReference新值1:2 A
         * atomicStampedReference新值2:2 A
         * atomicStampedReference1 : true
         * atomicStampedReference新值1:3 B1
         * atomicStampedReference1 : true
         * atomicStampedReference新值1:4 B2
         * atomicStampedReference1 : true
         * atomicStampedReference新值1:5 B3
         * atomicStampedReference1 : true
         * atomicStampedReference新值1:6 B4
         * atomicStampedReference1 : true
         * atomicStampedReference新值1:7 B5
         * atomicStampedReference1 : true
         * atomicStampedReference新值1:8 B6
         * atomicStampedReference1 : true
         * atomicStampedReference新值1:9 B7
         * atomicStampedReference1 : true
         * atomicStampedReference新值1:10 B8
         * atomicStampedReference1 : true
         * atomicStampedReference新值1:11 B9
         * atomicStampedReference新值00000:11 B9
         */


        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        //atomicStampedReference
        System.out.println("atomicStampedReference新值00000:" + atomicStampedReference.getStamp() + " " + atomicStampedReference.getReference());


        //atomicMarkableReference
        System.out.println("atomicMarkableReference00000值:" + atomicMarkableReference.isMarked() + " " + atomicMarkableReference.getReference());


        //AtomicInteger
        //compareAndSet使用,基准是inte的get原本的值,只有get获得当前的值,update需要修改的值,如果两者相同就退出if
        int ai = inte.get();
        System.out.println("ai: " + ai);
        if (inte.compareAndSet(ai, 3)) {
            System.out.println(3);
        }

        System.out.println(i + "  " + inte);

    }
}

总结:

并发的手段两种:
  • synchronized(悲观体现:表示我上锁了,别的线程无法修改,只有我释放锁了其他线程才能修改)
  • 无锁CAS(乐观体现:乐观锁就是没加锁,只考虑方案的实现,其他线程修改了也没事,使用了CAS会一直重试获取结果;CAS是无锁并发,无阻塞)

相关文章

网友评论

      本文标题:锁相关

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