0
点赞
收藏
分享

微信扫一扫

八、JUC-原子类

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

举报

相关推荐

0 条评论