多线程高并发,这一篇你就进阶了

多线程

多线程实现方式

启动线程的只有一个,代理模式Thread(proxy).start();

Runnable和Callable只能以实例的形式被代理启动线程。

1.Thread类

​ override run();

​ //运行 .start()

2.Runnable接口

没有返回值,不会抛出异常

​ override run();

​ //运行 需要代理类对象运行 new Thread(实现类).start

3.Callable接口

类似于Runnable,都是为其实例可能有另一个线程执行的类设计的

有返回值,会抛出异常

​ override call();

​ return

​ //运行方式1

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}
public class FutureTask<V> implements RunnableFuture<V> 
public FutureTask(Callable<V> callable) 
new Thread(new Runnable()).start();
new Thread(new FutureTask<V>(),"A").start();//
new Thread(new FutureTask<V>(),"B").start();//有缓存,只执行1次call(),
futureTask .get();//可能会产生阻塞,放到代码最后,或者使用异步通信处理,跑完之后在拿结果
new Thread(new FutureTask<V>(Callable).start();

​ FutureTask futureTask = new FutureTask(Callable callable);

​ FutureTask继承了Runnable接口,运行需要代理类对象运行 new Thread(实现类).start

​ //运行方式2

​ executorService.submit((Callable callable);

image-20220331185055416

线程状态

5个:1.新生状态 2.就绪状态 3.运行状态 4.阻塞状态 5.死亡状态

线程停止

不建议使用jdk提供的stop(),destory(),已废弃,使用标识控制线程停止

image-20220331192433097

线程休眠sleep()

线程阻塞,参数时间,存在异常需要捕获,休眠后会进入就绪状态,每一个对象都有一个锁,sleep不会释放锁

线程礼让yield()

当前正在执行的线程暂停,线程从运行状态转为就绪状态,不阻塞,也不一定礼让成功

image-20220331214106994

合并线程Join()

插队的意思,当前线程阻塞,执行其他线程,其他线程执行完后在执行当前线程

image-20220331214656784

线程状态getState()

枚举类型,有6个

image-20220331221039414 image-20220331221853636

线程的优先级get/setPriority()

不一定成功,但是会增加成功的权重

image-20220331222312636

守护线程setDaemon()

线程分为用户线程和守护线程,虚拟机必须确保用户线程执行完毕,而守护线程不需要等待执行完毕

image-20220331222729117

线程同步机制

多个线程操作同一个数据的时候在同一时刻会产生并发,需要同步,最可靠的保证办法就是同步(排队),此时这些线程就进入对象的等待池,形成队列,排队执行

每个对象都有一把锁,这个锁就是在线程同步时候使用,线程同步形成的条件 :队列+锁

为了保证数据的安全性使用锁,synchronized,排他锁,独占资源,其他线程必须等待,所以也存在一些问题

1.其他需要此锁的线程挂起等待,效率变低

2.在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,必然会损失性能

3.优先级高的线程等待优先级低的线程释放锁,会引起优先级倒置,引起性能问题(重要):比如说Thread A run()10H,Thread B run()1S

B要等待10小时

死锁

多个线程相互持有对方需要的资源,形成僵持

形成条件(4个同时满足)

互斥,一个资源每次只能被一个进程使用

线程协作(通信)

1.一个进程内的两个线程

2.两个进程的两个线程

1.生产者消费者管程法

2。生产者消费者信号灯法

注意,2个以上线程唤醒存在虚假唤醒,if改为while循环判断

image-20220401213413768

实现方式1.管程法

image-20220401044256344

信号灯法(flag)

ThreadLocal

将数据缓存在线程内的线程本地存储机制,该线程可在生命周期内任意方法中缓存或获取缓存的数据

底层使用ThreadLocalMap实现,是Thread的局部变量,数据结构ThreadLocalMap

image-20220331180602899

如果在线程池中使用ThreadLocal会造成内存泄漏,因为线程执行任务后不会被回收而是执行下一个任务,缓存的变量一直存在,用完后需要手动move()掉

应用场景:链接管理,一个线程持有一个链接,该链接对象可以在不同方法间传递

JUC

package java.util.concurrent;//juc,并发包
package java.util.concurrent.atomic;//原子性
package java.util.concurrent.locks;//Lock锁
package java.util.function;//函数式

降低耦合性,多线程尽量不用以前的implements Runnable实现,oop思想,抽象成一个资源类来操作

Lock

实现类有3个

image-20220401201103612

常用实现类ReentrantLock.

和Condition组合使用

public class ReentrantLock implements Lock, java.io.Serializable//可重用锁,常用
//Constructors
public ReentrantLock() {
        sync = new NonfairSync();//非公平锁
    }
//Constructors
public ReentrantLock(boolean fair) {//FairSync公平锁
        sync = fair ? new FairSync() : new NonfairSync();
    }
/**
公平锁:先来后到  非公平锁:可以插队(默认),synchronized是非公平锁
/**
private final ReentrantReadWriteLock.ReadLock readerLock;//读锁
private final ReentrantReadWriteLock.WriteLock writerLock;//写锁

ReadWriteLock

读写锁

独占锁:一次只能被一个线程占有 共享锁:一次可以被多个线程占有

ReadWriteLock维护一对关联的locks ,一个用于只读操作,一个用于写入。只要没有写者, read lock可能被多个读线程同时持有。 write lock是独占的,即:写是一个线程写,读可以多个线程读

实现类

public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {
/**
 * demo
 */
import java.util.HashMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteLockDemo {

    public static void main(String[] args) {
        MyCache cache = new MyCache();
        ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        /**
         * 写
         */
        for (int i = 0; i < 5; i++) {
            final int temp = i;
            new Thread(()->{
                try {
                    readWriteLock.writeLock().lock();//并发写入,原子性操作,需要加锁
                    System.out.println("开始写入缓存:"+temp+"...");
                    cache.set(temp+"",Integer.valueOf(temp));
                }finally {
                    readWriteLock.writeLock().unlock();
                }
            },Thread.currentThread().getName()).start();
        }
        /**
         * 读
         */
        for (int i = 0; i < 6; i++) {
            final int temp = i;
            new Thread(()->{
                System.out.println("从缓存中取出数据"+cache.get(temp+""));
            }).start();
        }
    }
}
class MyCache{
    private volatile HashMap<String, Object> cache = new HashMap<String, Object>();

    public void set(String key,Object Value){
        cache.put(key,Value);
        System.out.println(key+"已放入缓存...");
    }
    public Object get(String key){
        return key == null ? null : cache.get(key);
    }
}
开始写入缓存:1...
1已放入缓存...   	//	写是原子性操作
开始写入缓存:0...
0已放入缓存...
开始写入缓存:2...
2已放入缓存...
开始写入缓存:3...
3已放入缓存...
开始写入缓存:4...
4已放入缓存...
从缓存中取出数据0
从缓存中取出数据1
从缓存中取出数据2
从缓存中取出数据4
从缓存中取出数据null
从缓存中取出数据3

synchronized和lock对比

1.synchronized是内置关键字,Lock是一个接口

2.synchronized无法获取锁状态,Lock可以判断是否获得锁

3.synchronized隐式锁(出了作用域自动释放),lock显示锁(手动加锁解锁),不释放会产生死锁

4.synchronized如果有两个线程,并且线程B等待线程A,如果A阻塞,B就一直等待,Lock有thyLock()不一定一直等待

5.synchronized 可重入锁,不可中断,非公平锁,Lock可重入锁,可以判断锁,可以指定公平或非公平锁

6.Lock只有代码块锁,synchorized还有方法锁

7.Lock锁,JVM话费较少的时间来调度线程,性能好,有扩展性(提供更多子类)

8.可重入锁中,synchronized 只有一把锁,Lock有两把

可重入锁

锁中锁,也叫递归锁

/**
 * 可重入锁:锁中锁.synchronized的demo
 * A.lock会自动带入到B中,只有一把锁,B会阻塞
 */
public class RTTLockDemo {
    public static void main(String[] args) {
        Person person = new Person();
        new Thread(()->{
            person.eat();
        },"A").start();
        new Thread(()->{
            person.eat();
        },"B").start();
    }
}

class Person{
    public synchronized void eat(){
        System.out.println(Thread.currentThread().getName()+".eat...");
        drink();
    }
    public synchronized void drink(){
        System.out.println(Thread.currentThread().getName()+".drink...");
    }
}
A.eat...
A.drink...
B.eat...
B.drink...
/**
 * 可重入锁:锁中锁.Lock的demo
 * A.lock锁有两对,B会阻塞
 */
public class RTTLockDemo {
    public static void main(String[] args) {
        Person1 person = new Person1();
        new Thread(()->{
            person.eat();
        },"A").start();
        new Thread(()->{
            person.eat();
        },"B").start();
    }
}

class Person1{
    Lock lock = new ReentrantLock();
    public  void eat(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+".eat...");
            //eat.lock中还有一把drink.lock,
            drink();
        }finally {
            lock.unlock();
        }

    }
    public  void drink(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+".drink...");
        }finally {
            lock.unlock();
        }
    }
}
A.eat...
A.drink...
B.eat...
B.drink...

