JUCxu学习整理

JUC 知识点 - 全集

juc是什么 ?

java.util.concurrent
java.util.concurrent.locks锁相关API
java.util.concurrent.atomic原子性操作api
线程安全,灵活

JUC 分类总结 ?

可重入锁 ReentrantLock,读写锁 ReadWriteLock,邮戳锁 StampedLock,等待通知 Condition,阻塞唤醒 LockSupport
原子类基本类型,数组类型,引用类型,字段类型 【AtomicLong】【AtomicLongArray】【AtomicReference】【AtomicReference】
阻塞队列非阻塞队列ConcurrentLinkedDeque,阻塞队列 【BlockingDeque,AbstractQueue】,ArrayBlockingQueue,ConcurrentLinkedQueue,同步队列,延迟队列,优先级PriorityQueue
线程池【单一,可缓存,固定大小,周期】,自定义线程池,【forkjoin poll】分而治之,工作窃取 , future callable
并发集合CopyOnWriteArrayList,CopyOnWriteArraySet,ConcurrentHashMap
并发工具类限流作用的 信号量 Semaphore,发令枪:CountDownLatch,循环栅栏 CyclicBarrier ,ThreadLocalRandom

JUC 三大核心思想

AQSAbstractQueuedSynchronizer 抽象同步器 juc 锁 核心思想
CASCompareAndSwap 比较与替换 不加锁的情况下,实现原子性更新,不需要cpu切换上下文,cpu性能浪费
volatile可见性,主内存数据,与线程副本内部数保持一致

Lock

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iBWRYdfE-1621751537636)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210411155432950.png)]

悲观锁:取数据时,担心数据被其他线程修改,所以悲观的加上一把锁,悲观锁。一刀切的做法,降低性能。线程安全。ReentrantLock
乐观锁:取数据时,不担心数据被其他线程修改,所以不加锁,等待更新时才去检查数据是否被修改。如果检查数据被修改,就更新失败。不切换上下文,浪费cpu。【CAS】

共享锁:读锁,多条线程可以持有线程锁。可以认为不加锁
独占锁:互斥锁,一条线程持有锁。

公平锁:先到先得获取锁,有序获取。降低了性能。
非公平锁:无须,靠运气,提供了性能,

自旋锁:CAS思想:不选自旋,浪费cpu,不会阻塞。
非自旋锁:造成线程等待。

可中断锁:发送信号,中断获取锁,提高了灵活性。
非可中断锁:不能中断获取锁,会一直等待锁的释放。

可重入锁:能获取锁多次,一定程度避免死锁
非可重入锁:不可以多次获取锁,只能获取一次。

ReentrantLock

特点

  • 支持了锁超时
  • 支持可中断
  • 支持公平锁、非公平锁
  • 获取到锁的结果
  • condition 等待通知
  • 可重入的锁

API

lock获取锁
unlock释放锁
boolean trylock()尝试获取锁
boolean trylock(time,unit)限定时间内获取锁
lockInterruptibly()相应中断
newCondition()等待通知队列

可重入锁

  不可重入:线程只能获取一把锁。容易造成死锁。sun.lock

  可重入:线程可以获取多次同一把锁。避免死锁  
package com.company;

import sun.misc.Lock;

/**
 * 不可重入锁
 */
public class NoLockDemo {

    public static void main(String[] args) {

        Lock lock=new Lock();
        try {
            lock.lock();
            System.out.println("首次获取锁");
            lock.lock();
            System.out.println("再次获取锁");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
package com.company;


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

/**
 * 可重入锁
 */
public class NoLockDemo {

    public static void main(String[] args) {

        Lock lock=new ReentrantLock();
        try {
            lock.lock();
            System.out.println("首次获取锁");
            lock.lock();
            System.out.println("再次获取锁");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

可重入锁原理

 内部维护了一个计数器。获取一次锁就加一次。释放一次锁,就减去一次。 

怎么算拿到锁?

就是AQS内部 维护了一个 变量 state 等于0 表示无锁,等于1表示获取到锁,大于1 就是可重入锁。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pyTraeHg-1621751537638)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210413201706542.png)]

公平&非公平锁

公平锁:关心队列,关心顺序,先到先得。打饭

非公平锁:不关心有序,不关心队列情况。土匪

案例演示

package com.company;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 *  公平锁  构造方法  true:
 *
 *  非公平锁  构造方法  false 默认 是 非公平锁:
 *
 */
public class ImpartialLockTest {
    
    //构造方法缺省是非公平锁,false 公平锁,true 就是公平锁
   Lock lock  =new ReentrantLock(true);

    public void getLocks() {
            lock.lock();//非公平不排队,公平锁会排队
            System.out.println(Thread.currentThread().getName()+" 获取到锁");
            lock.unlock();
    }

      public static void main(String[] args) {
        //同步资源
          ImpartialLockTest tt=new ImpartialLockTest();
          new Thread(()->{
              tt.getLocks();
          },"线程A").start();

          new Thread(()->{

              tt.getLocks();

          },"线程B").start();
        }
}

原理

   非公平锁:先获取锁通过CAS,如果失败了就在尝试获取锁,尝试获取锁(无锁,获取锁,检查当前线程否是是自己)如果成功就结束,否则就把自己包装成一个Node节点放到阻塞队列里,并且释放掉阻塞。

  公平锁:先判断阻塞队列里是否有等待的节点。如果有就执行就会把自己包装成Node节点去等待(可重入检查),否则就通过CAS更新获取锁。      

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zCUSUIeA-1621751537642)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210413203139849.png)]

中断获取锁线程

lockInterruptibly()  throws InterruptedException       //Thread.interrupt()

尝试获取锁&获取锁超时

   boolean tryLock :尝试获取锁 ,获取成功返回true,否则返回false,不等待 

   boolean tryLock(time,unit),指定时间内获取成功返回true,否则返回false,等待 

案例

package com.company;

import java.util.concurrent.locks.ReentrantLock;

public class MyTryLock {
public ReentrantLock lock = new ReentrantLock();
 
public void serviceMethod(){
    try {
        if (lock.tryLock()) {
      // if (lock.tryLock(3,TimeUnit.SECONDS)) {
         if (lock.tryLock(3, TimeUnit.SECONDS)) {
            System.out.println(Thread.currentThread().getName() + "获取锁定 ");
            Thread.sleep(4000);
        } else {
            System.out.println(Thread.currentThread().getName() + "未获取锁定");
        }
    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}
public static void main(String[] args) {
    //同步资源
    MyTryLock myTryLock = new MyTryLock();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            myTryLock.serviceMethod();
        }
    };
    Thread aa = new Thread(runnable, "aa");
    aa.start();
    Thread bb = new Thread(runnable, "bb");
    bb.start();
}
}

