多线程进阶

JUC

1. 什么是JUC

java,util下面的包,线程,锁相关的类

2. 线程和进程

线程和进程

进程:一个程序,资源分配的单位

线程:java默认有2个线程,主线程和GC线程

java是否真的能开启线程? 并不行(本地方法c++库)

public synchronized void start() {
        if (this.threadStatus != 0) {
            throw new IllegalThreadStateException();
        } else {
            this.group.add(this);
            boolean started = false;

            try {
                this.start0();
                started = true;
            } finally {
                try {
                    if (!started) {
                        this.group.threadStartFailed(this);
                    }
                } catch (Throwable var8) {
                }

            }

        }
    }

    //本地方法,java无法直接操作硬件
    private native void start0();

并发和并行

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

  • CPU一个核,快速交替

并发:多个人一起走

  • CPU多核,多个核心同时运行
//查看CPU核数
Runtime.getRuntime().availableProcessors()

并发编程:充分利用CPU的资源

线程状态

public static enum State {
    	//新建
        NEW,
    	//运行
        RUNNABLE,
    	//阻塞
        BLOCKED,
    	//等待
        WAITING,
    	//超时等待
        TIMED_WAITING,
    	//死亡
        TERMINATED;

        private State() {
        }
    }

wait/sleep区别

  1. 来自不同的类

    wait=>Object

    sleep=>Thread

  2. 关于锁的释放

    wait---释放锁

    sleep---不释放锁

  3. 使用范围

    wait---必须在同步代码块中使用

    sleep---可以任何地方

  4. 捕获异常

    wait不需要捕获异常

    sleep需要捕获异常

3. Lock锁

传统Synchronized

Lock接口

公平锁:先来后到

非公平锁:可以插队(默认)

Synchronized和Lock的区别

  1. Synchronized是内置的关键字,Lock是一个类
  2. Synchronized 无法判断获取锁的状态,Lock可以判断
  3. Synchronized 会自动释放锁,Lock需要手动释放锁
  4. Synchronized 线程1(获取锁,直接阻塞),线程2(死等解锁),Lock锁不一定一直等
  5. Synchronized 可重入锁,不可以中断,非公平;Lock,可重入锁,可以判断锁,非公平(可以自己设置)
  6. Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步问题

锁是什么,如何判断所得是谁

4. 生产者和消费者

public class A {
    public static void main(String[] args) {
        Data data = new Data();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                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) {
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+ "=>"+ number);
        //通知其他线程
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        if (number == 0) {
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+ "=>"+ number);
        //通知其他线程
        this.notifyAll();
    }
}

问题存在,A,B,C,D四个线程---虚假唤醒

if 改为while判断

public class A {
    public static void main(String[] args) {
        Data data = new Data();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            }, "C").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}


class Data{
    private int number = 0;

    public synchronized void increment() throws InterruptedException {
        while (number != 0) {
            //等待
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+ "=>"+ number);
        //通知其他线程
        this.notifyAll();
    }

    public synchronized void decrement() throws InterruptedException {
        while (number == 0) {
            //等待
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+ "=>"+ number);
        //通知其他线程
        this.notifyAll();
    }
}

JUC的生产者消费者

通过Lock找到Condition方法

Synchronized---wait和notify

Lock---await和signal

代码实现

package com.liu;

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

public class B {
    public static void main(String[] args) {
        Data2 data = new Data2();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}


class Data2 {
    private int number = 0;

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

