JUC笔记

1.什么是JUC

  • java.util.concurrent并发

  • java.util.concurrent.atomic

  • java.util.concurrent.locks

java.util工具包,包,分类

业务:普通的线程代码 Thread

Runnable 没有返回值,效率相比于Callable相对较低

2.线程和进程

进程:一个程序,例:QQ.EXE,代表程序的集合

一个进程往往可以包含多个线程,至少包含一个!

java默认有几个线程?2个 main,GC

线程:系统资源调度和分配的最小单位

java开不了线程,他会调用本地的native方法(底层c++)开启,java不能直接操作硬件,java运行在jvm中

并发/并行

并发编程:并发,并行

并发(多个线程操作同一资源)

  • cpu一核,模拟出来的多条线程,快速交替

并行(多个人一起行走)

  • cpu多核,多个线程可以真正的同时执行;线程池

并发编程的本质:充分利用cpu的资源

线程有几个状态?

	public enum State {
    	//线程新生
        NEW,
    	//运行状态
        RUNNABLE,
    	//阻塞状态
        BLOCKED,
    	//等待,死死的等
        WAITING,
    	//超时等待
        TIMED_WAITING,
    	//中止
        TERMINATED;
    }

wait/sleep区别

1.来自不同的类

wait=>Object

sleep=>Thread

企业当中,休眠用:TimeUnit.单位.sleep(几个单位);

2.关于锁的释放

wait会释放锁,

sleep睡觉了,抱着锁一起睡,不释放

3.使用的范围是不同的

wait必须在同步代码块中

sleep可以在任何地方睡

4.是否需要捕获异常

wait 不需要

sleep 必须要捕获异常

3.Lock锁(重点)

传统 Synchronized

看juc卖票代码

lock

lock接口

l.lock()加锁

l.unlock()解锁

lock实现类

ReentrantLock:可重入锁

ReentrantLockReadWriteLock.ReadLock:读锁

ReentrantLockReadWriteLock.WriteLock:写锁

公平/非公平锁

FairSync:公平锁,非常公平,可以先来后到

NonFairSync:非公平锁,非常不公平,可以插队

Synchronized 和 Lock 区别

1.Synchronized 时内置的java关键字,Lock是一个java类

2.Synchronized 无法判断获取锁的状态,而Lock可以判断是否获得到了锁

3.Synchronized 会自动释放锁,Lock必须手动释放锁,!如果不释放,死锁

4.Synchronized 线程1(获得锁),线程2(等待,傻傻的等),Lock锁就不一定等待下去

5.Synchronized 可重入锁,不可中断的,非公平锁,lock 可重入的,可以判断锁,可公平可不公平

6.Synchronizde 适合锁少量的代码同步问题,Lock适合锁大量的同步代码!

锁是什么?如何判断锁的是谁?

4.生产者和消费者问题

面试常见问题:单例模式,排序算法,生产者和消费者,死锁问题

Synchronized版本

/**
 * 线程之间的通信问题:生产者和消费者问题
 * 线程交替执行A,B操作同于一个变量 num = 0
 * A num++
 * B num--
 * A num++
 * .
 * .
 * .
 * 交替执行
 *
 * 口诀,判断等待通知
 * */