获取等待通知,等待队列实例 Condition

Condition newCondition()  代替 object里面的wait和notify() 在多线程场景下更加灵活使用等待通知功能。

ReentrantReadWriteLock 读写锁

读写锁出现的原因:弥补独占锁的不足,公司的场景:读多写少

特性:

公平与非公平

支持锁降级

支持可重入

支持可中断

支持condition

API方面

readLock()获取读锁对象
writeLock()获取写锁对象
int getReadLockCount()读锁数量
getWriteHoldCount()写锁数量
* 读写互斥
* 写写互斥
* 读读不互斥

读写锁原理

写锁:通过对state加锁状态进行 与&运算等到写锁的数量,【存在锁的情况】–>【存在读锁,获取写锁就失败】【写锁被其他线程占用,获取写锁也失败】,获取写锁的数量最大是【MAX_COUNT,65535】,否则就去更新锁,获取成功就结束。【不存在锁的情况】–>如果校验锁未被获取,就查看是否有等待队列或者false。然后去更新锁,更新成功就获取锁成功,否则获锁失败。(公平锁校验队列是否有等待任务,非公平直接返回false) 体现出:【读写互斥,写写互斥】
读锁:计算写锁的数量:其他线程占有了写锁,那么获取读锁就失败(读写互斥,写锁正在写的过程,我们不能读)通过二进制右运算获取读锁的数量。获取锁,(等待队列,最大写锁数量,是否cas更新成功,获取锁成功)返回1表示成功,否则调用fullTryAcquireShared(current) 死循环获取到最终值(要不加锁成功,要不加锁失败的)

​ state 表示计算读锁的数量和写锁的数量

读锁: return c >>> SHARED_SHIFT; 右运算

写锁:return c & EXCLUSIVE_MASK;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jv7NH8MW-1621751537645)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210417093604300.png)]

缺点:会造成线程饥饿

邮戳锁StampedLock

  线程饥饿?   读锁大量占用,写锁始终获取不到,造成写锁线程无法获取到。一直等待。

戳:通过戳校验;是否写锁别获取过。验戳

读/写+乐观读

public long tryOptimisticRead()获取乐观读锁,返回一个 “戳” 版本号
public boolean validate(long stamp)验戳
public long tryReadLock()获取读锁
public long tryUnlockWrite()获取写锁

注意:不支持可重入

注意:不支持Condition 等待通知

读写锁之间互相转化。

邮戳,提高并发

package com.company;

import java.util.concurrent.locks.StampedLock;

/**
 * 常用邮戳锁api
 */
public class StampedLockDemo {


    public static void main(String[] args) {

        StampedLock lock=new StampedLock();

        long stamped = lock.tryOptimisticRead();

        System.out.println("乐观邮戳号 "+stamped);
        
        long r = lock.tryReadLock();
        
        System.out.println("读锁版本号 "+r);
        
        lock.unlockRead(r);
        
        long w = lock.tryWriteLock();
        
        System.out.println("写锁版本号 "+w);
        
        lock.unlockWrite(w);


        boolean validate = lock.validate(stamped);
        
        if(validate){
            System.out.println("没有被写锁修改过"+stamped);
        }else{
            System.out.println("被写锁获取"+stamped);
        }

         
    }
    
}

乐观读:案例

package com.company;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.StampedLock;

/**
 * 计算总和  解决线程饥饿问题
 */
public class StampedLockDemo {

    StampedLock lock=new StampedLock();
    int a=10,b=10;

    public void sum(){

        //乐观读 获取到版本号
        long l = lock.tryOptimisticRead();

        int a1=a;
        int b1=b;


        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //验戳
        if(!lock.validate(l)){

            //悲观读
            long l1 = lock.readLock();
            System.out.println("获取到悲观读锁-----》版本号"+l1);

              a1=a;
              b1=b;

            System.out.println("释放悲观读锁-----》版本号"+l1);
            lock.unlockRead(l1);
        }



        System.out.println("a1="+a1+"b1="+b1+"计算的总和"+(a1+b1));


    }

    public void udpateInt(int a,int b){

        long w = lock.writeLock();
        System.out.println("获取到了写锁"+w);
        try {
            this.a=a;
            this.b=b;
        } finally {
            System.out.println("释放了写锁"+w);
           lock.unlockWrite(w);
        }


    }

    public static void main(String[] args) {

        StampedLockDemo demo=new StampedLockDemo();

        new Thread(()->demo.sum()).start();

        new Thread(()->demo.udpateInt(1,2)).start();


        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }

}

Condition 等待通知 线程间写作工具

  • JUC包出现新的等待通知对象(条件队列)的原因:提供了丰富的api ,强大的扩展功能。
  • 相同点:必须要获取锁对象。
  • 不同点:object里面api(wait,notify,notifyAll),condition里面的(await,signal,signalAll)
  • 获取一个, 可以获取多个条件队列,
  • 也支持了 等待超时。

Lock+Condition 替代:synchronized+Object

void await()等待,释放锁资源
boolean await(long time, TimeUnit unit)超时等待
void signal()随机唤醒
void signalAll()全部唤醒
package com.company;

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

/**
 * 等待通知 Condition
 */
public class ConditionDemo {


    public static void main(String[] args) {


        ReentrantLock lock=new ReentrantLock();
        Condition condition = lock.newCondition();


        new Thread(()->{

            lock.lock();

            try {
                System.out.println(Thread.currentThread().getName()+"开始等待");
                //等待,释放锁
                condition.await();
                System.out.println(Thread.currentThread().getName()+"释放等待");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }

        }).start();


        new Thread(()->{


            //前提必须有锁
            lock.lock();


            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (Exception e) {
                e.printStackTrace();
            }


            try {
                System.out.println(Thread.currentThread().getName()+"开始唤醒");
                //随机唤醒 一个
                condition.signal();
            } catch (IllegalMonitorStateException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }


        }).start();


    }

}

源码层面:

​ await(): LockSupport.park(this); 等待,挂起线程,【加入到条件队列,释放锁资源,校验aqs阻塞队列之后,挂起线程】完成了等待

​ siognal():LockSupport.unpark(node.thread); 核心 唤醒线程 【加入到阻塞队列,aqs队列,就有机会获取到锁】,unpark 唤醒。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tOsxbDVm-1621751537646)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210418161958019.png)]

LockSupport 阻塞/唤醒工具

1, 不需要加锁,直接阻塞和唤醒。

2,唤醒和阻塞的过程可以互换顺序。避免死锁【许可证 0,1】

静态方法 不需要获取对象,直接使用