自旋锁

class MyLock{
    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(()->{
            spinLockDemo.myLock();
            System.out.println(Thread.currentThread().getName()+"线程加锁成功...");
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.myUnLock();
            System.out.println(Thread.currentThread().getName()+"线程解锁成功...");
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            spinLockDemo.myLock();
            System.out.println(Thread.currentThread().getName()+"线程加锁成功...");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.myUnLock();
            System.out.println(Thread.currentThread().getName()+"线程解锁成功...");
        },"B").start();

    }
}
/**
 * 自旋锁
 */
public class SpinLockDemo {

    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void myLock(){
        System.out.println(Thread.currentThread().getName()+"加锁中...");
        //CAS自旋加锁
        while (!atomicReference.compareAndSet(null,Thread.currentThread())){

        }
    }
    //解锁
    public void myUnLock(){
        System.out.println(Thread.currentThread().getName()+"解锁中...");
        //CAS解锁
        atomicReference.compareAndSet(Thread.currentThread(),null);
    }
}
A加锁中...
A线程加锁成功...
B加锁中...
A解锁中...
A线程解锁成功...
B线程加锁成功...
B解锁中...
B线程解锁成功...

死锁

两个线程相互持有对方需要的资源,形成僵持

package thread;

import java.util.concurrent.TimeUnit;