    public void increment() throws InterruptedException {
        try {
            lock.lock();
            while (number != 0) {
                //等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+ "=>"+ number);
            //通知其他线程
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public synchronized void decrement() throws InterruptedException {
        try {
            lock.lock();
            while (number == 0) {
                //等待
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+ "=>"+ number);
            //通知其他线程
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Condition--精准的通知和唤醒线程

package com.liu;

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

public class C {
    public static void main(String[] args) {
        Data3 data3 = new Data3();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data3.printA();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data3.printB();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data3.printC();
            }
        }, "C").start();

    }
}


class Data3 {
    private Lock lock = new ReentrantLock();

    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    private int number = 1;

    public void printA() {
        lock.lock();
        try {
            while (number != 1) {
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+ "=>AAAAAA");
            number = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB() {
        lock.lock();
        try {
            while (number != 2) {
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+ "=>BBBBB");
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC() {
        lock.lock();
        try {
            while (number != 3) {
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>CCCCC");
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

5. 8锁现象

小结

//1.标准情况下,两个线程哪个先打印--顺序执行
//2.第一个延迟4秒,两个线程谁先打印--顺序执行
//3.增加一个普通方法,先执行哪个?--普通方法
//4.两个对象,谁先执行---时间快的执行
//5.一个对象增加两个静态同步方法--顺序执行
//6.两个对象,静态方法---顺序执行
//7.一个对象普通的同步方法和静态同步方法---普通同步方法
//8.两个对象普通同步方法和静态同步方法---普通同步方法

new this 一个具体的对象

class 唯一的模板

6. 集合类不安全

ArrayList安全性

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;

public class ListTest {
    public static void main(String[] args) {
        List<String> list = new CopyOnWriteArrayList<>();
        //并发下的ArrayList是不安全的
        //解决方案
        //1.List<String> list = new Vector<>();
        //2.List<String> list = Collections.synchronizedList(new ArrayList<>());
        //3.List<String> list = new CopyOnWriteArrayList<>();

        //CopyOnWrite 写入时复制,COW 优化策略
        //在写入的适合避免覆盖,避免数据问题
        //读写分离
        //CopyOnWriteArrayList 比Vector好在哪里:第一个读的时候不加锁,读的速度较快,jdk11将add方法从Lock变成Synchronized
        for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString());
                System.out.println(list);
            }).start();
        }
    }
}

Set

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

public class SetTest {
    public static void main(String[] args) {
//        Set<String> set = new HashSet<>();
//        Set<String> set = Collections.synchronizedSet(new HashSet<>());
        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 1; i <= 30; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString());
                System.out.println(set);
            }).start();
        }
    }
}

hashSet的底层

public HashSet() {
        this.map = new HashMap();
}
//add set 本质就是map key 是无法重复
public boolean add(E e) {
        return this.map.put(e, PRESENT) == null;
}
private static final Object PRESENT = new Object(); 

hashMap

package com.liu.unsafe;

import javax.swing.plaf.synth.SynthOptionPaneUI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class MapTest {
    public static void main(String[] args) {
        //map
//        Map<String, String> map= new HashMap<>(16, 0.75f);
        //加载因子,默认容量
//        Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 1; i < 30; i++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString());
                System.out.println(map);
            }).start();
        }
    }
}

7.Callable

  1. 可以有返回值
  2. 可以抛出异常
  3. 方法不同
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //new Thread(new Runnable()).start();
        //new Thread(new FutureTask<V>()).start();
        //new Thread(new FutureTask<V>(Callable)).start();
        MyThread myThread = new MyThread();
        //适配类
        FutureTask futureTask = new FutureTask(myThread);
        new Thread(futureTask, "A").start();
        new Thread(futureTask, "B").start();//结果缓存
        Integer o = (Integer) futureTask.get();//结果阻塞
        System.out.println(o);
    }
}

class MyThread implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("call()");
        return "1024";
    }
}

8.常用辅助类

8.1 CountDownLatch---减法计数器

import java.util.concurrent.CountDownLatch;

//计数器
public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        //总数为6
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "go out");
                countDownLatch.countDown();
            }, String.valueOf(i)).start();
        }
        countDownLatch.await();//等待计数器归零,然后向下执行
        System.out.println("close");
    }
}

原理:

countDownLatch.countDown();//数量减1

countDownLatch.await();//等待计数器归零,向下执行

每次线程调用计数器进行减一,直到计数器归零。

8.2 CyclicBarrier---加法计数器

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