public static void park()阻塞
public static void unpark(Thread thread)释放,唤醒线程
package com.company;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;

public class LockSupportTest {
    
    public static void main(String[] args) {

        Thread t=new Thread(()->{

            System.out.println("无限等待的线程");
            //不需要加锁
            LockSupport.park();
            System.out.println("唤醒线程");

        });

        t.start();


        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        LockSupport.unpark(t);
     
    }

}

原子类 atomic

​ 多**线程情况下不加锁,仍然保持线程安全,原子更新同步资源,不同中断。 ** java.util.concurrent.atomic

​ 基于 CAS+unsafe+volatile

​ CAS: 【比较与交换】【compareAndSwap】 主内存,预期值,待更新值, 10==10?20替代10:不更新。

​ unsafe:操作c,c++库,jni操作,发送指令通过硬件资源,直接操作内存,原子操作

​ volatile:可见性。

api分类:四个部分【基本类型】【数组类型】【引用类型】【字段类型】 累加器

基本类型AtomicInteger,AtomicBoolean,AtomicLong
数组类型AtomicLongArray,AtomicIntegerArray,AtomicReferenceArray,
引用类型AtomicReference,AtomicStampedReference,AtomicMarkableReference
字段类型AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUpdater,
累加器LongAdder,DoubleAdder,LongAccumulator,DoubleAccumulator

原子整形类-AtomicInteger

出现的原因:多线程情况下,仍然保持原子更新操作。无需加锁。 线程安全,(i++ ,多线程情况下出现并发问题)

/**
 * 非原子 操作  (笨重)
 */
public class AtomicIntegerDemo {
    int i=0;
    public synchronized void sum(){
        System.out.println(Thread.currentThread().getName()+" "+(i++));
    }
    
    public static void main(String[] args) {

        AtomicIntegerDemo  demo=new AtomicIntegerDemo();

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

            new Thread(()->demo.sum()).start();

        }
    }
}
package com.company;


import java.util.concurrent.atomic.AtomicInteger;

/**
 * 基于CAS 无锁的操作
 */
public class AtomicIntegerDemo {
    AtomicInteger atomicInteger=new AtomicInteger();
    public   void sum(){
        System.out.println(Thread.currentThread().getName()+" "+(atomicInteger.getAndIncrement()));
    }

    public static void main(String[] args) {

        AtomicIntegerDemo  demo=new AtomicIntegerDemo();

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

            new Thread(()->demo.sum()).start();

        }
    }
}

API

public final int get()得到最新值
public final int getAndSet(int newValue)得到旧值,然后设置成新值
public final boolean compareAndSet(int expect, int update)expect==当前值?替换成update:什么也不做
public final int getAndIncrement()得到旧值,自增1
public final int getAndDecrement()得到旧值,减去1
public final int getAndAdd(int delta)等到旧址,累加delta
public final int addAndGet(int delta)得到新值,累加delta
package com.company;


import java.util.concurrent.atomic.AtomicInteger;

/**
 * api cas AtomicInteger
 */
public class AtomicIntegerDemo {



    public static void main(String[] args) {
        AtomicInteger atomicInteger=new AtomicInteger();
        int andIncrement = atomicInteger.getAndIncrement();
        System.out.println("andIncrement="+andIncrement);
        System.out.println("get="+atomicInteger.get());

        int andAdd = atomicInteger.getAndAdd(3);

        System.out.println("andAdd="+andAdd);

        System.out.println("get="+atomicInteger.get());

        int andDecrement = atomicInteger.getAndDecrement();

        System.out.println("getAndDecrement"+andDecrement);

        System.out.println("get="+atomicInteger.get());

        boolean b = atomicInteger.compareAndSet(7, 6);

        System.out.println("比较替换"+b);

        System.out.println("get="+atomicInteger.get());

    }
}

原子更新数组-AtomicLongArray

更新时:加一个角标字段

public final long getAndSet(int i, long newValue)返回旧值,通过角标更新成新值
public final boolean compareAndSet(int i, long expect,long update)返回布尔值,角标:期望值,待更新值 (期望值==当前值?待更新值:什么也不做)
public final long getAndIncrement(int i)返回旧值,通过角标加一
public final long getAndDecrement(int i)返回旧值,通过角标减一

原子更新引用类型- AtomicReference

public final boolean compareAndSet(V expect, V update)返回旧值,比较替换成新值
public final V getAndSet(V newValue)返回旧值,设置成新值
package com.company;

import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceDemo {

    public static void main(String[] args) {
        AtomicReference reference=new AtomicReference("周杰伦");

        boolean b = reference.compareAndSet("周杰伦", "昆凌");
        System.out.println("b=  "+b);

        System.out.println(reference.get());

        Object fws = reference.getAndSet("方文山");

        System.out.println(fws);

        System.out.println(reference.get());
    }

}

原子更新字段类型- AtomicIntegerFieldUpdater

public abstract boolean compareAndSet(T obj, int expect, int update)
public int getAndAdd(T obj, int delta)

必须:

必须是volatile修饰,

必须是int

不能 static

不能是private

package com.company;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class AtomicIntegerFieldUpdaterDemo {

    public static void main(String[] args) {

        Persion persion=new Persion();

        AtomicIntegerFieldUpdater f  = AtomicIntegerFieldUpdater.newUpdater(Persion.class,"i");

        boolean b = f.compareAndSet(persion, persion.i, 2);

        System.out.println(b);

        System.out.println(f.get(persion));

        int andAdd = f.getAndAdd(persion, 4);

        System.out.println(andAdd);

        System.out.println(f.get(persion));

    }


}

不停自旋的CAS不安全unSafe类

CAS: 【比较与交换】【compareAndSwap】  主内存值,预期值,待更新值,  10==10?20替代10:不更新。

unSafe:不安全类,危险的类,不建议我们使用,能够发送指令操作硬件资源。c,c++库。操作内存

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XcLmKHET-1621751537647)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210422210729275.png)]

问题: 不断的自旋,会浪费cpu性能,建议非大量并发时使用。不必上线文切换

ABA问题 A–B--A

解决ABA问题-AtomicStampedReference

API

public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)期望引用,新值,戳,新戳
public V getReference()得到当前对象应用
AtomicMarkableReference ,AtomicStampedReference 都可以解决 ABA 问题。

与生俱来的问题CAS

package com.company;

import java.util.concurrent.atomic.AtomicStampedReference;

public class AtomicStampedReferenceDemo {