class TestDeadlockDemo{
    public static void main(String[] args) {
        String A = "A";
        String B = "B";
        new Thread(new DeadlockDemo(A,B),"a线程").start();
        new Thread(new DeadlockDemo(B,A),"b线程").start();
    }
}
public class DeadlockDemo implements Runnable {

    private String A;
    private String B;

    public DeadlockDemo(String A,String B){
        this.A = A;
        this.B = B;
    }

    @Override
    public void run() {
        synchronized (A){
            System.out.println(A+B);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (B){
                System.out.println(B+A);
            }
        }
    }
}

排查死锁

jps -l查看进程号,jstack查看堆栈信息

C:\hot\devlopt\jdk\jdk8\bin目录下执行

PS C:\hot\devlopt\jdk\jdk8\bin> jps -l
13760 thread.TestDeadlockDemo
6608 org.jetbrains.jps.cmdline.Launcher
17436
5324 sun.tools.jps.Jps
PS C:\hot\devlopt\jdk\jdk8\bin> jstack 13760
........
Found one Java-level deadlock:
=============================
"b线程":
  waiting to lock monitor 0x000001b8fafdc978 (object 0x00000000d6377d58, a java.lang.String),
  which is held by "a线程"
"a线程":
  waiting to lock monitor 0x000001b8fafd9f88 (object 0x00000000d6377d88, a java.lang.String),
  which is held by "b线程"

Java stack information for the threads listed above:
===================================================
"b线程":
        at thread.DeadlockDemo.run(DeadlockDemo.java:33)
        - waiting to lock <0x00000000d6377d58> (a java.lang.String)
        - locked <0x00000000d6377d88> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:750)
"a线程":
        at thread.DeadlockDemo.run(DeadlockDemo.java:33)
        - waiting to lock <0x00000000d6377d88> (a java.lang.String)
        - locked <0x00000000d6377d58> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:750)

Found 1 deadlock.

8锁现象

方法上加synchronized, 锁的对象是方法的调用者,谁先拿到谁执行

锁的东西就两个,一个是方法的调用者:this.一个是Class

static,静态的,类一加载就执行,锁的是类模板,全局唯一,只有一个类对象,对象.class。static锁,不管几个对象调用,锁的是该唯一对象

线程协作(通信)

是不是可以使用到流程里?

传统的通信 syncroized关键字和Object的wait(), notify/nofifyAll(),多个线程就会出现问题

juc通信 Lock接口和Condition接口的await(), signal()/signalAll()

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
condition.await();
condition.signal();

优势:Condition可以做到精准通知,如Thread A,B,C… A执行完B执行,B执行完C执行…

package juc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class JucCommunication {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                data.test1();
            }
            },"线程A").start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                data.test2();
            }
            },"线程B").start();
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                data.test3();
            }
            },"线程C").start();
    }
}

/**
 * 资源类
 */
class Data {
    /**
     * 执行方法标识
     */
    private int flag = 1;
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();
     protected void test1(){
        lock.lock();
        //判断,执行,通知
        try {
            while (flag !=1){
                condition1.await();
            }
            //执行
            System.out.println(Thread.currentThread().getName()+"AAAAAA");
            flag = 2;
            condition2.signalAll();//精准通知
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
        lock.unlock();
        }
    }