public class CyclicBarrierTest {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("成功");
        });
        for (int i = 1; i <= 7; i++) {
            final int temp = i;
            new Thread(()-> {
                System.out.println(Thread.currentThread().getName() + "第" + temp + "个");
                try {
                    cyclicBarrier.await();//等待
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

8.3 Semaphore---信号量

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

public class SemaphoreTest {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                //acquire()  得到
                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();//release() 释放
                }
            }, String.valueOf(i)).start();
        }
    }
}

原理

semaphore.acquire() 获得,如果满就等待释放

semaphore.release() 释放,当前的信号量释放+1,唤醒等待的线程

作用:多个共享资源互斥的作用,并发限流,控制最大的线程数量

9. 读写锁

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriterLockDemo {
    public static void main(String[] args) {
        MyCacheLock myCache = new MyCacheLock();

        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.put(temp + "", temp + "");
            }, String.valueOf(i)).start();
        }


        for (int i = 1; i <= 5; i++) {
            final int temp = i;
            new Thread(() -> {
                myCache.get(temp + "");
            }, String.valueOf(i)).start();
        }
    }
}


class MyCache {
    private volatile Map<String, Object> map = new HashMap<>();


    //存,写
    public void put(String key, Object value) {
        System.out.println(Thread.currentThread().getName() + "写入" + key);
        map.put(key, value);
        System.out.println(Thread.currentThread().getName() + "写入完成");
    }

    //取,读
    public void get(String key) {
        System.out.println(Thread.currentThread().getName() + "取出" + key);
        map.get(key);
        System.out.println(Thread.currentThread().getName() + "取出完成");
    }
}


class MyCacheLock {
    private volatile Map<String, Object> map = new HashMap<>();
    //读写锁,更加细粒度的控制
    private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();

    //存,写入的时候,只希望同时只有一个线程写
    public void put(String key, Object value) {
        reentrantReadWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "写入" + key);
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入完成");
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            reentrantReadWriteLock.writeLock().unlock();
        }
    }

    //取,读
    public void get(String key) {
        reentrantReadWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "取出" + key);
            map.get(key);
            System.out.println(Thread.currentThread().getName() + "取出完成");
        } catch (Exception e) {
            e.printStackTrace();
        } finally 
            reentrantReadWriteLock.readLock().unlock();
        }
    }
}

10. 阻塞队列

BlockingQueue

什么情况下使用阻塞队列:多线程并发处理,线程池

使用队列

四组API

方式 抛出异常 有返回值,不抛出异常 阻塞等待 超时等待
添加 add offer() put offer(,)
移除 remove poll() take poll(,)
判断队列首 element peek
 //抛出异常
    public static void test1() {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));

        //抛出异常java.lang.IllegalStateException: Queue full
        //System.out.println(blockingQueue.add("d"));

        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());

        //抛出异常java.util.NoSuchElementException
        //System.out.println(blockingQueue.remove());
}
//有返回值,不抛出异常
public static void test2() {
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));

        //返回false,不抛出异常
        //System.out.println(blockingQueue.offer("d"));
        System.out.println("============");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());

        //取出null值,不抛出异常
        //System.out.println(blockingQueue.poll());
    }
//等待,阻塞(一直阻塞)
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
        //队列位置满了,一直等待
        //blockingQueue.put("d");
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        //没有这个元素,一直阻塞
        //System.out.println(blockingQueue.take());
 }
//等待,阻塞(超时时间)
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);

        blockingQueue.offer("a");
        blockingQueue.offer("b");
        blockingQueue.offer("c");
        //等待超过2秒退出
        //blockingQueue.offer("d", 2, TimeUnit.SECONDS);
        System.out.println("=======");
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        //等待超过2秒退出
        //blockingQueue.poll(2, TimeUnit.SECONDS);
    }

SynchronousQueue同步队列

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

/**
 * 同步队列,SynchronousQueue不存储元素
 * put进去之后,只能拿出来才能继续放
 */
public class SynchronousQueueDemo {
    public static void main(String[] args) {
        SynchronousQueue<String> blockingQueue = new SynchronousQueue<>();

        new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + " put 1");
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName() + " put 2");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName() + " put 3");
                blockingQueue.put("3");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "T1").start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "=" + blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "=" + blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName() + "=" + blockingQueue.take());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "T2").start();
    }
}