public class Test {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            while (true){
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            while(true){
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
class Data{
    private int number = 0;
    public synchronized void increment() throws InterruptedException {
        if (number != 0) {
            //todo 等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"执行后number = "+number);
        this.notify();
        //todo 通知其他线程执行完毕
    }
    public synchronized void decrement() throws InterruptedException {
        if(number == 0){
            //todo 等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"执行后number = "+number);
        this.notify();
        //todo 通知其他线程执行完毕
    }
}

问题,两个线程以上if要换成while,看文档,wait总应该出现在循环中

正确代码:

class Data{
    private int number = 0;
    public synchronized void increment() throws InterruptedException {
        while (number == 1) {
            //todo 等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"执行后number = "+number);
        this.notify();
        //todo 通知其他线程执行完毕
    }
    public synchronized void decrement() throws InterruptedException {
        while (number == 0){
            //todo 等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"执行后number = "+number);
        this.notify();
        //todo 通知其他线程执行完毕
    }
}

JUC版生产者和消费者问题

代码:

class Data2 {
    private int number = 0;
    private Lock lock;
    private Condition toAdd;
    private Condition toLess;
    public Data2(){
        lock = new ReentrantLock();
        toAdd = lock.newCondition();
        toLess = lock.newCondition();
    }

    public void increment() throws InterruptedException {
        lock.lock();
        try{
            while(number == 1){
                toAdd.await();
            }
            number ++;
            System.out.println(Thread.currentThread().getName() + "执行后number = " + number);
            toLess.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void decrement() throws InterruptedException {
        lock.lock();
        try{
            while(number == 0){
                toLess.await();
            }
            number --;
            System.out.println(Thread.currentThread().getName() + "执行后number = " + number);
            toAdd.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

5.八锁现象

如何判断锁的是谁!永远知道什么锁,锁到底锁的谁

深刻理解锁是什么

1.synchronized锁的对象是方法的调用者

6.集合类不安全

List

public class ListTest {
    public static void main(String[] args) {
        //List<String> 线程安全集合.map.set.list = new ArrayList<>(); 线程不安全
        // List<String> 线程安全集合.map.set.list = new Vector<>();  synchronized实现
        // List<String> 线程安全集合.map.set.list = Collections.synchronizedList(new ArrayList<>());
        List<String> list = new CopyOnWriteArrayList<>();
        //copyOnWrite 写入时复制
        //多个线程调用的时候,线程安全集合.map.set.list,读取时固定,写入覆盖
        //在写入时避免覆盖,造成数据问题
        //读写分离
        for (int i = 0; i < 10; i++) {
            new Thread(()-> {
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
            new Thread(()-> {
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
            new Thread(()-> {
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

set

public class SetTest {
    public static void main(String[] args) {
        // Set<String > 线程安全集合.map.set = new HashSet<>(); 线程不安全
        // Set<String > 线程安全集合.map.set = Collections.synchronizedSet(new HashSet<>()); synchronized 实现
        Set<String > set = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 100; i++) {
            new Thread(() ->{
                set.add(UUID.randomUUID().toString());
                System.out.println(set);
            },String.valueOf(i)).start();
            new Thread(() ->{
                set.add(UUID.randomUUID().toString());
                System.out.println(set);
            },String.valueOf(i)).start();
        }

    }
}

map

public class MapTest   {
    public static void main(String[] args) {
        //map怎么用,默认等价于什么 工作中不用hashmap
        //默认等价于 : HashMap<String,String> map = new HashMap<>(16,0.75f);
        //Map<String,String> map = new HashMap<>(); 线程不安全
        //Map<String,String> map = Collections.synchronizedMap(new HashMap<>()); synchronized实现
        Map<String,String> map = new ConcurrentHashMap<>();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                map.put(UUID.randomUUID().toString(),UUID.randomUUID().toString());
                System.out.println(map);
            },String.valueOf(i)).start();
            new Thread(() -> {
                map.put(UUID.randomUUID().toString(),UUID.randomUUID().toString());
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

7.Callable

1.可以有返回值

2.可以抛出异常

3.方法不同,run()/call()

使用方法:使用FutureTask中转,FutureTask可以接受call的返回值,用get方法取得返回值

get方法可能会超时

public class CallableTest {
    public static void main(String[] args) {
        FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable());
        new Thread(futureTask).start();
        new Thread(futureTask).start(); //结果会被缓存,效率高,调用两次只会调用一次
        try {
            System.out.println(futureTask.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}
class MyCallable implements Callable<Integer>{
    @Override
    public Integer call() throws Exception {
        System.out.println("Call");
        return 123456;
    }
}

8.常用的辅助类

8.1 CountDownLatch(减法计数器)

1

减法计数器,当count为0时才执行后续操作,如果执行次数没到就等待

import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName()+"out");
                //数量减一
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }
        //等待计数器归零
        countDownLatch.await();
        System.out.println("close");
    }
}

原理:countdown()数量-1

​ await()//等待数量归零

​ 每次有线程调用countdown数量-1,假设计数器变为0,countdownlatch.await会被唤醒,继续执行

8.2 CyclicBarrier(加法计数器)

2

加法计数器,当执行到指定次数时执行放入CyclicBarrier中的Runabble方法

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("召唤成功");
        });
        for (int i = 0; i < 6; i++) {
            int finalI = i;
            new Thread(()-> {
                System.out.println(finalI +"召唤");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

8.3 Semaphore(信号量)

捕获

限流,最多多少个线程,到达指定数量之后等待之前某个线程结束才能继续执行新的线程

public class SemaphoreDemo {
    public static void main(String[] args) {
        //线程数量,停车位,限流作用
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                try {
                    //获得
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " 停车了");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + " 开走了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //释放
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}

原理:acquire()获得,假设如果以及满了,等待,等待被释放为之

​ relrese() 释放,会将当前的信号量释放+1.然后唤醒等待的线程

作用:多个共享资源互斥使用

​ 并发限流

9.读写锁

ReadWritelock

4

/**
 * ReadWriteLock
 * 独占锁(写锁) 一次只能被一个线程占有
 * 共享锁(读锁) 多个线程可以同时占有
 * 读 - 读 : 可以共存
 * 读 - 写 : 不能共存
 * 写 - 写 : 不能共存
 * **/
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache cache = new MyCache();
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                cache.put(""+finalI,""+finalI);
            },String.valueOf(i)).start();
        }
        for (int i = 0; i < 10; i++) {
            int finalI = i;
            new Thread(() -> {
                cache.get(""+finalI);
            },String.valueOf(i)).start();
        }

    }
}
class MyCache{
    private volatile Map<String,Object> map = new HashMap<>();
    private ReadWriteLock lock = new ReentrantReadWriteLock();
    //存 / 写
    public void put(String key,Object value){
        lock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"正在写入"+key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入OK");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.writeLock().unlock();
        }
    }
    //取 / 读
    public Object get(String key){
        lock.readLock().lock();
        Object o = null;
        try{
            System.out.println(Thread.currentThread().getName()+"正在读取"+key);
            o = map.get(key);
            System.out.println(Thread.currentThread().getName()+"读取OK"+key);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.readLock().unlock();
        }
        return o;
    }
}

10.阻塞队列

10.1 ArrayBlockingQueue 阻塞队列

写入:如果队列满了,就必须阻塞等待

取:如果队列是空的,必须阻塞等待生产

5

什么时候我们使用阻塞队列:并发任务,线程池

6

使用(添加/删除):

​ 四组API

​ 1.抛出异常

​ 2.不会抛出异常

​ 3.阻塞等待

​ 4.超时等待

方式抛出异常有返回值阻塞等待超时等待
添加add()offer()put()offer(obj,TIME,TIMEUTIL)
移除remove()poll()take()poll(TIME,TIMEUTIL)
判断队列首element()peek()
public class Test {
    public static void main(String[] args) {
        try {
            new Test().test4();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 抛出异常 会输出错误信息
    public void test1(){
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        //System.out.println(blockingQueue.add("d"));
        System.out.println(blockingQueue.element());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        //h会抛出异常
        /*Exception in thread "main" java.lang.IllegalStateException: Queue full
        at java.util.AbstractQueue.add(AbstractQueue.java:98)
        at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:312)
        at 阻塞队列.Test.test1(Test.java:17)
        at 阻塞队列.Test.main(Test.java:7)*/
    }
    //不抛出异常  会返回false
    public void test2(){
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
        //存
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        System.out.println(blockingQueue.offer("d"));
        System.out.println(blockingQueue.peek());
        //取
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        //false
    }
    /*
    * 阻塞 - 等待
    * */
    public void test3() throws InterruptedException {
        ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(3);
        queue.put(1);
        queue.put(1);
        queue.put(1);

        //一直阻塞
        //queue.put(1);

        System.out.println(queue.take());
        System.out.println(queue.take());
        System.out.println(queue.take());
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
                queue.put(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        //如果不加入就等待
        System.out.println(queue.take());
    }
    /*
     * 超时等待
     * */
    public void test4() throws InterruptedException {
        ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<>(3);
        System.out.println(queue.offer(1,3,TimeUnit.SECONDS));
        System.out.println(queue.offer(1,3,TimeUnit.SECONDS));
        System.out.println(queue.offer(1,3,TimeUnit.SECONDS));
        System.out.println(queue.offer(1,3,TimeUnit.SECONDS));
        System.out.println(queue.poll(3,TimeUnit.SECONDS));
        System.out.println(queue.poll(3,TimeUnit.SECONDS));
        System.out.println(queue.poll(3,TimeUnit.SECONDS));
        System.out.println(queue.poll(3,TimeUnit.SECONDS));
    }
}

10.2 SynchronizedQueue 同步队列

没有容量,进去一个元素必须等待取出来之后才能再往里面放新的元素

public class Test {
    public static void main(String[] args) {
        BlockingQueue<Integer> queue =new SynchronousQueue<Integer>();
        new Thread(()->{
            try {
                queue.put(1);
                System.out.println("put1ok");
                queue.put(2);
                System.out.println("put2ok");
                queue.put(3);
                System.out.println("put3ok");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(queue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(queue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(queue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

11.线程池(重要)

池化技术

程序的运行,本质:占用系统资源!优化资源的使用! 演进出一种策略=》池化技术

例如:线程池 ,连接池,内存池,对象池//。。。创建销毁十分浪费资源

对开启和关闭比较消耗资源的东西,事先准备好资源,有人要用就拿,用完还回来

线程池的好处:

1.降低资源的消耗

2.提高相应速度

3.方便管理

线程可以复用,可以控制最大并发数,管理线程

7

三大方法

public class Test {
    public static void main(String[] args) {
        //ExecutorService executor = Executors.newSingleThreadExecutor(); //单个线程
        //ExecutorService executor = Executors.newFixedThreadPool(2);//固定线程数的线程池
        ExecutorService executor = Executors.newCachedThreadPool();//可伸缩的,遇强则强,遇弱则弱
        try{
            for (int i = 0; i < 10; i++) {
                //使用线程池后,用线程池来创建线程
                executor.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            executor.shutdown();
        }
    }
}

七大参数

三个线程池开启的源码

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
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,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

开启线程池调用的是ThreadPoolExecutor!

ThreadPoolExecutor源码:

public ThreadPoolExecutor(int corePoolSize,
                           int maximumPoolSize,
                           long keepAliveTime,
                           TimeUnit unit,
                           BlockingQueue<Runnable> workQueue,
                           ThreadFactory threadFactory,
                           RejectedExecutionHandler handler) {
     if (corePoolSize < 0 ||
         maximumPoolSize <= 0 ||
         maximumPoolSize < corePoolSize ||
         keepAliveTime < 0)
         throw new IllegalArgumentException();
     if (workQueue == null || threadFactory == null || handler == null)
         throw new NullPointerException();
     this.acc = System.getSecurityManager() == null ?
         null :
     AccessController.getContext();
     this.corePoolSize = corePoolSize;
     this.maximumPoolSize = maximumPoolSize;
     this.workQueue = workQueue;
     this.keepAliveTime = unit.toNanos(keepAliveTime);
     this.threadFactory = threadFactory;
     this.handler = handler;
 }

ThreadPoolExecutor中有七个参数

int corePoolSize : 核心线程池大小
int maximumPoolSize : 最大的线程池大小
long keepAliveTime : 线程存活时间(超时了没有人调用就会释放)
TimeUnit unit : 释放时间的单位
BlockingQueue<Runnable> workQueue : 阻塞队列
ThreadFactory threadFactory : 线程工厂(创建线程的,一般不用动)
RejectedExecutionHandler handler : 拒绝策略

10

11

四种拒绝策略

AbortPolicy 线程和队列都满了就不处理这个人的并抛出异常

CallerRunsPolicy 线程和队列都满了就让他哪来的去哪里(由调用的线程处理方法体中的任务)

DiscardPolicy 队列满了不会抛出异常,队列满了就会丢掉任务

DiscardOldestPolicy 也不会抛出异常,队列满了会尝试和最早的竞争,竞争成功就执行,失败就抛弃

12

最大线程该如何定义

1.CPU密集型 ,cpu线程数就定义为几,cpu效率最高

2.IO密集型 , 判断程序中十分耗io的线程, 设置为两倍于耗io线程的数量

​ 程序 15个大型任务,io十分占用资源,那就最少30个线程处理

12.四大函数式接口(必须掌握)

泛型,枚举,反射

lambda表达式

链式编程

函数式接口 : 只有一个方法的接口

​ 在java中出现极多

​ 新版本框架底层大量应用

  			   foreach(消费者类型的函数式接口)

Stream流式计算

四大函数式接口

13

Function 函数式接口

public class FunctionDemo {
    public static void main(String[] args) {
        //返回参数的接口
        /*Function<String ,String> function = new Function<String, String>() {
            @Override
            public String apply(String s) {
                return s;
            }
        };*/
        /*Function<String,String> function = (s)->{
            return s;
        };*/
        Function<String,String> function = s->{
            return s;
        };
        System.out.println(function.apply("123"));
    }
}

predicate 断定型接口

public class PredicateDemo {
    public static void main(String[] args) {
        //返回输入是否等于0的接口
        /*Predicate<Integer> predicate =new Predicate<Integer>() {
            @Override
            public boolean test(Integer integer) {
                return integer == 0;
            }
        };*/
        /*Predicate<Integer> predicate = (a)->{
            return a==0;
        };*/
        Predicate<Integer> predicate = a->{
            return a==0;
        };
        System.out.println(predicate.test(0));
        System.out.println(predicate.test(1));

    }
}

Consumer 消费型接口

public class ConsumerDemo {
    public static void main(String[] args) {
        //输出传入的参数的接口
//        Consumer<Integer> consumer = new Consumer<Integer>() {
//            @Override
//            public void accept(Integer integer) {
//                System.out.println(integer);
//            }
//        };
//        Consumer<Integer> consumer = (a)->{
//            System.out.println(a);
//        };
//        Consumer<Integer> consumer = a->{
//            System.out.println(a);
//        };
        Consumer<Integer> consumer = System.out::println;
        consumer.accept(123);
    }
}

Supplier 生产型接口

public class SupplierDemo {
    public static void main(String[] args) {
        /*Supplier<Double> supplier = new Supplier<Double>() {
            @Override
            public Double get() {
                return Math.random();
            }
        };*/
        /*Supplier<Double> supplier = ()->{
            return Math.random();
        };*/
        Supplier<Double> supplier = Math::random;
        System.out.println(supplier.get());

    }
}

13.Stream流式计算

什么是流式计算

大数据:存储 + 计算

存储:集合,Mysql 本质就是存东西的

计算都应该交给流操作!

/**
 * 题目要求 : 一行代码实现
 * 现有五个用户!筛选:
 * 1.id必须是偶数
 * 2.年龄呢必须大于21岁
 * 3.用户名转换为大写字母
 * 4.用户名字母倒着排序
 * 5.只输出一个用户
 * **/
public class Test {
    public static void main(String[] args) {
        User u1 = new User(1,"a",21);
        User u2 = new User(2,"b",22);
        User u3 = new User(3,"c",23);
        User u4 = new User(4,"d",24);
        User u5 = new User(6,"e",25);
        //集合只需要存储
        List<User> list = Arrays.asList(u1,u2,u3,u4,u5);
        //流负责操作
        //lambda表达式,链式编程,函数式接口,Stream流式计算
        list.stream().
                filter(u->{return u.getId()%2 == 0;}).
                filter(u->{return u.getAge() > 21;}).
                peek((u)-> u.setName(u.getName().toUpperCase())).
                sorted((a,b)->{return b.getName().charAt(0) -  a.getName().charAt(0);}).
                limit(1).
                forEach(System.out::println);
    }
}

14.ForkJoin

什么是ForkJoin

ForkJoin出现在jdk1.7,并行执行任务!提高效率,处理大数据量!

大数据:Map Reduce(把大任务分成小任务) – 分治的思想

15

ForkJoin特点:工作窃取

这个里面维护的都是双端队列,b执行完之后可以存a队列的另一端偷任务去执行

16

Test类

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

public class Test {
    static long start = 0;
    static long end = 0;
    public static void main(String[] args) {
        start = 1;
        end = 10_0000_0000;
        test1();
        test2();
        test3();
    }
    public static void test1(){
        long start = System.currentTimeMillis();
        long sum = 0;
        for(long i = Test.start; i <= end ; i++){
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("sum="+sum+" 时间 : " + (end - start));
    }
    public static void test2(){
        long start = System.currentTimeMillis();
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinDemo task = new ForkJoinDemo(Test.start,Test.end);
        pool.execute(task);
        long sum = 0;
        try {
            sum = task.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        long end = System.currentTimeMillis();
        System.out.println("sum="+sum+" 时间 : " + (end - start));
    }
    public static void test3(){
        long start = System.currentTimeMillis();
        long sum = LongStream.rangeClosed(Test.start,Test.end).parallel().reduce(0,Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("sum="+sum+" 时间 : " + (end - start));
    }

}

forkJoin类

/**
 *
 * 求和计算任务
 * 如何使用ForkJoin
 * 1.ForkJoinPool通过它执行
 * 2.ForkJoinTask
 *
 * **/
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){
            //todo 分支合并计算
            long mid = (start + end) / 2 ;
            ForkJoinDemo task1 = new ForkJoinDemo(start,mid);
            task1.fork();//拆分任务,把线程加入线程队列
            ForkJoinDemo task2 = new ForkJoinDemo(mid + 1,end);
            task2.fork();//拆分任务,把线程加入线程队列
            return task1.join() + task2.join();

        }else{
            long sum = 0;
            for (long i = start; i <=end ; i++) {
                sum += i;
            }
            return sum;
        }
    }
}

15.异步回调

​ Future 设计的初衷:对某个事件的返回结果进行建模

//异步调用 : CompletableFuture
//异步执行
//成功回调
//失败回调
// runAsync 无返回值的异步任务
// supplyAsync 有返回值的异步任务
public class Demo01 {
    public static void main(String[] args) {
//        CompletableFuture<Void> future = CompletableFuture.runAsync(()-> {
//            try {
//                TimeUnit.SECONDS.sleep(3);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            System.out.println("async run") ;
//        });
//        try {
//            TimeUnit.SECONDS.sleep(2);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        System.out.println("Main run");
//        try {
//            future.get();
//        } catch (InterruptedException | ExecutionException e) {
//            e.printStackTrace();
//        }



        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(()-> {
            //int a = 1/0;
            return 1024;
        });
        try {
            System.out.println(future.whenComplete((w, u) -> { }).exceptionally((e) -> {
                return 233;
            }).get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

16.JMM

请你谈谈对Volatile的理解

Volatile 是java虚拟机提供的轻量级的同步机制

1.保证可见性

2.不保证原子性

3.禁止指令重排

什么是JMM

JMM:java内存模型,不存在的东西,概念!约定!

关于JMM的一些同步的约定:

1.线程解锁前,必须把共享变量**立刻**刷会主存。

2.线程加锁前,必须读取主存中的最新值到工作内存中!

3.加锁和解锁是同一把锁。

线程 主存,工作内存

问题:线程b修改了值,但线程a不能及时可见

17

内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)

    • lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
  • unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
  • read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
  • use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
  • assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
  • store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
  • write  (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中

JMM对这八种指令的使用,制定了如下规则:

    • 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
  • 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
  • 不允许一个线程将没有assign的数据从工作内存同步回主内存
  • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
  • 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
  • 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
  • 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
  • 对一个变量进行unlock操作之前,必须把此变量同步回主内存

JMM对这八种操作规则和对volatile的一些特殊规则就能确定哪里操作是线程安全,哪些操作是线程不安全的了。但是这些规则实在复杂,很难在实践中直接分析。所以一般我们也不会通过上述规则进行分析。更多的时候,使用java的happen-before规则来进行分析。

17.Volatile

可见性

//可见性测试
public class VolatileTest {
    //程序不知道内存中的值已经被修改过了
    private static volatile int num = 0;
    public static void main(String[] args) {
        new Thread(()->{
            while (num == 0){

            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num = 1;
        System.out.println(num);
    }

}

2.不保证原子性

原子性:不可分割

线程a在执行的时候,不能被打扰

atomic原子类,这些类底层与操作系统挂钩,在内存中修改值!Unsafe类是一个很特殊的存在

//不保证原子性
public class VolatileTest1 {
    private static  AtomicInteger num = new AtomicInteger(0);
    //1.synchronized能保证值为20000
    //2.volatile不保证原子性
    //3.使用原子类解决原子性问题
    public static void add(){
        //num++;//不是一个原子性操作
        num.getAndIncrement();//1.方法:CAS
    }
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println(num);
    }
}

18.单例模式

饿汉式 DCL懒汉式

饿汉式

public class Hungry {
    private byte[] data1 = new byte[1048576];
    private byte[] data2 = new byte[1048576];
    private byte[] data3 = new byte[1048576];
    private byte[] data4 = new byte[1048576];
    private static final Hungry HUNGRY = new Hungry();

    private Hungry() {
    }

    public static Hungry getInstance() {
        return HUNGRY;
    }
}

DCL懒汉式

public class Lazy {
    private static boolean key = false;
    private static volatile Lazy LAZY;

    private Lazy() {
        Class var1 = Lazy.class;
        synchronized(Lazy.class) {
            if (!key) {
                key = true;
            } else {
                throw new RuntimeException("不要试图使用反射破环异常");
            }
        }
    }

    public static Lazy getInstance() {
        if (LAZY == null) {
            Class var0 = Lazy.class;
            synchronized(Lazy.class) {
                if (LAZY == null) {
                    LAZY = new Lazy();
                }
            }
        }

        return LAZY;
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        Constructor<Lazy> constructor = Lazy.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Lazy instance2 = (Lazy)constructor.newInstance();
        Field key = Lazy.class.getDeclaredField("key");
        key.set(instance2, false);
        System.out.println(instance2);
        System.out.println(getInstance());
    }
}

静态内部类

public class Holder {
    private Holder() {
    }

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

    public static void main(String[] args) {
    }

    public static class InnerClass {
        private static final Holder HOLDER = new Holder();

        public InnerClass() {
        }
    }
}

单例不安全,因为有反射 所以要用枚举 :

21

枚举的最终反编译源码

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumSingle.java

package 53554F8B6A215F0F;


public final class EnumSingle extends Enum
{

    public static EnumSingle[] values()
    {
        return (EnumSingle[])$VALUES.clone();
    }

    public static EnumSingle valueOf(String name)
    {
        return (EnumSingle)Enum.valueOf(53554F8B6A215F0F/EnumSingle, name);
    }

    private EnumSingle(String s, int i)
    {
        super(s, i);
    }

    public static final EnumSingle INSTANCE;
    private static final EnumSingle $VALUES[];

    static 
    {
        INSTANCE = new EnumSingle("INSTANCE", 0);
        $VALUES = (new EnumSingle[] {
            INSTANCE
        });
    }
}

枚举保证线程安全,不会被反射入侵

19.深入理解CAS

什么是CAS ?

import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {
    //CAS   compareAndSet : 比较并交换!
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);

        // expect期望,update更新
        // public final boolean compareAndSet(int expect, int update)
        //如果我期望的值达到了就更新,否则就不更新 , cas是cpu的并发原语!
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());

        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());


    }
}

22

23

CAS : 比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么执行操作,如果不是就一直循环!

缺点:

1.由于底层是自旋锁,循环会耗时

2.一次性只能保证一个共享变量的原子性

3.ABA问题

ABA问题:

A要把变量改为2,在A的cas操作中,B线程把A改为3又改回了1,A对此不知情

25

import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {
    //CAS   compareAndSet : 比较并交换!
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);

        // 对于我平时写的sql : 乐观锁!
        // expect期望,update更新
        // public final boolean compareAndSet(int expect, int update)
        //如果我期望的值达到了就更新,否则就不更新 , cas是cpu的并发原语!
        //===========捣乱的线程++++++++++++++++
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());

        System.out.println(atomicInteger.compareAndSet(2021, 2020));
        System.out.println(atomicInteger.get());

        // ==================期望的线程-------------
        System.out.println(atomicInteger.compareAndSet(2020, 6666));
        System.out.println(atomicInteger.get());
    }
}

20.原子引用

带版本号的原子操作!解决ABA问题,引入原子引用,对应的思想:乐观锁

注意

Integer 使用了对象缓存机制,默认范围是-128-127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valuerOf使用缓存,而new一定会为创建新的对分配新的内存空间;

public class CASDemo {
    //CAS   compareAndSet : 比较并交换!
    public static void main(String[] args) {
        //AtomicInteger atomicInteger = new AtomicInteger(2020);
        //Integer 在-128到127之间是同一个对象,超过这个范围就会是新的对象
        //正常的业务操作中,比较的都是一个个对象
        AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1,1);

        new Thread(()->{
            //获得版本号
            System.out.println("a1->"+ atomicInteger.getStamp());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicInteger.compareAndSet(1,3,atomicInteger.getStamp(),atomicInteger.getStamp() + 1);
            System.out.println("a2->"+atomicInteger.getStamp());

            atomicInteger.compareAndSet(3,1,atomicInteger.getStamp(),atomicInteger.getStamp() + 1);
            System.out.println("a3->"+atomicInteger.getStamp());
        },"a").start();
        
        
        //和乐观锁的原理相同
        new Thread(()->{
            //获得版本号
            int stamp = atomicInteger.getStamp();
            System.out.println("b1->"+stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicInteger.compareAndSet(1,2,stamp,stamp+1));
        },"b").start();

    }
}

21.各种锁的理解

1.公平锁,非公平锁

公平锁:非常公平,不能插队

非公平锁,可以插队,默认都是非公平锁

2.可重入锁

可重入锁(递归锁)

26

3.自旋锁

spinLock

//自旋锁
class MyLock{
    //加锁,解锁

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

    private int count = 0;


    public void lock(){
        Thread thread = Thread.currentThread();
        if(atomicReference.get() == thread){
            count ++;
            return;
        }
        while(!atomicReference.compareAndSet(null,thread)){

        }
        System.out.println(thread.getName() + "加锁成功");
    }
    public void unlock(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName() + "解锁了");
        if(atomicReference.get() == thread){
            if(count > 0){
                count --;
            }else{
                atomicReference.compareAndSet(thread,null);
            }
        }
    }

}
class Test{
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(()->{
            phone.sms();
        }).start();
        new Thread(()->{
            phone.sms();
        }).start();
    }
}
class Phone{
    MyLock lock = new MyLock();
    public void sms(){
        lock.lock();
        try{
            System.out.println(Thread.currentThread().getName() + "sms");
            phone();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void phone(){
        lock.lock();
        try{
            System.out.println(Thread.currentThread().getName() + "phone");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

4.死锁

死锁是什么

a有a的锁,b有b的锁

他们都想抢占对方的锁,就会死锁

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

public class Demo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sLock1();
        }).start();

        new Thread(() -> {
            phone.sLock2();
        }).start();
    }
}
class Phone{
    ReentrantLock lock1 = new ReentrantLock();
    ReentrantLock lock2 = new ReentrantLock();
    public void sLock1(){
        lock1.lock();
        try{
            System.out.println(Thread.currentThread().getName() + "sLock1");
            TimeUnit.SECONDS.sleep(1);
            sLock2();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock1.unlock();
        }

    }
    public void sLock2(){
        lock2.lock();
        try{
            System.out.println(Thread.currentThread().getName() + "sLock2");
            TimeUnit.SECONDS.sleep(1);
            sLock1();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock2.unlock();
        }
    }
}

死锁测试,怎么排除死锁:

1.使用jps定位进程号

2.使用jstack 进程号 查看信息

排查问题: 1.日志 2.堆栈信息

完结撒花

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值