首页 > 其他分享 >八、JUC-原子类

八、JUC-原子类

时间:2023-05-28 14:31:44浏览次数:32  
标签:JUC int System 原子 new println import public

144.png

一、基本类型原子类

148.png

  • 首先,我们来用原子类实现i++的效果,可以看出,结果并不是50000
package com.lori.juc2023.juc8;

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicDemo1 {
    public static final int SIZE = 50;
    public static void main(String[] args) {
        MyNumber myNumber = new MyNumber();

        for (int i = 1; i <= SIZE; i++) {
            new Thread(()->{
                for (int j = 1; j <= 1000; j++) {
                    myNumber.addPlusPlus();
                }
            },String.valueOf(i)).start();
        }

        System.out.println(Thread.currentThread().getName()+"result:::"+myNumber.atomicInteger.get());
    }
}


class MyNumber{
    //默认初始值为0
    AtomicInteger atomicInteger = new AtomicInteger();
    public void addPlusPlus(){
        atomicInteger.getAndIncrement();
    }
}

145.png

原因是线程还没执行完,主线程就去获取了,我们再来睡5s后获取

package com.lori.juc2023.juc8;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicDemo1 {
    public static final int SIZE = 50;
    public static void main(String[] args) {
        MyNumber myNumber = new MyNumber();

        for (int i = 1; i <=SIZE; i++) {
            new Thread(()->{
                for (int j = 1; j <=1000; j++) {
                    myNumber.addPlusPlus();
                }
            },String.valueOf(i)).start();
        }

 try {
     TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {throw new RuntimeException(e);}
        System.out.println(Thread.currentThread().getName()+"result:::"+myNumber.atomicInteger.get());
    }
}


class MyNumber{
    //默认初始值为0
    AtomicInteger atomicInteger = new AtomicInteger();
    public void addPlusPlus(){
        atomicInteger.getAndIncrement();
    }
}

146.png

此时,我们已经能获得了预想的结果,可是等待时间人为控制不是很友好,也不准确。

CountDownLatch

  • CountDownLatch的作用很简单,就是一个或者一组线程在开始执行操作之前,必须要等到其他线程执行完才可以。我们举一个例子来说明,在考试的时候,老师必须要等到所有人交了试卷才可以走。此时老师就相当于等待线程,而学生就好比是执行的线程。
package com.lori.juc2023.juc8;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicDemo1 {
    public static final int SIZE = 50;
    public static void main(String[] args) {
        MyNumber myNumber = new MyNumber();
        CountDownLatch countDownLatch = new CountDownLatch(SIZE);
        for (int i = 1; i <=SIZE; i++) {
            new Thread(()->{
                try {
                    for (int j = 1; j <=1000; j++) {
                        myNumber.addPlusPlus();
                    }
                } finally {
                    //每执行完一个线程就-1
                    countDownLatch.countDown();
                }
            },String.valueOf(i)).start();
        }

        try {
            //等待所有线程执行完毕再执行下面的
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName()+"result:::"+myNumber.atomicInteger.get());
    }
}


class MyNumber{
    //默认初始值为0
    AtomicInteger atomicInteger = new AtomicInteger();
    public void addPlusPlus(){
        atomicInteger.getAndIncrement();
    }
}

147.png

二、数组类型原子类

149.png

package com.lori.juc2023.juc8;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;

public class AtomicDemo2 {
    public static void main(String[] args) {
        //AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[]{1,2,3,4,5});
        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[5]);
        for (int i = 0; i < atomicIntegerArray.length(); i++) {
            System.out.println(atomicIntegerArray.get(i));

        }

        int tmpInt = 0;
        tmpInt = atomicIntegerArray.getAndSet(0,2023);
        System.out.println(atomicIntegerArray.get(0));
        tmpInt = atomicIntegerArray.getAndIncrement(0);
        System.out.println(atomicIntegerArray.get(0));

    }
}

150.png

三、引用类型原子类

151.png

1、AtomicReference

package com.lori.juc2023.juc7;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;

import java.util.concurrent.atomic.AtomicReference;

public class CASDemo2 {
    public static void main(String[] args) {
        AtomicReference<User> atomicReference=new AtomicReference<>();
        User lori = new User("lori", 18);
        User yyqx = new User("yyqx", 19);
        atomicReference.set(lori);
        System.out.println(atomicReference.compareAndSet(lori, yyqx)+"\t"+atomicReference.get().toString());
    }
}