    public static void main(String[] args) {

        AtomicStampedReference  atomicStampedReference=new AtomicStampedReference("周杰伦",1);

        boolean b = atomicStampedReference.compareAndSet("周杰伦", "周润发", 1, 2);

        System.out.println(b);
        Object reference = atomicStampedReference.getReference();
        System.out.println(reference);
        int stamp = atomicStampedReference.getStamp();
        System.out.println(stamp);

        if(stamp!=2){
            return;
        }

    }

}

LongAdder-高并发原子累加器

AtomicLong 为什么还有  LongAdder  ?
AtomicLong Cas操作失败,自旋,浪费想能     private volatile int value;  一把手,
LongAdder cells[value] 数组,分担竞争锁资源的压力,分成若干份。 累加  。【没有多线程竞争 一个 bvaseValue。如果是多线程的情况下使用cells[]数组】
构造方法:默认从0开始累加

API

public void increment()加一
public long sum()求和
package com.company;

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

public class LongAdderDemoo {

    public static void main(String[] args) {
        LongAdder l=new LongAdder();

        for (int i=0;i<10000;i++){

            new Thread(()->l.increment()).start();

        }
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("求和"+l.sum());
    }


}

并发集合-CopyOnWriteArrayList

  ArrayList :  elementData[size++] = e;  线程不安全,造成数据丢失。
解决方案:

Vector :所有方法都加上synchronized。性能下降。线程饥饿,一把锁

Collections.synchronizedList(list); / synchronized 把一个不安全的集合变成安全的集合 .。性能下降。线程饥饿,一把锁

CopyOnWriteArrayList :写(增删改)的时候复制一个新数组,来保证集合安全、(数据量不大的时候可以用,内存空间的浪费)。

API

addIfAbsent(E e)如果存在元素,就不添加,有去重的功能
public List subList(int fromIndex, int toIndex)截取指定范围内的元素(包含头,不包含尾部)
package com.company;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteArrayListDemo {

    public static void main(String[] args) {
        ArrayList list=new ArrayList();
        list.add("周杰伦");

        Vector vector=new Vector();
        vector.add("");
      //  vector.get(9);

        //把一个不安全的集合变成安全的集合
        Collections.synchronizedList(list);


        
        //安全的集合,添加时,增删改复制集合
        CopyOnWriteArrayList clist=new CopyOnWriteArrayList();

        clist.add("周杰伦");

        clist.addIfAbsent("五月天");

        clist.add("蔡依林");

        clist.addIfAbsent("周杰伦");


        ListIterator listIterator = clist.listIterator();

        while (listIterator.hasNext()){
            Object next = listIterator.next();
            System.out.println(next);
        }

        //截取部分值 【包含头,不包含尾部】
        List list1 = clist.subList(0, 1);

        System.out.println(list.get(0));
    }

}

原理

加锁,复制数组 ,设置新数组指向。

public boolean add(E e) {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        Object[] elements = getArray();
        int len = elements.length;
        //复制新元素到新数组
        Object[] newElements = Arrays.copyOf(elements, len + 1);
        newElements[len] = e;
        //指向新数组
        setArray(newElements);
        return true;
    } finally {
        lock.unlock();
    }
}

并发集合-CopyOnWriteArraySet

hashSet:线程不安全:底部使用的 HashMap

Collections.synchronizedSet(hashSet); 加锁,性能低下。

CopyOnWriteArraySet :CopyOnWriteArrayList.addIfAbsent()api,不能再就加入数据到容器。

并发集合-ConcurrentHashMap

hashMap不能在多线程情况下使用的原因? :并发修改异常,扩容导致闭环,死锁, put 丢数据   【多线程情况-线程池】
不建议使用的系列
Hashtable hashtable=new Hashtable();
Collections.synchronizedMap(new HashMap<>());
//不安全  并发修改异常
HashMap hashMap=new HashMap();
for (int i=0;i<1000;i++){
    int finalI = i;
    new Thread(()->{
        hashMap.put(Thread.currentThread().getName()+"-->"+ finalI, finalI);
        System.out.println(hashMap);
    }).start();

}
//线程安全
ConcurrentHashMap hash=new ConcurrentHashMap();
for (int i=0;i<1000;i++){
    int finalI = i;
    new Thread(()->{
        hash.put(Thread.currentThread().getName()+"-->"+ finalI, finalI);
        System.out.println(hash);
    }).start();

}

避免扩容的问题 ?

扩容的数量: int sc = n - (n >>> 2);

不要作为数组使用

/**
      map 最大容量
 */
private static final int MAXIMUM_CAPACITY = 1 << 30;

/**
   默认容量
 */
private static final int DEFAULT_CAPACITY = 16;


/**
 * 默认并发等级 锁的数量
 */
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;

/**
 * 负载因子:计算hash值,减少hash 碰撞数次  
 */
private static final float LOAD_FACTOR = 0.75f;

/**
 *  大于等于8 链表结构转化成红黑数
 */
static final int TREEIFY_THRESHOLD = 8;

/**
 *  小于6 红黑数 结构转化成 链表
 */
static final int UNTREEIFY_THRESHOLD = 6;

扩容数量计算

// put 30个元素   不是 数组[30]

ConcurrentHashMap cHashMap=new ConcurrentHashMap(40);
cHashMap.put("zjl",48);

int sc = 16 - (16 >>> 2);
System.out.println(sc);


double i=30/0.75;
System.out.println(i);

map简单的原理 put时 ?

1.7版本:分段锁seGement[]锁+hashEntry 内部有链表 腊肉 (Reentrantlock+实体+链表)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-feaCpAbt-1621751537648)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210424155125162.png)]

1.8 版本 CAS+node节点+红黑树

ConcurrentHashMap 原理:

synchronized:性能目前优化的不是很差了。算很好了
Node 节点:包装数据,链表的组成部分
CAS自旋操作:初始化添加首节点使用
红黑树,链表 :小于8个是链表,大于8个转化成 红黑树。
红黑树:性能好,可以减少io次数,提升遍历效率

put时:计算哈希值,初始化table,等到扩容值,,为空的情况下 Cas操作添加原因到 node数组中。否则加查看是否正在扩容,不是扩容,就添加node节点(链表接口,红黑树),如果数量是大于等于8个就转化成红黑树。添加count值。

聊聊并发队列-Queue

队列分类:阻塞队列+非阻塞队列

队列特点:先进先出,后进后出。【优先级队列】

阻塞队列:查看队列容量是否为【空,满】阻塞当前线程等待。直到队列不为空,或者数据不满,才继续操作容器,线程唤醒。

非阻塞队列:队列的容量是无线大的。Integer.MaxValue(); 基于CAS ,非阻塞算法,并发能力都是比较强,数据不需要等待。

阻塞队列:ReentrantLock【悲观策略】,非阻塞队列【CAS冲突监测的乐观锁机制】