    protected void test2() {
        lock.lock();
        try {
            while (flag !=2){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"BBBBBB");
            flag = 3;
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    protected void test3() {
        lock.lock();
        try {
            while (flag !=3){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"CCCCC");
            flag = 1;
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

不安全的集合

1.List

add是扩容操作,并发下会fail fast机制 提前失败

解决1.Vector :add加了synchronized

解决2.Collections工具类转换:Collections.synchronizedList(arrayList)

解决3.juc并发包,用CopyOnWriteArrayList代替ArrayList:读写分离

2.Set

public HashSet() {
    map = new HashMap<>();//hashSet就是hashMap
}
public boolean add(E e) {
        return map.put(e, PRESENT)==null; //map的key是唯一的,所以set是不重复的
    }

解决1.Collection工具类转换:Collections.synchronizedSet(HashSet)

解决2.juc并发包,用CopyOnWriteArraySet

3.Map

解决1.juc并发包,用ConcurrentHashMap

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final float DEFAULT_LOAD_FACTOR = 0.75f;

队列:Queue

类似于管道,先进先出。FIFO

如果队列满了,写就阻塞,需要等前面的元素拿出去后在可写入

反之,如果队列是空,读就阻塞,等待写入(生产)

队列的方法只有两种,添加元素和删除元素

4组API

1.抛出异常 2.不抛出异常 3.阻塞等待 4.超时等待

操作 抛出异常 不抛异常返回布尔 阻塞等待 超时等待
添加 add(E e) offer(E e) put(E e) offer(E e, long timeout, TimeUnit unit)
删除 remove() poll() take() poll(long timeout, TimeUnit unit)
获取队首元素 element() peek() - -
import java.util.concurrent.ArrayBlockingQueue;

public class BlockingQueueDemo {
    public static void main(String[] args) {
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
        for (int i = 0; i < 3; i++) {//添加超过队列长度抛出异常
            arrayBlockingQueue.add(i);
            System.out.println("添加元素"+i);
        }
        for (int i = 0; i < 4; i++) {//取出长度超过队列长度报错
            System.out.println(arrayBlockingQueue.remove());
        }
    }
}
添加元素0
添加元素1
添加元素2
0
1
2
Exception in thread "main" java.util.NoSuchElementException

阻塞队列

BlockingQueue

多线程和线程池中用的多

非阻塞队列

AbstractQueue

双端队列

Deque

同步队列

SynchronousQueue:只能存一个元素,只有take()空了才能put()进去,会阻塞

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

public class SynchronousQueueDemo {
    public static void main(String[] args) {
        BlockingQueue synchronousQueue  = new SynchronousQueue<>();

        new Thread(()->{
            for (int i = 0; i < 3; i++) {
                try {
                    System.out.println("put:"+i);
                    synchronousQueue.put(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(()->{
            for (int i = 0; i < 3; i++) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(synchronousQueue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}
put:0
0
put:1
1
put:2
2

.线程池

池化技术:优化资源使用

1.降低资源消耗 2.提高相应速度 3.方便管理(线程复用,控制最大并发数,管理线程)

线程频繁的创建与销毁对资源的开销很大,引人线程池,重复利用

作用:提高响应速度,降低资源消耗,便于管理线程(核心线程数corePoolSize(),最大线程数maxmumPoolSize(),超时销毁机制keepAliveTime())

ExecutorService线程池接口

Executor工具类线程池的工厂类

Callable和Runnable都可以使用这个ExecutorService线程池

image-20220401174423705 image-20220401174506761
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorServiceDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();//一个线程
        ExecutorService threadPool1 = Executors.newFixedThreadPool(5);//5个线程
        ExecutorService threadPool2 = Executors.newCachedThreadPool();//线程数可伸缩
        try {
            for (int i = 0; i < 100; i++) {
                threadPool2.execute(()->{
                    System.out.println(Thread.currentThread().getName());
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool1.shutdown();
        }
    }
}
pool-3-thread-1
pool-3-thread-1
pool-3-thread-2
pool-3-thread-3
pool-3-thread-4
pool-3-thread-5
pool-3-thread-2
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
    /**newSingleThreadExecutor默认的拒绝策略
    *private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
    */
}
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,//约21亿,容易导致oom移除
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
//本质走的是这个方法创建线程池
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小     平时开启线程的个数
                              int maximumPoolSize,//最大线程数    阻塞队列满了会触发线程最大数
                              long keepAliveTime,//超时回收    核心线程外的其他线程超时没有任务回收
                              TimeUnit unit,//超时时间单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory,//线程工厂,不变
                              RejectedExecutionHandler handler) {//拒绝策略   >最大线程数+阻塞队列数触发

不用上面的Executor创建线程用原生ThreadPoolExecutor

import java.util.concurrent.*;

public class ExecutorServiceDemo {
    public static void main(String[] args) {
        /**
         *     @Range(from = 0, to = Integer.MAX_VALUE) int corePoolSize,
         *     @Range(from = 1, to = Integer.MAX_VALUE) int maximumPoolSize,
         *     @Range(from = 0, to = Long.MAX_VALUE) long keepAliveTime,
         *     @NotNull TimeUnit unit,
         *     @NotNull BlockingQueue workQueue,
         *     @NotNull ThreadFactory threadFactory,
         *     @NotNull RejectedExecutionHandler handler
         */
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
            /**
            *任务分cpu密集型,IO密集型
            *cpu密集型:计算,逻辑等任务较多,IO型操作较少。线程数=cpu个数/cpu+1(超线程)
            *IO密集型:网络的请求,文件的操作,数据库的交互。线程数=(1+线程等待的时间/线程cpu计算时间)*cpu个数*cpu使用率
            *一般线程数=cpu个数*2
            */
                Runtime.getRuntime().availableProcessors(),//获取cpu核数
                2,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
//                new ThreadPoolExecutor.AbortPolicy() 直接拒绝,抛出异常
//                new ThreadPoolExecutor.CallerRunsPolicy()//哪个线程送过来返回给哪个线程执行,>maximumPoolSize+workQueue
//                new ThreadPoolExecutor.DiscardPolicy()拒绝,不抛出异常
                new ThreadPoolExecutor.DiscardOldestPolicy()//>maximumPoolSize+workQueue,尝试和最早的竞争,竞争不上丢弃,不抛异常
        );
        try {
            for (int i = 0; i < 100; i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName());
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
}

用ThreadPoolExecutor创建

juc辅助类

1.CountDownLatch

减法计数器

//Constructs
public CountDownLatch(int count) {//要等待线程的个数
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
/**
 * 使当前线程进入同步阻塞队列进行等待,直到计数器的值减到0或者当前
 * 线程被中断,当前线程就会被唤醒
 */
public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
/**
 * 带有超时时间的
 */
public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }
/**
 * 计数器-1,减到0则会唤醒所有等待在这个CountDownLatch上的线程
 */
public void countDown() {
        sync.releaseShared(1);
    }
/**
 * 获得计数器的值
 */
public long getCount() {
        return sync.getCount();
    }

//demo
public static void main(String[] args) throws InterruptedException {

//        CountDownLatch startSignal = new CountDownLatch(1);
        CountDownLatch doneSignal = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
            final int temp = i;//lambda里的i好像是final的,lambda也被称为闭包???
            new Thread(()->{
                //lambda无法访问外部变量,通过fianl型的中间变量获取,所以不能直接拿到i
                //直接拿i报错,java: 从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量
                System.out.println("第"+ temp +"个线程"+Thread.currentThread().getName()+"is running");
                doneSignal.countDown();//执行一个线程计数器减1
            }).start();

        }
        doneSignal.await();//阻塞当前main线程,计数器的值减到0会被唤醒
        System.out.println("main Thread is running...");
    }

作用和场景

1.实现最大并发性

2.开始执行前等待其他线程各自执行完后进行汇总合并,如使用多线程统计数据后做一个汇总

确定,一次性工具类,计数器的值只能在构造函数中初始化一次,之后不能再次赋值,使用完后不能再次使用

2.CyclicBarrier

加法计数器

/**
 *重置计数器,可重复使用
 */
public void reset() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        breakBarrier();   // break the current generation
        nextGeneration(); // start a new generation
    } finally {
        lock.unlock();
    }
}
/**
 *获得阻塞线程数量
 */
public int getNumberWaiting() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return parties - count;
    } finally {
        lock.unlock();
    }
}
/**
 *阻塞线程是否被中断
 */
public boolean isBroken() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return generation.broken;
    } finally {
        lock.unlock();
    }
}
/**
 *demo
 */
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(10,()->{System.out.println("开始汇总...");});
        for (int i = 1; i <= 10; i++) {
            final int temp = i;
            new Thread(()->{
                try {
                    System.out.println(temp+"等待");
                    cyclicBarrier.await();//阻塞当前线程
                    System.out.println(temp+"开始执行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
1等待
2等待
3等待
4等待
5等待
6等待
7等待
8等待
9等待
10等待
开始汇总...
10开始执行
1开始执行
2开始执行
3开始执行
5开始执行
6开始执行
4开始执行
9开始执行
8开始执行
7开始执行

案例:模拟人都到齐,一声令下,所有人都干活

使用场景:多线程计算数据,最后合并计算结果。

CyclicBarrier和CountDownLatch的区别

CountDownLatch:阻塞当前线程,待其他线程完成后,自己在执行

CyclicBarrier:多个线程一起阻塞,达到屏障后所有线程一起执行

3.Semaphore

计数许可信号量,多个共享资源的互斥使用,并发限流,控制最大并发线程数。可以用来做限流,控制同时访问资源的线程或用户数量

/**
 *Constructs
 *permits:许可数量。即同时允许多少线程访问
 */
public Semaphore(int permits) {
    sync = new NonfairSync(permits);//默认非公平
}
/**
 *Constructs
 *permits:许可数量。即同时允许多少线程访问
 *fair:是否是公平的,
 *FairSync(permits):按照请求时间获得许可,即,先发送请求的先活得许可
 *NonfairSync(permits):先发送请求的未必先获得许可,有助于提高系统的吞吐量,但是可能导致某些线程始终获取不到许可
 */
public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
/**
 *Method
 *获取一个许可
 */
public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
/**
 *归还一个许可,有才可以还
 */
public void release() {
        sync.releaseShared(1);
    }
/**
 *获取多个许可
 */
public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }
/**
 *归还多个许可,归还<=已获得
 */
public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }
/**
 *上面4个方法都是会阻塞的,获取许可非阻塞的方法tryAcquire()/tryAcquire(int permits)/
 *tryAcquire(long timeout, TimeUnit unit)/tryAcquire(int permits, long timeout, TimeUnit unit)
 */

/**
 *获取剩余的许可信号量个数
 */
public int availablePermits() {
        return sync.getPermits();
    }
/**
 *demo
 */
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreDemo {
    private static final int MAX_THREAD_NUM = 6;
    private static final int NOW_PERSON_NUM = 10;
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(MAX_THREAD_NUM);
        for (int i = 0; i < NOW_PERSON_NUM; i++) {
            final int temp = i;
            new Thread(()->{
                try {
                    semaphore.acquire();//得到信号量
                    System.out.println(temp+"抢到票");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(temp+"离开");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();//释放信号量
                }
            }).start();
        }
    }
0抢到票
1抢到票
2抢到票
3抢到票
4抢到票
5抢到票  //6个位置都没了
0离开
2离开
1离开		//腾出3个位置
6抢到票
7抢到票	//剩余1个位置
3离开		//剩余2个位置
8抢到票	//剩余1个位置
9抢到票	//剩余0个位置
4离开
5离开
6离开
8离开
9离开
7离开		//都有票了,腾出6个位置

函数式接口

jdk1.8以后简化编程模型,4大函数式接口,新版本框架底层大量应用

函数式接口都可简化成lambda表达式

1.Function 函数式接口 ,一个参数,一个返回值

/**
* Source
 */
package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Function<T, R> {//传入参数T类型,返回参数R类型

    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

/**
* demo
 */
import java.util.function.Function;

public class FunctionDemo {
    public static void main(String[] args) {
        Function<Integer,String> function = new Function<Integer,String>() {
            @Override
            public String  apply(Integer s) {
                return s.toString();
            }
        };
        /**
         * 所有的函数式接口都可简化为Lambda表达式
         */
        Function<Integer,String> function2 = (num)->{//num的括号可以省略
            return num.toString();
        };
        for (int i = 0; i < 3; i++) {
            System.out.println(function2.apply(999));
        }
    }
}
999
999
999

2.Predicate:断定型接口,有一个参数,返回boolean

/**
* Source
 */
package java.util.function;

import java.util.Objects;

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}
/**
* demo
 */
import java.util.function.Predicate;

public class PredicateTest {
    public static void main(String[] args) {
        Predicate<String> predicate = new Predicate<String>() {
            @Override
            public boolean test(String str) {
                return str.isEmpty();
            }
        };
        Predicate<String> predicate1 = str->{return str.isEmpty();};
        System.out.println(predicate.test(""));
        System.out.println(predicate1.test("asd"));
    }
}
true
false

3.Supplier:供给型接口:没有参数,只有返回值

/**
* Source
 */
package java.util.function;
@FunctionalInterface
public interface Supplier<T> {
    
    T get();
}
/**
* demo
 */
@Test
    public void testSupplier(){
        Supplier supplier = new Supplier() {
            @Override
            public String get() {
                return "hello";
            }
        };
        Supplier supplier1 = ()->{return "rock";};
        System.out.println(supplier.get());
        System.out.println(supplier1.get());
    }
hello
rock

4.Consumer:消费型接口 只有输入值,没有返回值

/**
* Source
 */
package java.util.function;
import java.util.Objects;

@FunctionalInterface
public interface Consumer {
    void accept(T t);
    
    default Consumer andThen(Consumer after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}
/**
* demo
 */
@Test
    public void testConsumer(){
        Consumer consumer = new Consumer() {
            @Override
            public void accept(String str) {
                System.out.println(str);
            }
        };
        consumer.accept("123");

        Consumer consumer1 = str->{System.out.println(str);};
        consumer1.accept("rock");
    }
123
rock

流式计算

集合存储,流计算

/**
*source 
*Collection类型转换成Stream类型
*/    
default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
//Stream接口的过滤方法,参数是断定型接口类型
Stream<T> filter(Predicate<? super T> predicate);
/**
*demo 
*/
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;

/**
 * 1.id为偶数的用户
 * 2.年龄必须大于23岁
 * 3.用户名字母倒着排序
 * 4.用户名转为大写
 * 5.只输出一个用户
 */
public class StreamTest {

    List<Person> list = new ArrayList<Person>();

    @Test
    public void test(){
        for (int i = 0; i < 10; i++) {
            list.add(new Person(i, "hot"+i,i*i));
        }
        System.out.println(list.get(list.size()-1).getAge());
        /**
        *List转换成Stream流
        *链式编程
        */
        list.stream().filter((u)->{return u.getId()%2==0;})//id为偶数的用户
            .filter((u)->{return u.getAge()>23;})//年龄必须大于23岁
            .sorted((u1,u2)->{return u2.getName().compareTo(u1.getName());})//用户名字母倒着排序
            .map((u)->{ u.getName().toUpperCase();//用户名转为大写
                 return u;})
            .limit(1)//只输出一个用户
            .forEach(System.out::println);
    }

ForkJoin

jdk1.7的,计算大数据量使用,几十亿。一个大任务分成几个子任务处理,结果汇总,会工作窃取,A任务先执行完后会窃取B任务执行,任务是双端队列,从尾部窃取,也会存在竞争同一个任务,但优大于劣,适合于cpu密集型任务处理 fork/join和map/reduce的意思差不多

public interface ExecutorService extends Executor {
public abstract class AbstractExecutorService implements ExecutorService {
image-20220403211750348
public Future<?> submit(Runnable task) {
    if (task == null) throw new NullPointerException();
    RunnableFuture<Void> ftask = newTaskFor(task, null);
    execute(ftask);
    return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }
public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
//Runnable,Callable都返回的是FutureTask
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }

步骤:1.大任务分割为多个子任务 2.通过Callable获取返回值提交给线程池处理 3.FutureTask接受任务 4.在主线程通过FutureTask.get()阻塞式的获取结果 5.汇总

import java.util.concurrent.RecursiveTask;

public class ForkJoinDemo extends RecursiveTask<Long> {

    private Long start;
    private Long end;
    private Long temp = 10000L;

    public ForkJoinDemo(Long start,Long end){
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        if(end-start<temp){
            Long sum = 0L;
            for (long i = start; i <=end ; i++) {
                sum+=i;
            }
            return sum;
        }else {
            long middle = (start+end)/2;
            ForkJoinDemo task1 = new ForkJoinDemo(start,middle);
            task1.fork();//任务1压入线程队列
            ForkJoinDemo task2 = new ForkJoinDemo(middle+1,end);
            task2.fork();
            return task1.join()+task2.join();
        }
    }
}

import org.junit.Test;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

public class TestForkJoinDemo {
    private Long num = 10_0000_0000L;

    /**常规计算
     * 500000000500000000
     * 6730
     */
    @Test
    public void test1(){
        Long sum = 0L;
        Long startTime = System.currentTimeMillis();
        for (Long i = 1L; i <= num; i++) {
            sum+=i;
        }
        Long endTime = System.currentTimeMillis();
        System.out.println(sum);
        System.out.println(endTime-startTime);
    }

    /**ForkJoin计算
     * 500000000500000000
     * 54378
     */
    @Test
    public void test2() throws ExecutionException, InterruptedException {
        Long startTime = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> forkJoinTask = new ForkJoinDemo(0L,10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(forkJoinTask);//提交任务
        Long sum = submit.get();
        Long endTime = System.currentTimeMillis();
        System.out.println(sum);
        System.out.println(endTime-startTime);
    }

    /**并行流计算
     * 359
     * 500000000500000000
     */
    @Test
    public void test3()  {
        Long startTime = System.currentTimeMillis();
        Long sum = LongStream.rangeClosed(0L,10_0000_0000L).parallel().reduce(0,Long::sum);
        Long endTime = System.currentTimeMillis();
        System.out.println(endTime-startTime);
        System.out.println(sum);
    }
}

Future

多线程异步回调

JMM

Java内存模型,不存在的,只是概念,规范,有4对,8种操作,必须成对使用,和{},“”,()一个道理

image-20220404194002789 image-20220404194305974

根据上图为了保证线程直接的可见性,便引人了Volatile

Volatile

1.保证可见性
import java.util.concurrent.TimeUnit;

public class VolatileTest {
    /**
     * volatile保证主内存中的数据对其他线程可见性
     */
    private volatile static  int num = 0;
    public static void main(String[] args) throws InterruptedException {

       new Thread(()->{
           while (num == 0){
               System.out.println(num);}
       }).start();
       TimeUnit.SECONDS.sleep(2);
       num = 1;
       System.out.println(num);
    }
}
0
0
0
1
2.不保证原子性
public class VolatileTest {
    /**
     * volatile不保证原子性
     */
    //private volatile static  int num = 0;
    private volatile static  AtomicInteger num = new AtomicInteger(0);//CAS,效率很高
    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }
        /**
         * main和gc。礼让,保证其他线程先执行完
         */
        while (Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(num);
    }

    /** bytecode
0 getstatic #9  //获取值
3 iconst_1
4 iadd									  //+1
5 putstatic #9  //设置值
8 return
     * num++是分几个步骤完成的,volatile保证不了原子性,结果必然<=10000
     * synchroined和lock保证原子性但开销大
     * 可以使用CAS原子类java.util.concurrent.atomic.AtomicInteger;//Atomicxxx
     * CAS比锁高效很多倍
     */
    private static void add(){
        //num++;
        num.incrementAndGet();
    }
}
//9347
10000
image-20220404204852271
3.防止指令重排

指令重排:code–>编译器优化重排–>指令并行重排–>内存系统重排–>执行

int x = 1;//1
int y = 2;//2
x = x+5;  //3
y = x*x;  //4
期望执行顺序1234,但可能会编程21341324
不可能4123。指令重排会处理数据之间的依赖数据
    
int a,b,x,y=0;
ThreadA      ThreadB
  x=a//1		y=b//3
  b=1//2		a=2//4
期望的执行顺序是1234.x=a,y=b,b=1,a=2; 
由于12没有依赖关系指令重排可能顺序是21,同理,4,3
 b=1//2		a=2//4
 x=a//1		y=b//3   
 得出诡异的结果a=2,y=11000W次可能不会出现指令重排造成的问题,但逻辑上是存在会产生诡异结果的
 volatile就是避免指令重排

实现原理

内存屏障

image-20220404223612642

volatile内存屏障在单例中使用很多

单例模式
饿汉式
/**
 * 饿汉
 * @author Rock
 */
public class Hungry {
    /**
     * 类一加载就占用浪费了很多内存资源,所以有了懒汉单例
     */
    private byte[] data1 = new byte[1024*1024];
    private byte[] data2 = new byte[1024*1024];
    private byte[] data3 = new byte[1024*1024];
    private byte[] data4 = new byte[1024*1024];
    /**
     * 单例构造器私有,外部无法new对象,保证对象唯一
     */
    private Hungry(){

    }
    private static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance(){
        return HUNGRY;
    }
}
懒汉式
public class LazyMan {
    private LazyMan(){
        System.out.println(Thread.currentThread().getName());
    }
    private  static LazyMan lazyMan;

    /**
     * 单线程下没有问题,多线程情况下会有问题,所以就有了DCL懒汉式
     * @return
     */
    public static LazyMan getInstance(){
        if(lazyMan == null){
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                getInstance();
            }).start();
        }
    }
}
Thread-0
Thread-1
DCL懒汉式
public class LazyMan {
    private LazyMan(){
        System.out.println(Thread.currentThread().getName());
    }
    //防止指令重排
    private volatile static LazyMan lazyMan;

    /**
     * 双重检测锁模式,DCL懒汉式单例
     * @return
     */
    public static LazyMan getInstance(){
        if(lazyMan == null){
            synchronized (LazyMan.class){
                if(lazyMan == null){
                    /**
                     * 1.分配内存空间
                     * 2.执行构造方法进行初始化对象
                     * 3.对象指向内存空间
                     * 指令重排顺序有可能是132,执行13此时如果其他线程进入就会发现lazyMan != null
                     * 构造方法没执行,lazyMan指向的空间是空的,所以必须加上volatile
                     * DCL懒汉式 = 双重检测所+volatile保证多线程下也是单例的
                     */
                    lazyMan = new LazyMan();
                }
            }
        }
        return lazyMan;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                getInstance();
            }).start();
        }
    }
}
Thread-0
/**
 * 反射破坏单例
 * 1.通过无参构造器反射出新对象
 */
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        LazyMan instance = LazyMan.getInstance();//正常获取对象1
        Constructor<LazyMan> declaredConstructors = LazyMan.class.getDeclaredConstructor();
        declaredConstructors.setAccessible(true);//禁用访问安全检查,速度会提升
        LazyMan instance1 =declaredConstructors.newInstance();//反射获取对象2
        System.out.println(instance);
        System.out.println(instance1);
    }
single.LazyMan@7f31245a
single.LazyMan@6d6f6e28
 /**
 * 解决,构造器中类模板上锁
 */
private LazyMan(){
//        System.out.println(Thread.currentThread().getName());
        synchronized (LazyMan.class){
            if(lazyMan!=null){
                throw new RuntimeException("构造器已锁死");
            }
        }
    }
/**
 * 2中的instance也反射获取
 */
LazyMan instance =declaredConstructors.newInstance();

single.LazyMan@7f31245a
single.LazyMan@6d6f6e28
/**
 * 解决:锁的同时判断标识
 */   
private static Boolean refectFlag = false;
    synchronized (LazyMan.class){
                if(refectFlag == false){
                    refectFlag = true;
                }else{
                    throw new RuntimeException("标识防止反射二次创建");
                }
                if(lazyMan!=null){
                    throw new RuntimeException("构造器已锁死");
                }
            }
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at single.LazyMan.main(LazyMan.java:67)
Caused by: java.lang.RuntimeException: 标识防止反射二次创建
	at single.LazyMan.<init>(LazyMan.java:15)
	... 5 more
 /**
 * 标识也可以破解
 */ 
Field flagField = LazyMan.class.getDeclaredField("refectFlag");//获取字段
//反射第一个对象instance后在设置字段值
flagField.set(instance,false);
静态内部类单例
/**
 * 静态内部类单例
 */
public class Holder {
    private Holder(){

    }
    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }

    public static class InnerClass{
        private static final Holder HOLDER = new Holder();
    }
}
枚举单例
/**
 * 反射不能破坏枚举单例
 */
public enum EnumSingle {
    INSTANCE;

    public EnumSingle getInstance(){
        return INSTANCE;
    }
}
class TestEnum{
    EnumSingle single = EnumSingle.INSTANCE;
}

CAS

compareAndSwap,compareAndSet更符合java的操作思想。比较并交换

比较当前工作内存中的值和主内存中的值,如果是期望的值,那么交换,如果不是就一直循环

atomicInteger.incrementAndGet();
public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;//valueOffset:内存地址偏移值
    }
/**
 * Unsafe类是java调用c++操作内存的类库,Java不能直接操作内存,Java通过native调用C++操作内存
 */
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;

static {
    try {
        valueOffset = unsafe.objectFieldOffset//获取内存偏移值
            (AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}

private volatile int value;
public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);//获取内存中的值
            /**
             * CAS,期望,更新;
             * var1(对象本身)var2(static时获取的内存地址偏移值)是var5(内存中的值),那么交换成var5 + var4(传进来的1)
             * while自旋
             * var5 + var4:直接操作的内存值,效率高	
             */
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

自旋会耗时,但比java的效率高很多

一次性只能保证一个共享变量的原子性,但也够使用

ABA问题:CAS有ABA问题

a = 2;ThreadA操作了 a+1;a+4;a-3…a=2;。ThreadB获取a,a没有变,但是a经过A一系列的操作B不知情,所以此a非彼a

image-20220405022655529
import java.util.concurrent.atomic.AtomicInteger;

public class ABADemo {
    @Test
    public void test(){
        AtomicInteger a  = new AtomicInteger(10);
        //true
        System.out.println(a.compareAndSet(10, 20));
        //20
        System.out.println(a.get());
        //true
        System.out.println(a.compareAndSet(20, 10));
        //10
        System.out.println(a.get());

        //true
        System.out.println(a.compareAndSet(10, 30));
        //30
        System.out.println(a.get());
    }
}

解决:原子引用 AtomicStampedReference

带版本号的原子操作

其他知识点

Integer对象使用的缓存机制,默认范围是-128~127,只要超过这个范围就会new。推荐使用静态工厂方法valueOf()获取对象实例,而不是new,因为valueOf()从缓存IntegerCache.cache中取值,而new会创建新的对象分配新的内存空间,同理,其他包装类也看一下,泛型是包装类型的一定要注意引用问题

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
@Test
public void testInteger(){
    Integer x = 127;
    Integer a = 127;
    Integer y = 128;
    Integer c = 128;
    System.out.println(x == a);//true
    System.out.println(y == c);//false
    System.out.println(x.equals(a));//true
    System.out.println(y.equals(c));//true
}

Atomic类会在热点数据上损耗性能,用LongAddr

你可能感兴趣的:(java,开发语言,数据结构)