@Getter
@ToString
@AllArgsConstructor
class User{
    String name;
    int age;
}

152.png

package com.lori.juc2023.juc7;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;

import java.util.concurrent.atomic.AtomicReference;

public class CASDemo2 {
    public static void main(String[] args) {
        AtomicReference<User> atomicReference=new AtomicReference<>();
        User lori = new User("lori", 18);
        User yyqx = new User("yyqx", 19);
        atomicReference.set(lori);
        System.out.println(atomicReference.compareAndSet(lori, yyqx)+"\t"+atomicReference.get().toString());
        System.out.println(atomicReference.compareAndSet(lori, yyqx)+"\t"+atomicReference.get().toString());
    }
}

@Getter
@ToString
@AllArgsConstructor
class User{
    String name;
    int age;
}

153.png

2、AtomicStampedReference

ABA问题: CAS算法实现一个重要的前提需要取出内存中某时刻的数据并在当下时刻比较并且替换,那么在这个时间差会导致数据变化。 比如说一个线程1从内存位置V去取出A,这时候另一个线程2也从内存中取出A,并且线程2进行了一些操作,将A值修改为了B值,然后线程2又将V位置的数据变成A,这时候线程1进行CAS操作发现内存中仍然是A,预期OK,然后线程1操作成功。 尽管线程1的CAS操作成功,不代表这个过程是没有问题的。 解决ABA问题: 带版本号查询:

package com.lori.juc2023.juc7;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.concurrent.atomic.AtomicStampedReference;

public class CASDemo4 {
    public static void main(String[] args) {
        Book book = new Book(1,"java");
        //传参1:引用,传参2:版本号
        AtomicStampedReference<Book> reference = new AtomicStampedReference<>(book,1);
        System.out.println(reference.getReference()+"\t"+reference.getStamp());  //Book(id=1, name=java)	1

        
        Book book1 = new Book(2,"mysql");
        boolean compareAndSet = reference.compareAndSet(book, book1, reference.getStamp(), reference.getStamp() + 1);
        System.out.println(compareAndSet+"\t"+reference.getReference()+"\t"+reference.getStamp());  //



        compareAndSet = reference.compareAndSet(book1, book, reference.getStamp(), reference.getStamp() + 1);
        System.out.println(compareAndSet+"\t"+reference.getReference()+"\t"+reference.getStamp());  //

    }
}


@Data
@AllArgsConstructor
@NoArgsConstructor
class Book{
    private int id;
    private String name;
}

154.png 首先就使用原子类,不使用版本号:

package com.lori.juc2023.juc7;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo5 {
    static AtomicInteger atomicInteger = new AtomicInteger(100);
    public static void main(String[] args) {
        new Thread(()->{
           atomicInteger.compareAndSet(100,101);
           //暂停10毫秒
            try {
                TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {throw new RuntimeException(e);}
            atomicInteger.compareAndSet(101,100);
        },"t1").start();

        new Thread(()->{
            //暂停200毫秒,等线程1 执行完毕
             try {TimeUnit.MILLISECONDS.sleep(200);} catch (InterruptedException e) {throw new RuntimeException(e);}

            System.out.println("\t"+ atomicInteger.compareAndSet(100,2023)+"\t"+atomicInteger.get());
        },"t2").start();
    }
}

155.png 使用版本号原子方法

package com.lori.juc2023.juc7;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

public class CASDemo5 {
    //参数:初始值;版本号
  static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(100,1);
    public static void main(String[] args) {
        new Thread(()->{
            //拿到版本号
            int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"   首次版本号:::"+stamp);
            //暂停一段时间,确保t2拿到的版本号和t1一样
             try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}
             //第一次修改值
            stampedReference.compareAndSet(100,101,stamp,stamp+1);
            int stamp2 = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t"+"线程1第一次修改后版本号:::"+stamp2+"\t"+stampedReference.getReference());
            //第二次修改值
            stampedReference.compareAndSet(101,100,stamp2,stamp2+1);
            int stamp3 = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"\t"+"线程1第2次修改后版本号:::"+stamp3+"\t"+stampedReference.getReference());

                },"t1").start();


        new Thread(()->{
            int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+"   首次版本号:::"+stamp);
            //暂停3s确保t1线程已执行完毕,值已经变回100
             try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}
            boolean b = stampedReference.compareAndSet(100, 2023, stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName()+"\t"+"线程2第1次修改后版本号:::"+stamp+"  "+b+"\t"+stampedReference.getReference());
        },"t2").start();
    }

}