阻塞队列&非阻塞事项场景:【生产者消费者】,【线程池】

非阻塞队列API:

ConcurrentLinkedQueue  非阻塞队列  CAS 无锁,无限大容量

阻塞队列API:

​ 阻塞+非阻塞 api

ArrayBlockingQueue基于数组+指定容量,有界队列
DelayQueue延迟的队列,到时间自动执行获取元素
LinkedBlockingQueue基于链表+指定容量 ,有界队列
PriorityBlockingQueue优先级队列,取数据时,可以排序
SynchronousQueue同步的队列

非阻塞队列-ConcurrentLinkedQueue

非阻塞队列 基于 CAS 并发能力超过阻塞队列,插入到数据尾部,从头获取,线程安全
boolean add(E e)添加元素,不能为null,返回布尔值
boolean offer(E e)添加元素,不能为null,返回布尔值
E peek()获取到元素:偷窥,看看,不删除元素,切返回首元素
E poll()获取到元素:光明正大,看,删除元素,切返回首元素
package com.company;

import java.util.concurrent.ConcurrentLinkedQueue;

public class ConcurrentLinkedQueueDemo {

    public static void main(String[] args) {
        ConcurrentLinkedQueue queue=new ConcurrentLinkedQueue();
        boolean 周杰伦 = queue.add("周杰伦");
        System.out.println(周杰伦);
        boolean 王力宏 = queue.offer("王力宏");
        System.out.println(王力宏);
        Object peek = queue.peek();
        System.out.println(peek);
        //删除了元素
        Object poll = queue.poll();
        System.out.println(poll);

        //效率低
        int size = queue.size();
        System.out.println(size);

        //效率高
        boolean empty = queue.isEmpty();
        System.out.println(empty);

    }

}

原理:offer 添加元素

node.next--->下一个node节点   链表结构
private transient volatile Node<E> head;
private transient volatile Node<E> tail;
UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val)  CAS操作+死循环

数组阻塞队列-ArrayBlockingQueue

提供了阻塞功能的方法:take(),pull(),先流的作用。
Interface BlockingQueue
两类方法:阻塞和非阻塞的方法

构造方法:public ArrayBlockingQueue(int capacity)公平锁非公平锁+容量+初始化数组 【必须写一个容量】

所有的队列不能添加空元素

boolean add(E e)添加元素,返回结果true,false
boolean offer(E e)添加元素,返回结果true,false
void put(E e)添加元素,阻塞
public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException添加元素,超时阻塞
E peek()获取到元素:偷窥,看看,不删除元素,切返回首元素
poll()删除首元素,不会阻塞
public E poll(long timeout, TimeUnit unit) throws InterruptedException删除首元素,不会阻塞(一定时间内超时)
public E take() throws InterruptedException删除首元素,会阻塞
package com.company;

import java.util.concurrent.ArrayBlockingQueue;

public class ArrayBlockingQueueDemo {

    public static void main(String[] args) {
        ArrayBlockingQueue arrayBlockingQueue=new ArrayBlockingQueue(1);
        boolean add = arrayBlockingQueue.add(3);
        System.out.println(add);
        boolean offer = arrayBlockingQueue.offer(4);
        System.out.println(offer);
        int size = arrayBlockingQueue.size();
        System.out.println(size);

        try {
            arrayBlockingQueue.put("32");
            System.out.println("32");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        Object peek = arrayBlockingQueue.peek();
        System.out.println(peek);

        try {
            Object take = arrayBlockingQueue.take();
            System.out.println("能打印的日志"+take);
            Object take1 = arrayBlockingQueue.take();
            System.out.println("打印不出来的日志");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }

}

数组结构

/* 元素存储 */
final Object[] items;

/** 数据元素的数量 */
int count;

/** 一把可重入锁 */
final ReentrantLock lock;

/** Condition  条件队列 */
private final Condition notEmpty;

/** Condition 条件队列 */
private final Condition notFull;

阻塞原理:

   pull();notFull.await(); 阻塞当前线程,等待取数据的take()取数据之后,唤醒await()线程(signal 随机唤醒)

   take(); notEmpty.await();  阻塞当前线程,添加数据之后,然后在把自己唤醒。(signal 随机唤醒)

链表阻塞队列-LinkedBlockingQueue

基于 Node 节点:item,next, 链表结构,MAX_VALUE,内存泄漏,指定容量的大小,元素不能为空否则空指针异常,2把锁,锁分离,吞吐量更高
package com.company;

import java.util.concurrent.LinkedBlockingQueue;

public class LinkedBlockingQueueDemo {

    public static void main(String[] args) {
        LinkedBlockingQueue queue=new LinkedBlockingQueue(2);
        queue.offer("周杰伦");
        queue.add("昆凌");
        System.out.println(queue);
        queue.peek();
        queue.remove("周杰伦");
        try {
            queue.put("方文山");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(queue);
    }

}
/**
 * 实体对象
 */
static class Node<E> {
    E item;

    /**
     * One of:
     * - the real successor Node
     * - this Node, meaning the successor is head.next
     * - null, meaning there is no successor (this is the last node)
     */
    Node<E> next;

    Node(E x) { item = x; }
}

/** 容量 */
private final int capacity;

/** 数量 节点 */
private final AtomicInteger count = new AtomicInteger();

/**
 * 链表的头部
 */
transient Node<E> head;

/**
 * Tail of linked list.
 *  链表的尾部
 */
private transient Node<E> last;

/**  AQS 阻塞队列 取首元素 */
private final ReentrantLock takeLock = new ReentrantLock();

/** 条件队列( 取首元素 ) */
private final Condition notEmpty = takeLock.newCondition();

/** AQS 阻塞队列  添加队列的锁 */
private final ReentrantLock putLock = new ReentrantLock();

/**  条件队列( 添加元素 ) */
private final Condition notFull = putLock.newCondition();

原理:

put:  队列满时,就会 notFull.await();阻塞线程,必须要等待其它线程唤醒阻塞线程。添加队列-- lastnode指向新的node节点,并且修改count值。

take:队列为空时:就会 notEmpty.await(); 阻塞线程,必须要等待其它线程唤醒阻塞线程。first.item = null; 修改成null。

ArrayBlockingQueue&LinkedBlockingQueue 区别

相同点:

ReentrantLock 锁 AQS阻塞队列。

Condition :条件队列 唤醒等待。

不相同点:

前者是一把锁,后者使用了2把锁。读写分离。

数据结构:一个数组,一个是链表,内存占用比较多。

容量部分:都可以执行大小,后者不指定其实就是 无界队列(MAX_VALUE)

优先级队列-PriorityBlockingQueue

什么叫做优先队列:按照我们自定义比较器,来指定元素的顺序,比如银行的vip办理业务,插队打饭。

存储特点:二叉堆+数组+CAS,无界队列【默认是11个大小容量,可以扩容到无界队列MAX_VALUE】【排序之后可以到队列的头部】

二叉堆:【最大堆,最小堆】

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5v4eeIpJ-1621751537649)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210428205408584.png)]