11.线程池

线程池:三大方法,7大参数,4种拒绝策略

池化技术

程序运行的本质:占用系统的资源,优化资源的使用=>池化技术

线程池的好处:线程复用,可以控制最大并发数,管理线程

线程池:三大方法

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

public class Demo1 {
    public static void main(String[] args) {
//        ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
//        ExecutorService threadPool = Executors.newFixedThreadPool(5);//固定大小的线程池
        ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩的线程池
        try {
            for (int i = 0; i < 100; i++) {
                //线程池创建线程
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }

    }
}

7大参数

源码分析

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
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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

手动创建一个线程池

import java.util.concurrent.*;

public class Demo1 {
    public static void main(String[] args) {
        //自定义线程池
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试去和最早的竞争,不抛出异常
        );
        try {
            //最大承载:Deque + max
            //超过RejectedExecutionException
            for (int i = 1; i <= 9; i++) {
                //线程池创建线程
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }

    }
}

四种拒绝策略

//ThreadPoolExecutor.AbortPolicy()//队列满了,不处理抛出异常
//ThreadPoolExecutor.CallerRunsPolicy()//哪来的去哪里
//ThreadPoolExecutor.DiscardPolicy()//队列满了,丢掉任务,不会抛出异常
//ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试去和最早的竞争,不抛出异常

小结和拓展

package com.liu.pool;

import java.util.concurrent.*;

public class Demo1 {
    public static void main(String[] args) {
        //自定义线程池
        //最大线程如何定义
        //1.CPU密集型  多少核CPU定义为多少
        //2.IO密集型   大于耗IO的线程
        //获取CPU和核数
        System.out.println(Runtime.getRuntime().availableProcessors());
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()//队列满了,尝试去和最早的竞争,不抛出异常
        );
        try {
            //最大承载:Deque + max
            //超过RejectedExecutionException
            for (int i = 1; i <= 9; i++) {
                //线程池创建线程
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }

    }
}

12.四大函数式接口

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

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
//简化编程模型
//foreach(消费者函数式接口)

代码测试

public interface Function<T, R> {
    R apply(T t);
}

import java.util.function.Function;
//Function接口,有一个参数,有一个输出,可以使用Lambda表达式
public class Demo01 {
    public static void main(String[] args) {
//        Function function = new Function<String, String>() {
//            @Override
//            public String apply(String s) {
//                return s;
//            }
//        };
        Function<String, String> function = (str) -> {return str;};
        System.out.println(function.apply("asd"));
    }
}

断定型接口:有一个输入参数,返回值只能是布尔值

代码测试

public interface Predicate<T> {
    boolean test(T t);
}

import java.util.function.Predicate;

/**
 * 断定型接口,有一个输入参数,返回值只能是布尔值
 */
public class Demo02 {
    public static void main(String[] args) {
        //判断字符串是否为空
//        Predicate<String> predicate = new Predicate<String>() {
//            @Override
//            public boolean test(String str) {
//                return str.isEmpty();
//            }
//        };
        Predicate<String> predicate = String::isEmpty;
        System.out.println(predicate.test(""));
    }
}

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

public interface Consumer<T> {
    void accept(T t);
]

import java.util.function.Consumer;

/**
 * Consumer消费型接口:只有输入,没有返回
 */
public class Demo03 {