156.png

3、AtomicMarkableReference

原子更新带有标记位的引用类对象,标记是否被修改过

package com.lori.juc2023.juc8;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicMarkableReference;

public class AtomicDemo3 {
    static AtomicMarkableReference<Integer> ses = new AtomicMarkableReference<>(100,false);
    public static void main(String[] args) {
           new Thread(()->{
               //获取标识
               boolean marked = ses.isMarked();
               System.out.println(Thread.currentThread().getName()+"获取mark::"+marked);
               //暂停2s,确保t2也能获取到和t1一样的标识
                try {
                    TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}
                ses.compareAndSet(100,2023,marked,!marked);
               System.out.println(Thread.currentThread().getName()+"\t"+ses.getReference()+"修改后标识"+ses.isMarked());
           },"t1").start();


        new Thread(()->{
            //获取标识
            boolean marked = ses.isMarked();
            System.out.println(Thread.currentThread().getName()+"获取mark::"+marked);

            //暂停3s,确保t1已经执行完毕
            try {
                TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}
            ses.compareAndSet(100,8888,marked,!marked);
            if(!"8888".equals(ses.getReference())){
                System.out.println(Thread.currentThread().getName() +"修改失败::标识已被修改为:::"+ses.isMarked());
            }else {
                System.out.println(Thread.currentThread().getName() + "\t" + ses.getReference() + "修改后标识" + ses.isMarked());
            }
        },"t2").start();
    }
}

157.png

四、对象的属性修改原子类

158.png

  • AtomicIntegerFieldUpdater:原子更新对象中int类型的字段
  • AtomicLongFieldUpdater:原子更新对象中long类型的字段
  • AtomicReferenceFieldUpdater:原子更新对象中引用类型的字段

1、使用目的

以一种线程安全的方式操作非线程安全对象内的某些字段 比例:以前医院全麻,现在都是局部麻醉

2、使用要求

  • 更新的对象属性必须使用pubilc volatile修饰符
  • 因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。

3、代码

1、例一

i++

package com.lori.juc2023.juc8;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class AtomicDemo4 {
    public static void main(String[] args) {
        BankAccount bankAccount = new BankAccount();
        CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
               new Thread(()->{
                   try {
                       for (int j = 0; j < 1000; j++) {
                           bankAccount.transMoney(bankAccount);
                       }
                   } finally {
                    countDownLatch.countDown();
                   }
               },"t1").start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("结果::::::"+bankAccount.money);
    }
}


class BankAccount{
    String bankName = "CCB";
   public volatile int money = 0;//钱数
    AtomicIntegerFieldUpdater<BankAccount> atomicIntegerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(BankAccount.class,"money");
    //不加synchronized,还要保证多线程操作的原子性
    public void transMoney(BankAccount bankAccount){
        atomicIntegerFieldUpdater.getAndIncrement(bankAccount);
    }
}

159.png

2、例二

需求 多线程并发调用一个类的初始化方法,如果未被初始化过,将执行初始化工作 要求只能被初始化一次,只有一个线程操作成功

package com.lori.juc2023.juc8;


import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * 需求
 * 多线程并发调用一个类的初始化方法,如果未被初始化过,将执行初始化工作
 * 要求只能被初始化一次,只有一个线程操作成功
 */
public class AtomicDemo5 {
    public static void main(String[] args) {

        MyVar myVar = new MyVar();

        for (int i = 0; i < 10; i++) {
               new Thread(()->{
                         myVar.init(myVar);
                       },String.valueOf(i)).start();
        }
    }
}


class MyVar{
    public volatile Boolean isInit = Boolean.FALSE;

    AtomicReferenceFieldUpdater<MyVar,Boolean> referenceFieldUpdater =
            AtomicReferenceFieldUpdater.newUpdater(MyVar.class,Boolean.class,"isInit");