API

put 将指定的元素插入到此优先级队列中。由于队列无限制,此方法将永远不会阻止。
package com.company;

import java.util.concurrent.PriorityBlockingQueue;

public class PriorityBlockingQueueDemo {
   static class  Persion implements Comparable<Persion>{
        int i;
        String name;

       public Persion(int i, String name) {
           this.i = i;
           this.name = name;
       }

       public int getI() {
            return i;
        }

        public void setI(int i) {
            this.i = i;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public int compareTo(Persion o) {
            return this.getI()>o.getI()?-1:1;
        }

       @Override
       public String toString() {
           return "Persion{" +
                   "i=" + i +
                   ", name='" + name + '\'' +
                   '}';
       }
   }

    public static void main(String[] args) {
        PriorityBlockingQueue queue=new PriorityBlockingQueue();
        queue.offer(new Persion(10,"周杰伦"));
        queue.offer(new Persion(4,"昆凌"));
        queue.offer(new Persion(7,"方文山"));
        queue.offer(new Persion(2,"王力宏"));
        queue.offer(new Persion(9,"林俊杰"));
        queue.offer(new Persion(5,"薛之谦"));
        try {
            queue.take();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.err.println(queue);
    }

}

原理:

offer: 获取锁之后,如果需要扩容就释放锁并且基于CAS操作去扩容,否则另一个没有竞争成功的线程就让出CPU时间片。把新数组赋值并且执行新引用queue对象。添加元素,排序。再随机唤醒线程。条件队列  (每次都会唤醒)

延迟队列-DelayQueue

什么是延迟队列:必须过期之后元素才可以取出来 peek(),poll(),take(),   元素要实现 java.util.concurrent.Delayed ,long getDelay(TimeUnit unit) 返回剩余时间,如果小于等于0 表示过期。有序,PriorityQueue (数组结构)

倒计时,取消订单

案例:

package com.company;

import java.sql.Time;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

public class DelayQueueDemo {


    static class DelayedDemo implements Delayed{


        int i;
        String name;
        //过期时间
        long exprie;

        public DelayedDemo(int i, String name, long exprie) {
            this.i = i;
            this.name = name;
            this.exprie = exprie+System.currentTimeMillis();
        }

        /**
         * 过期计算  convert<=0 表示过期
         * @param unit
         * @return
         */
        @Override
        public long getDelay(TimeUnit unit) {
            long convert = unit.convert(exprie - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
            System.out.println(convert);
            return convert;
        }

        @Override
        public int compareTo(Delayed o) {
            return this.getDelay(TimeUnit.MILLISECONDS)-o.getDelay(TimeUnit.MILLISECONDS)>=0?1:-1;
        }

        @Override
        public String toString() {
            return "DelayedDemo{" +
                    "i=" + i +
                    ", name='" + name + '\'' +
                    ", exprie=" + exprie +
                    '}';
        }
    }



    public static void main(String[] args) {

        DelayQueue queue=new DelayQueue();
        queue.offer(new DelayedDemo(1,"周杰伦",111));
        queue.offer(new DelayedDemo(2,"昆凌",10022));

        System.out.println(queue);

        while (true){

            try {
                Delayed take = queue.take();
                System.out.println(take);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }


    }

}

原理:

offer();  添加元素之后,再头部,就重置线程null,随机唤醒线程。
take();   死循环+available.await(); 查看队列是否为空,如果为空,就等待,否则就获取超时时长。如果小于等于0就弹出元素。否则,再去查看是否有人等待获取首元素,如果有人等待就睡眠等待,否则,没有等待,等待固定时长,然后自动唤醒自己。再去for 遍历首元素,获取元素。

同步队列-SynchronousQueue

同步队列:线程要去取数据会阻塞到有一个线程把数据添加到队列里。我们才可以取出来。阻塞

特点:阻塞,空集合,api不能使用的,不能判断是不是空isEmpty() true, 迭代器永远是空,无效。peek();

线程池:ExecutorService executorService = Executors.newCachedThreadPool();

案例:

package com.company;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;

/**
 * 同步队列  put take 阻塞方法  不能是 offer,peek
 */
public class SynchronousQueueDemo {

    public static void main(String[] args) {

        SynchronousQueue queue=new SynchronousQueue();

        new Thread(){
            
            
            @Override
            public void run() {
                super.run();
                try {
                    /***
                     * 阻塞子线程    queue.put("周杰伦");
                     */
                    queue.put("周杰伦");
                    System.out.println("哎呦不错哦!"+queue);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();

        try {
            Thread.sleep(3000);
            System.out.println("我起来了~~~~~");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {
            Object take = queue.take();
            System.out.println("哈哈哈="+take);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

聊聊线程池-Executor

不建议用: new Thread().start();

1, 浪费服务器的资源,创建和销毁 浪费资源

2,不能立马就销毁对象。大量的对象,GC

3, 线程的什么周期比较长,不好管理线程的状态,中断…

4,不支持周期,调度的功能。线程大容易竞争资源

【资源销毁】【不好管理】

**什么是线程池:**维护一批 new thread().start() 放到一个池子或者队列集合里面。提交任务就取出来一条线程直接使用,速度快,结束任务后,把线程返回线程池。或者空闲的线程销毁掉。

**好处:**不用频繁创建和销毁线程。减少浪费资源。

​ 便于管理线程的状态。

​ 支持周期性,提高相应速度。减少资源竞争,

​ 便于控制线程的数量

简单介绍线程池的分类:

Executorvoid execute(Runnable command) 顶级接口 ,提交任务
ExecutorService扩展了Executor接口,提供了submit,api
ForkJoinPool工作窃取,密集型补充之前的线程池
ThreadPoolExecutor自定义线程池的时候使用
Executors有静态方法,可以返回线程池对象

JDK四大内置线程池

//固定大小线程池

package com.company;

import java.util.concurrent.*;

public class ExecurotrsDemo {

    public static void main(String[] args) {

        //固定大小线程池 3  submit 提交任务  executor(new Runable)  oom内存溢出 
        ExecutorService es= Executors.newFixedThreadPool(3);
        for (int i=0;i<5;i++){


            es.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });


            es.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });

            Future<Object> submit = es.submit(new Callable<Object>() {
                @Override
                public Object call() throws Exception {
                    System.out.println(Thread.currentThread().getName());
                    return "總經理";
                }
            });

            try {
                Object o = submit.get();
                System.out.println(o);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }

        }





    }


}
package com.company;

import java.util.concurrent.*;

public class ExecurotrsDemo {

    public static void main(String[] args) {

        //单个线程池 只有一个线程 ,异常退出的线程,会自动创建。同步的作用 【测试环境使用】 oom内存溢出的风险  
        ExecutorService es= Executors.newSingleThreadExecutor();
        for (int i=0;i<5;i++){
            es.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
            
        }
 

    }


}
package com.company;

import java.util.concurrent.*;

public class ExecurotrsDemo {

    public static void main(String[] args) {

        //一直创建线程 整形最大值,oom[线程数量无限制] , 未使用六十秒的线程将被终止并从缓存中删除【不断的创建线程】
        ExecutorService es= Executors.newCachedThreadPool();
        for (int i=0;i<45;i++){
            es.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });

        }


    }


}
package com.company;

import java.util.concurrent.*;

public class ExecurotrsDemo {