    public static void main(String[] args) {
//        Consumer<String> consumer = new Consumer<String>() {
//            @Override
//            public void accept(String s) {
//                System.out.println(s);
//            }
//        };
        Consumer<String> consumer = System.out::println;
        consumer.accept("afsad");
    }
}

Supplier:供给型接口

public interface Supplier<T> {
    T get();
}

import java.util.function.Supplier;

/**
 * Supplier供给型接口:没有输入参数,有返回值
 */
public class Demo04 {
    public static void main(String[] args) {

//        Supplier supplier = new Supplier<Integer>() {
//            @Override
//            public Integer get() {
//                System.out.println("get()");
//                return 1024;
//            }
//        };
        Supplier supplier = () -> {
            return 1024;
        };
        System.out.println(supplier.get());
    }
}

13.strean流式计算

流式计算

存储和计算--->计算交给流去计算

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        User user1 = new User(1, "a", 21);
        User user2 = new User(2, "b", 22);
        User user3 = new User(3, "c", 23);
        User user4 = new User(4, "d", 24);
        User user5 = new User(6, "e", 25);
        List<User> users = Arrays.asList(user1, user2, user3, user4, user5);
        //计算交给stream流
        //lambda表达式,链式编程,函数式接口,Stream流式计算
        users.stream()
                .filter(u -> {return u.getId() % 2 == 0;})
                .filter(u -> {return u.getAge() > 23;})
                .map(u -> {return u.getName().toUpperCase();})
                .sorted(Comparator.naturalOrder())
                .forEach(System.out::println);
    }
}

14.ForkJoin

什么是ForkJoin

在jdk1.7之后出现,并发执行任务,提高效率

大任务分治归并

ForkJoin特点:工作窃取

里面维护的是双端队列

ForkJoin

import java.util.concurrent.RecursiveTask;

/**
 * 求和计算的任务
 * ForkJoin stream并行流
 * 如何使用forkJoin
 * 1.forkJoinPool 通过这个执行
 * 2,计算任务,ForkJoinPool.execute(ForkJoinTask task)
 * 3. 计算类继承ForkJoinTask
 * ForkJoinTask中RecursiveAction(递归事件,没有返回值)
 * ForkJoinTask中RecursiveTask(递归任务,有返回值)
 */
public class ForkJoinDemo extends RecursiveTask<Long> {
    private Long start;
    private Long end;

    //临界值
    private Long temp = 10000L;

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

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

测试类

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

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //test1();//16009
        //test2();//8569
        //test3();//1350
    }

    public static void test1() {
        Long sum = 0L;
        long start = System.currentTimeMillis();
        for (Long i = 1L; i < 10_0000_0000; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("sum=" + " 时间: " + (end - start));
    }


    public static void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);//提交任务
        Long sum = submit.get();
        long end = System.currentTimeMillis();
        System.out.println("sum=" + "时间" + (end - start));
    }


    public static void test3() {
        long start = System.currentTimeMillis();
        //Stream并行流
        long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("sum=" + "时间" + (end - start));
    }
}

15.异步回调

Future设计的初衷:对将来的某个事件的结果进行建模

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * 异步调用 CompletableFuture
 * // 异步执行
 * // 成功回调
 * // 失败回调
 */
public class Demo01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //没有返回值的异步回调
//        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
//            try {
//                TimeUnit.SECONDS.sleep(2);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            System.out.println(Thread.currentThread().getName() + "runAsync=>Void");
//        });
//        System.out.println("1111");
//        completableFuture.get();
        //有返回值的supplyAsync 异步回调
        //
        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "supplyAsync=>Integer");
            int i = 10 / 0;
            return 1024;
        });

        System.out.println(completableFuture.whenComplete((t, u) -> {
            System.out.println("t=>" + t);//正常的返回结果
            System.out.println("u=>" + u);//错误的信息
        }).exceptionally((e) -> {
            System.out.println(e.getMessage());
            return 233;//获取到错误的返回结果
        }).get());
    }
}

16. JMM

Volatile理解

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

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排

什么是JMM

JMM:java内存模型,不存在的东西,是一个约定

关于JMM的一些同步的约定

  1. 线程解锁前,必须把共享变量立刻刷回主存
  2. 线程加锁前,必须读取主存中的最新值到工作内存中
  3. 加锁和解锁是同一把锁

线程 工作内存主内存

8种操作:

  1. 加锁---lock
  2. 读主存数据---read
  3. 加载主存数据到工作内存---load
  4. 执行引擎使用工作内存数据---use
  5. 执行引擎更新工作内存---assign
  6. 工作内存传送到主存---store
  7. 数据写入主存---write
  8. 解锁---unlock