    public void init(MyVar myVar){
        if(referenceFieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE)){
            System.out.println(Thread.currentThread().getName()+"\t"+"开始初始化********");
             try {
                 TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}
            System.out.println(Thread.currentThread().getName()+"\t"+"完成初始化工作");

        }else {
            System.out.println(Thread.currentThread().getName()+"\t"+"已经有线程进行初始化工作********");
        }
    }

}

160.png

标签:JUC,int,System,原子,new,println,import,public
From: https://blog.51cto.com/u_15410237/6365094

相关文章

  • 七、JUC-CAS
    一、原子类java.util.concurrent.atomic下所有的类二、没有用CAS之前多线程下不使用原子类保证线程安全i++(基本数据类型)packagecom.lori.juc2023.juc7;publicclasscasDemo1{volatileintnumber=0;//读取publicintgetNumber(){returnnumber......
  • 正点原子imx6ull通过网线连接电脑并使用MobaXterm登录
    板子上配置静态IP注意:本文所用软件为SecureCRT与Mobaxterm 首先我们通过串口线将电脑与imx6ull进行连接。并在电脑上通过SecureCRT进入板子的linux系统①修改静态IP我们使用vi/etc/network/interfaces命令进入这个路径下的文件进行编辑,如下图把文件里面的#注释符删除就行了,当然......
  • 正点原子imx6ull中sudo命令失效问题
    问题出现事情的起因是这样的,我的imx6ull的板子很久没用了,这次重新上电之后,我习惯性的敲了一个sudo随后就发现报错sudo:errorin/etc/sudo.conf,line0whileloadingplugin`sudoers_policy'sudo:/usr/libexec/sudo/sudoers.somustbeownedbyuid0sudo:fatalerror,un......
  • 五、JUC-Java内存模型JMM
    一、一些疑问什么是Java内存模型JMMJMM与volatile他们两个之间的关系JMM有哪些特性为什么要有JMM,他们为什么出现,功能和作用是什么happens-before先行发生原则是什么二、计算机硬件存储体系计算机存储结构,从本地磁盘到主存到CPU缓存,也就是从硬盘到内存到CPU一般对应的程序......
  • 四、JUC-LockSupport与线程中断
    一、线程中断机制1、概述一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止,所以,Thread.stop、Thread.suspend、Thread.resume都已经被废弃了在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供了一种用于......
  • 【面试系列5】JUC
    并行与并发并行:多个机器同时执行并发:一个机器分时执行Future接口定义了操作异步任务执行的一些方法,获取异步任务执行的结果,取消任务的执行,判断任务是否被取消,判断任务执行完毕。多线程/有返回/异步任务classMyThread2implementsCallable<String>{@Overridepu......
  • 3.JUC【Java面试第三季】
    3.JUC【Java面试第三季】前言推荐3.JUC06_闲聊AQS面试1.题目+说明07_可重入锁理论2.可重入锁说明“可重入锁”这四个字分开来解释可重入锁的种类08_可重入锁的代码验证-上09_可重入锁的代码验证-下3.LockSupport10_LockSupport是什么LockSupport是什么11_waitNotify限制==线程等待......
  • 正点原子Linux第31章《Uboot顶层Makefile详解》学习
    uboot目录结构1. 文件夹arch:存放关于CPU架构的代码2. 文件夹board:存放关于特定开发板的代码3. 文件夹configs:存放uboot的配置,文件的格式为:xxxxx.deconfig,通过编译(make),生成.config文件。4. 文件u-boot.xxx,大多数为编译相关的或者编译生成的目标文件,例如uboot.imx等等......
  • CAS 和 原子类
    什么是CAS全称是Compare-And-Swap,对数据进行原子性操作,sun.misc.Unsafe类的各个native方法实现的比较当前工作内存中的值和主内存中的值,如果相同则执行规定操作,否则什么都不做或者重来一次,重来就是自旋锁了java各种锁看这里CASVSvolatileVSsynchronizedCAS:保证......
  • 多线程篇-线程安全-原子性、可见性、有序性解析
    在程序中使用多线程的目的是什么?1、提高效率,增加任务的吞吐量2、提升CPU等资源的利用率,减少CPU的空转多线程的应用在日常开发中很多,带来了很多的便利,让我们以前研究下在多线程场景中要注意问题吧,一般主要从这三个方面考虑1、原子性2、可见性3、有序性如果不能保证原......