    public static void main(String[] args) {

        //int corePoolSize 核心线程 oom 周期性质的线程池
        ScheduledExecutorService es= Executors.newScheduledThreadPool(5);


        for (int i=0;i<2;i++){

            //3秒之后执行
            for (int i2=0;i2<45;i2++){
                es.schedule(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName());
                    }
                },3, TimeUnit.SECONDS);



                //第一次是4秒之后,然后每隔三秒执行一次
            es.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            },4,3, TimeUnit.SECONDS);

        }

    }

}

自定义线程池

为什么使用自定义线程池:内置的四个线程池【可固定大小的线程池 newFixedThreadPool(int nThreads) 】【单个线程的线程池 xecutorService newSingleThreadExecutor()】【可缓存的线程池 ExecutorService newCachedThreadPool()】 【周期性 ScheduledExecutorService newScheduledThreadPool(int corePoolSize)】 会造成oom。所需要业务自定义线程池

1,队列的大小

2,比如是否运行丢弃任务。

ThreadPoolExecutor

package com.company;

import java.sql.Time;
import java.util.concurrent.*;

public class ExecurotrsDemo {

    static  class ThreadFactoryDemo implements ThreadFactory{


        @Override
        public Thread newThread(Runnable r) {
            Thread t= new Thread(r,"周杰伦自定义的线程");
            return t;
        }
    }

    public static void main(String[] args) {

//        int corePoolSize,  核心线程池的数量  银行
//        int maximumPoolSize, 最大线程池
//        long keepAliveTime, 60 空闲时间  :非核心线程
//        TimeUnit unit, 时间单位  秒,毫秒,天
//        BlockingQueue<Runnable> workQueue,阻塞队列 【ArrayBlockingQueue】【LinkedBlockingQueue】 指定队列大小
//        ThreadFactory threadFactory,  线程工程,执行线程名字
//        RejectedExecutionHandler handler 拒绝策略【抛出异常,丢弃任务】【抛出异常,不丢弃任务】【丢弃最旧任务,新任务添加队列】【任务主线程运行】

        RejectedExecutionHandler handler=new ThreadPoolExecutor.DiscardOldestPolicy();

        ThreadPoolExecutor threadPoolExecutor =
        new ThreadPoolExecutor(3, 10, 60, TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(20), new ThreadFactoryDemo(), handler);

        //创建了一个线程 ,核心线程数是3个,最大线程数是10,且队列可以容纳20个,拒绝策略是 丢弃老任务添加到新队列。

        threadPoolExecutor.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"===========打印周杰伦是自定义线程");
        
            }
        });
      
        //配置线程池 【io密集】【cpu密集型】
    }


}

线程池添加的流程

//        int corePoolSize,  核心线程池的数量  银行
//        int maximumPoolSize, 最大线程池
//        long keepAliveTime, 60 空闲时间  :非核心线程
//        TimeUnit unit, 时间单位  秒,毫秒,天
//        BlockingQueue<Runnable> workQueue,阻塞队列 【ArrayBlockingQueue】【LinkedBlockingQueue】 指定队列大小
//        ThreadFactory threadFactory,  线程工厂,自定义线程名字
//        RejectedExecutionHandler handler 拒绝策略【抛出异常,丢弃任务】【抛出异常,不丢弃任务】【丢弃最旧任务,新任务添加队列】【任务主线程运行】

添加任务:先判断核心线程数是否被沾满,没有沾满就直接交给核心线程执行任务,否则就会把任务放到队列里面,等待核心线程读取。

如果核心+队列都满了。那么就会创建非核心线程去处理任务。一直到【最大线程池满】,【核心+队列+最大线程数】都满的情况下,就开启拒绝策略。

如果超过空闲时间没有任务,那么keepAliveTime 【空闲时间就会起作用】删除掉非核心线程。

线程池的拒绝4大拒绝策略

拒绝策略

【抛出异常,丢弃任务】 ThreadPoolExecutor.AbortPolicy

【不抛出异常,丢弃任务】ThreadPoolExecutor.DiscardPolicy

【丢弃最旧任务,新任务添加队列】 ThreadPoolExecutor.DiscardOldestPolicy

【任务由(调用者)主线程运行】 ThreadPoolExecutor.CallerRunsPolicy