问题:

17. Volatile

保证可见性

import java.util.concurrent.TimeUnit;

public class JMMDemo {
    //不加volatile程序死循环
    //加volatile程序直接停止
    private volatile static int num = 0;
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> { //线程不可知主线程
            while (num == 0) {

            }
        }).start();

        TimeUnit.SECONDS.sleep(1);

        num = 1;
        System.out.println(num);
    }
}

不保证原子性

原子性:不可分割

线程A在执行任务的时候,不能被打扰,也不能被分割。

public class VDemo02 {
    private volatile static int num = 0;

    //volatile不保证原子性
    public static void add() {
        num++;
    }
    public static void main(String[] args) {
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " " + num);
    }
}

如果不加lock和synchronized,如何保证原子性

使用原子类解决

import java.util.concurrent.atomic.AtomicInteger;

public class VDemo02 {
    //使用原子类
    private volatile static AtomicInteger num = new AtomicInteger();

    //volatile不保证原子性
    public static void add() {
        //num++;//不是原子性操作
        num.getAndIncrement();
    }
    public static void main(String[] args) {
        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount() > 2) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " " + num);
    }
}

这些类的底层都是操作系统挂钩,内存中修改值,Unsafe类

指令重排

计算机并不是按照你的程序顺序执行

源代码-->编译器优化的重排-->指令并行也可能会重排-->内存系统也会重排-->执行

处理器在进行指令重排的时候,考虑:数据之间的依赖问题

int x = 1;//1

int y = 2;//2

x = x + 5;//3

y = x * x;//4
我们期望的: 1234,实现可能是2134 2143

可能造成的影响:abxy这四个值默认为0

线程A 线程B
x=a y=b
b=1 a=2

正常的结果:x=0;y=0;但是可能由于指令重排

线程A 线程B
b=1 a=2
x=a y=b

指令重排的诡异结果:x=2;y=1;

volatile避免指令重排

内存屏障(CPU指令)。作用:

  1. 保证特定的操作的执行顺序
  2. 可以保证某些变量的内存可见性(volatile实现了可见性)

18. 单例模式

饿汉式,DCL懒汉式

DCL懒汉式

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class LazyMan {

    private static boolean qinjiang = false;

    private LazyMan() {
        synchronized (LazyMan.class) {
            if (qinjiang == false) {
                qinjiang = true;
            } else {
                throw new RuntimeException("不要利用反射破坏单例");
            }
        }
        System.out.println(Thread.currentThread().getName() + "ok");
    }
    //加volatile防止指令重排
    private volatile static LazyMan lazyMan;

    //单线程单例
//    public static LazyMan getInstance() {
//        if (lazyMan == null) {
//            lazyMan = new LazyMan();
//        }
//        return lazyMan;
//    }
    //双重检测锁模式,DCL懒汉模式
    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan();//不是原子操作
                    /**
                     * 1. 分配内存空间
                     * 2. 执行构造方法,初始化空间
                     * 3. 把这个对象指向这个空间
                     */
                }
            }
        }
        return lazyMan;// 此时lazyMan还没有完成构造
    }


    //多线程并发
//    public static void main(String[] args) {
//        for (int i = 0; i < 10; i++) {
//            new Thread(() -> {
//                LazyMan.getInstance();
//            }).start();
//        }
//    }
    //反射
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
        //LazyMan instance = LazyMan.getInstance();

        Field qinjiang = LazyMan.class.getDeclaredField("qinjiang");
        qinjiang.setAccessible(true);

        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        //两个都用构造器构造
        LazyMan instance = declaredConstructor.newInstance();
        qinjiang.set(instance, false);

        LazyMan instance2 = declaredConstructor.newInstance();

        System.out.println(instance);
        System.out.println(instance2);
    }
}

静态内部类

//静态内部类实现
public class Holder {
    private Holder() {

    }

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

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

单例不安全,反射

枚举

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

//enum本身也是一个类
public enum  EnumSingle {
    INSTANCE;