//【抛出异常,丢弃任务】 ThreadPoolExecutor.AbortPolicy
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@5cad8086 rejected from java.util.concurrent.ThreadPoolExecutor@6e0be858[Running, pool size = 10, active threads = 10, queued tasks = 20, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
	at com.company.ExecurotrsDemo.main(ExecurotrsDemo.java:42)
【任务主线程运行】 ThreadPoolExecutor.CallerRunsPolicy 
周杰伦自定义的线程===========打印周杰伦是自定义线程
main===========打印周杰伦是自定义线程

FutureTask&Future

提供的功能:取消任务结果 ,阻塞主线程获取到结果。【//特点:取消任务+阻塞获取Callable返回值】

boolean cancel(boolean mayInterruptIfRunning)尝试取消执行此任务。
V get() throws InterruptedException, ExecutionException等待计算完成,然后检索其结果。 `【阻塞的功能】
boolean isCancelled()true`如果此任务在完成之前被取消
boolean isDone()true如果这个任务完成

Future

package com.company;

import java.util.concurrent.*;

public class FutureDemo {

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(3);

        Future<String> submit = executorService.submit(new Callable<String>() {

            @Override
            public String call() throws Exception {
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("哎呦不错哦~~~~~");
                return "周杰伦";
            }
        });

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    //    submit.cancel(false);

        try {
            System.out.println("是否被取消"+submit.isCancelled());
            System.out.println("是否完成任务"+submit.isDone());
            if(!submit.isCancelled()){
                String s = submit.get();
                System.out.println(s);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


    }

}
package com.company;

import java.util.concurrent.*;

public class FutureDemo {

    public static void main(String[] args) {

        //特点:取消任务+阻塞获取Callable返回值
        ExecutorService executorService = Executors.newFixedThreadPool(3);

            FutureTask task=new FutureTask(new Callable<String>() {

                @Override
                public String call() throws Exception {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("哎呦不错哦~~~~~");
                    return "周杰伦";
                }
            });
         executorService.submit(task);

        try {
            Object o = task.get();
            System.out.println(o);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

ForkJoinPool&Fork/Join框架

1,jdk 1.7 ForkJoinPool 处理CPU密集型任务。代替[补充的] threadpoolexecutor 【Fork/Join+ForkJoinPool】

​ CPU密集型 : 运算,三元,加减乘除。if switch不是io操作【查数据库io】

ForkJoinPool 简单的了解:

线程池,工作窃取或者 分而治之的思想:充分利息CPU多核心的性能

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ze0gq2YE-1621751537651)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210502162839959.png)]

Fork/Join可以使用在哪方面?

​ ForkJoinTask类:+ForkJoinPool 快速的求和

​ 把一个大任务可以拆分成多个小任务。最后在求和得到总数 【拆分小任务+迭代递归思想】

RecursiveTask 处理任务 有返回值,都实现了ForkjoinTask
RecursiveAction 处理任务没有返回值,都实现了ForkjoinTask
fork() :提交任务到队列.执行。
join() :合并,返回结果集
package com.company;

import com.sun.javafx.image.IntPixelGetter;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;

/**
 * ForkJoinTask+ForkjoinPool 计算一个和  通过拆分的方式  [CPU密集型]
 */
public class ForkJoinPoolTest extends RecursiveTask<Integer> {
    //最小拆分数量
    int min=3;
    int start;
    int end;

    public ForkJoinPoolTest(int start, int end) {
        this.start = start;
        this.end = end;
    }

    public static void main(String[] args) {

        //Runtime.getRuntime().availableProcessors())类似核心线程
        ForkJoinPool pool=new ForkJoinPool();
        ForkJoinTask<Integer> submit = pool.submit(new ForkJoinPoolTest(1,1000000));
        Integer integer = null;
        try {
            integer = submit.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        System.out.println(integer);

    }


    //进行任务拆分且返回一个 sum
    @Override
    protected Integer compute() {

        //不拆分 [范围太小] 减掉后的值,小于 3   1,3  3-1=2<=3    ,8,10=2
        if(end-start<=min){
                int sum=0;
                for(int i=start;i<=end;i++){
                      sum+=i;
                }

                 return sum;
        //拆分
        }else{
             int mid=(end+start)/2;
             ForkJoinPoolTest test1=new ForkJoinPoolTest(start,mid); // 0,5
             ForkJoinPoolTest test2=new ForkJoinPoolTest(mid+1,end);//6,10

              //拆分
             test1.fork();
             test2.fork();

            //拿到小任务结果
             Integer join1 = test1.join();
             Integer join2 = test2.join();
             int sum= join1+join2;
            System.out.println(Thread.currentThread().getName()+"====="+sum);
            //返回总和
            return sum;

        }
    }
}

CountDownLatch-同步计数器

什么是同步计数器:让一个线程等待(阻塞/等待wait.await(),其他是线程各自执行完毕自己的任务之后,在唤醒等待线程。

int i=10; --i 0;

场景:第一步校验,开启多线程,第二部批量入库。

void await()阻塞等待的意思
public void countDown()类似 i–

初始值 (count):countDown 就会减掉1.直到成为 0。唤醒

package com.company;


import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountdownLatchTest {

    public static void main(String[] args) {
        //构造方法count 线程的数量
        CountDownLatch latch=new CountDownLatch(10);
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        //第一步 校验
        for(int i=0;i<10;i++)
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"开始任务");
                //减掉1
                latch.countDown();
            }
        });

        try {
            //批量入库  不能用 join  等一步 必须同步
            System.out.println("开始 线程等待");

            //Condition await(); 等于0了之后, 自动唤醒
            latch.await();

            System.out.println("线程被唤醒");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

集成了AQS state=10个数量 +CAS 死循环

Semaphore-信号量

什么是信号量:银行的大厅。停车位数量:【限流的作用】

场景:数据库连接池的数量不被 使用完。

acquire()获得许可证,如果有可用并立即返回,则将可用许可证数量减少一个
void release()发放许可证,将可用许可证的数量增加一个。
package com.company;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class SemaphoreTest {

    public static void main(String[] args) {

        //只能有10个线程同时执行
        Semaphore semaphore=new Semaphore(10);

        ExecutorService executorService = Executors.newFixedThreadPool(21);

        //第一步 校验
        for(int i=0;i<21;i++)
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        //减掉1
                        semaphore.acquire();
                        System.out.println(Thread.currentThread().getName()+"进银行大厅了");
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        System.out.println(Thread.currentThread().getName()+"出银行大厅了");
                        //加上1 出去了
                        semaphore.release();
                    }
                }
            });
    }
}
LockSupport.park(this);阻塞方法 ,LockSupport.unpark(s.thread); 
一个是减去1,一个加1,

CyclicBarrier-循环栅栏

什么是循环栅栏:多条线程有一波操作。然后操作完成之后。可以出发一个事件(线程Runable)返回的操作(监听事件)

public int await() throws InterruptedException 等待,减去1

场景:求和:最后一个线程操作 监听事件

package com.company;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierTest {
    public static void main(String[] args) {

        class CycLicDemo extends Thread{
            CyclicBarrier cyclicBarrier;

            public CycLicDemo(CyclicBarrier cyclicBarrier) {
                this.cyclicBarrier=cyclicBarrier;
            }

            @Override
            public void run() {
                super.run();
                try {
                    cyclicBarrier.await();
                    cyclicBarrier.await();
                    cyclicBarrier.await();
                    System.out.println(Thread.currentThread().getName()+"睡醒了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }

            }
        }


        CyclicBarrier cyclicBarrier=new CyclicBarrier(3, new Runnable() {
            @Override
            public void run() {
                System.out.println("执行完毕");
            }
        });


        for (int i=0;i<3;i++){

            new CycLicDemo(cyclicBarrier).start();

        }


    }
}
原理:await();ReentrantLock,Condition.唤醒所有线程。减掉,如果等于0 就唤醒所有线程,但是在此这前,就会调用run方法  。trip.await();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值