    public EnumSingle getInstance() {
        return INSTANCE;
    }
}

class Test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle instance2 = declaredConstructor.newInstance();
        System.out.println(instance1);
        System.out.println(instance2);
    }
}

有参构造

枚举类型最终的反编译源码

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

	public static EnumSingle valueof(String name) {
    	return (EnumSingle)Enum.valueof(EnumSingle, name);
	}

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

	public EnumSingle getInstance() {
   		return INSTANCE;
	}

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

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

19. 深入CAS

unSafe类

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;
    //java 无法操作内存,调用c++ native进行操作内存

getAndIncrement

public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
//自旋锁
public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);//期望内存地址中的值
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    //如果内存地址的是期望的内存地址的值,则进行+1操作

        return var5;
    }

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

缺点:

  1. 循环会耗时
  2. 一次性只能保证一个共享变量的原子性
  3. ABA问题

CAS:ABA问题(狸猫换太子)

package com.liu.cas;

import java.util.concurrent.atomic.AtomicInteger;

public class CASDemo {
    //CAS
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        // 期望,更新
        //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,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间

package com.liu.cas;

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

public class CASDemo {
    //CAS
    public static void main(String[] args) {
        //AtomicStampedReference注意,如果泛型是一个包装类,注意对象的引用问题
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println("a1=>" + stamp);

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicStampedReference.compareAndSet(1, 2,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("a2=>" + atomicStampedReference.getStamp());

            System.out.println(atomicStampedReference.compareAndSet(2, 1,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("a3=>" + atomicStampedReference.getStamp());
        }, "a").start();

        new Thread(() -> {
            int stamp = atomicStampedReference.getStamp();
            System.out.println("b1=>" + stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicStampedReference.compareAndSet(1, 6,
                    stamp, stamp + 1));

            System.out.println("b1=>" + atomicStampedReference.getStamp());
        }, "b").start();
    }
}

21. 各种锁的分析

1.公平锁,非公平锁

公平锁:先来后到

非公平锁:可以插队(默认)

public ReentrantLock() {
    sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

2. 可重入锁

3. 自旋锁

自定义自旋锁

package com.liu.lock;

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

public class SpinLockDemo {
    AtomicReference<Thread> atomicReference = new AtomicReference<>();
    //加锁
    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "===> mylock");
        //自旋锁
        while (!atomicReference.compareAndSet(null, thread)) {

        }
    }
    //解锁
    public void myUnLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "===> myUnlock");
        atomicReference.compareAndSet(thread, null);
    }
}

测试类

package com.liu.lock;

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

public class TestSpinLock {
    public static void main(String[] args) throws InterruptedException {
//        ReentrantLock reentrantLock = new ReentrantLock();
//        reentrantLock.lock();
//        reentrantLock.unlock();

        //
        SpinLockDemo lock = new SpinLockDemo();
        new Thread(() -> {
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }
        }, "T1").start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(() -> {
            lock.myLock();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.myUnLock();
            }
        }, "T2").start();
    }
}

4. 死锁

排除死锁

//产生死锁
package com.liu.lock;

import java.util.concurrent.TimeUnit;

public class DeadLockDemo {
    public static void main(String[] args) {
        String lockA = "A";
        String lockB = "B";

        new Thread(new myThread(lockA, lockB), "T1").start();
        new Thread(new myThread(lockB, lockA), "T2").start();
    }
}


class myThread implements Runnable{
    private String lockA;
    private String lockB;

    public myThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {
        synchronized (lockA) {
            System.out.println(Thread.currentThread().getName() + "lock" + lockA + "==>get" + lockB);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (lockB) {
                System.out.println(Thread.currentThread().getName() + "lock" + lockB + "==>get" + lockA);
            }
        }
    }
}

解决死锁

  1. 使用jsp -l定位进程号
  2. 使用jstack 进程号找到死锁问题

1.日志

2.堆栈信息

posted @ 2021-01-24 16:56  学徒之心  阅读(47)  评论(0编辑  收藏  举报