JUC并发编程

什么是JUC
# java 的三个工具类包 统称为 JUC  java.util.concurrent在并发编程中使用的工具类
# java.util.Concurrent
# java.util.concurrent.atomic
# java.util.concurrent.locks

# Java是不能直接操作线程的,是通过 native 关键字 利用底层 c++ 进行操作!
并发编程:
# 并发、并行
# 并发:多个线程操作同一个资源,CPU一核,模拟多条线程快速交替
# 并行:多条线程同时执行,CPU多核

// 获取CPU的核数
// CPU 密集型,IO 密集型
System.out.println(Runtime.getRuntime().availableProcessors());

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

# 线程的状态:
	1. NEW 				新生
	2. RUNNABLE			运行
	3. BLOCKED 			阻塞
	4. WAITING			等待,死等
	5. TIMED_WAITING	超时等待
	6. TERMINATED		终止
	
# wait 和 sleep 的区别:
1. 来自不同的类:wait:object、sleep:Thread
2. 关于锁的释放:wait会释放锁,sleep不释放锁
3. 使用的范围不同:wait必须在同步块中,sleep可以在任何地方
4. 是否需要捕获异常:wait不需要捕获,sleep需要捕获异常
Lock锁:
# 传统的 synchronized 方法实现线程同步:

// 传统 synchronized 实现线程同步
public class SynchronizedClass {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(() -> {
            for (int i = 0; i<40; i++){
                ticket.sale();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i<50; i++){
                ticket.sale();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i<60; i++){
                ticket.sale();
            }
        }, "C").start();
    }
}

class Ticket{
    private int number = 30;
    public synchronized void sale(){
        if(number > 0){
            System.out.println(Thread.currentThread().getName() + "卖出了: " + number-- + ", 剩余: " + number);
        }
    }
}


# Lock锁 实现线程同步:

/ Lock 实现线程同步
public class SynchronizedClass {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(() -> { for (int i = 0; i<40; i++) ticket.sale(); }, "A").start();
        new Thread(() -> { for (int i = 0; i<50; i++) ticket.sale(); }, "B").start();
        new Thread(() -> { for (int i = 0; i<60; i++) ticket.sale(); }, "C").start();
    }
}
class Ticket{
    private int number = 30;
    Lock lock = new ReentrantLock();
    public void sale(){
    
        lock.lock();
        
        try{
            if(number > 0){
                System.out.println(Thread.currentThread().getName() + "卖出了: " + number-- + ", 剩余: " + number);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

# synchronized 和 Lock锁 的区别:
1. synchronized 是内置的java关键字;Lock 是一个接口(java类)
2. synchronized 无法判断获取锁的状态;Lock 可以判断是否获取到了锁
3. synchronized 会自动释放锁;Lock必须手动释放锁,如果不释放:死锁
4. synchronized 如果一个线程获得锁阻塞了,后续的线程都会等待;Lock不一定会等待(tryLock())
5. synchronized 可重入锁,不可以中断,非公平;Lock,可重入锁,可以判断锁,非公平(可设置)
6. synchronized 适合锁少量的代码同步问题;Lock 适合锁大量的同步代码

线程虚假唤醒:
# 虚假唤醒:线程可以唤醒,而不会被通知、中断或超时
# 例如下边代码:如果判断是if判断就会出现虚假唤醒,如果是while则不会

# 释义:如果是if判断 会存在多个线程同时执行wait()方法等待被唤醒,被唤醒后直接执行了if后的语句
# 	   如果是while 即使存在多个同时执行wait()方法等待被唤醒,也可以再次进行判断!

// 传统生产者消费者问题
public class OneTest {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) data.increment();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "A").start();
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) data.increment();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "B").start();
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) data.decrement();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "C").start();
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) 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();
    }
}

Condition:
# Condition 实现线程通信

public class OneTest {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) data.increment();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "A").start();
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) data.increment();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "B").start();
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) data.decrement();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "C").start();
        new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) data.decrement();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "D").start();
    }
}
class Data{
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    public void increment() throws InterruptedException {
        lock.lock();
        try{
            while (number != 0){
                condition.await();;
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "->" + number);
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }

    }
    public void decrement() throws InterruptedException {
        lock.lock();
        try{
            while (number == 0){
                condition.await();;
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "->" + number);
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}


# Condition 实现精准通知唤醒线程

public class OneTest {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {for (int i = 0; i < 10; i++) data.printA();}, "A").start();
        new Thread(() -> {for (int i = 0; i < 10; i++) data.printB();}, "B").start();
        new Thread(() -> { for (int i = 0; i < 10; i++) data.printC(); }, "C").start();
    }
}
class Data{
    private int number = 1;
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();
    public void printA() {
        lock.lock();
        try{
            while (number != 1){
                condition1.await();
            }
            number = 2;
            System.out.println(Thread.currentThread().getName() + "->" + "AAAAAAAAAAB");
            condition2.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }

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

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

    }
}

八锁现象:
# 1. 都是 synchronized 修饰的方法,先打印发短信,后打电话
# 2. 都是 synchronized 修饰的方法如果某个方法sleep,先打印发短信,后打电话
# 3. 加普通方法先执行普通方法,不必等待
# 4. 两个对象两个同步方法不是static修饰,没有sleep的先输出
# 5. 两个静态同步方法放入一个对象,按顺序执行!
# 6. 两个静态同步放入两个对象,按顺序执行!
# 7. 一个静态一个同步,一个对象,没有sleep的先输出!
# 8. 一个静态一个同步,两个对象,没有sleep的先输出!

# 总结:
	1. synchronized 锁的是调用的对象 new Object();
	2. static 修饰后,锁的就是调用的对象类 Class

# 测试代码
public class LockTest {
    public static void main(String[] args) throws InterruptedException {
        A a = new A();
        new Thread(() -> {
            try {
                a.sendMsg();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> a.hello()).start();
        
    }
}
class A{
    public synchronized void sendMsg() throws InterruptedException {
        Thread.sleep(4000);
        System.out.println("发短信");
    }
    public synchronized void callPhone(){
        System.out.println("打电话");
    }
    public void hello(){
        System.out.println("hello");
    }
}
集合类不安全:
# List不安全:

// ConcurrentModificationException 并发修改异常
public class ListTest {
    public static void main(String[] args) {
        // 并发下 ArrayList不安全
        /**
         * 解决方案
         * 1. new Vector<>(); Vector.add 方法有synchronized修饰
         * 2. Collections.synchronizedList(new ArrayList<>());
         * 3. new CopyOnWriteArrayList<>();	JUC工具类方法
         **/
        // CopyOnWrite 写入时复制 COW 计算机程序设计领域的优化策略
        // 13比较:Vector synchronized的性能问题
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i <= 10 ; i++) {
            new Thread(() -> {
                list.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(list);
            }).start();
        }
    }
}


# Set 不安全:

// ConcurrentModificationException 并发修改异常
public class SetTest {
    public static void main(String[] args) {
        // 并发下 HashSet不安全
        /**
         * 解决方案
         * 1. Collections.synchronizedSet(new HashSet<>());
         * 2. new CopyOnWriteArraySet<>(); JUC工具类方法
         **/
        Set<String> set = new CopyOnWriteArraySet<>();
        for (int i = 0; i <= 30 ; i++) {
            new Thread(() -> {
                set.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(set);
            }).start();
        }
    }
}

# hashSet 底层是什么?
hashSet 底层是hashmap,
hashSet 添加值(add)实际就是往hashmap添加key值,
所以hashset没有重复值!


# Map 不安全:

// ConcurrentModificationException 并发修改异常
public class MapTest {
    public static void main(String[] args) {
        // 并发下 HashMap不安全
        /**
         * 解决方案
         * 1. new ConcurrentHashMap<>();
         **/
        Map<String, String> map = new ConcurrentHashMap<>();
        for (int i = 0; i <= 30 ; i++) {
            new Thread(() -> {
                map.put(Thread.currentThread().getName(),
                        UUID.randomUUID().toString().substring(0, 5));
                System.out.println(map);
            }).start();
        }
    }
}


Callable:
# 实现 Callable:

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallanbeDemo callanbeDemo = new CallanbeDemo();
        FutureTask futureTask = new FutureTask(callanbeDemo);
        new Thread(futureTask, "A").start();
        String str = (String)futureTask.get();
        System.out.println(str);
    }
}
class CallanbeDemo implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("调用成功!");
        return "返回的是 String 类型";
    }
}

# 细节:
get 有缓存,下次可以很快得到结果
get 的 结果可能需要等待,会阻塞!


# FutureTask:
FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable与Future接口,因此FutureTask既可以作为一个Runnable被Thread执行,也可以获取到Future异步计算的结果。

常用辅助类:
# CountDownLatch:(倒计时计数器)
// 作用:CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。
// CountDownLatch的不足:CountDownLatch是一次性的,计算器的值只能在构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用。

// 原理:
// countDownLatch.countDown() 数量 -1
// countDownLatch.await() 等待计数器归零再向后执行!
// 每次有线程调用countDownLatch.countDown(),假设计数器变为0,那么countDownLatch.await()就	  会被唤醒,继续执行!

// 倒计时计算器
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(5);
        for (int i = 0; i <= 4; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "      Get Out!");
                countDownLatch.countDown();
            }).start();
        }
        countDownLatch.await();
        System.out.println("Close Door");
    }
}


# CyclicBarrier:(加法计数器)
// 作用:让所有线程都等待完成后才会继续下一步行动。
// 使用场景:可以用于多线程计算数据,最后合并计算结果的场景。

// 加法计数器
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("线程数到了一定量,执行了此线程!!!");
        });
        for (int i = 0; i < 7 ; i++) {
            final int temp = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "   " + temp + "");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

#  CyclicBarrier 与 CountDownLatch 区别
1. CountDownLatch 是一次性的,CyclicBarrier 是可循环利用的
2. CountDownLatch 参与的线程的职责是不一样的,有的在倒计时,有的在等待倒计时结束。			      CyclicBarrier 参与的线程职责是一样的。


# Semaphore:(信号量)
// 原理:
// acquire()获得,假设已经满了,等待,等待被释放为止!
// release()释放,会将当前的信号量释放 + 1,然后唤醒等待的线程!
// 作用:多个共享资源互斥的使用!并发限流,控制最大的线程数!

public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 9; 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();
        }
    }
}

读写锁:
# ReadWriteLock 

# ReadWriteLock总结:
1. Java并发库中 ReetrantReadWriteLock 实现了 ReadWriteLock 接口并添加了可重入的特性
2. ReetrantReadWriteLock读写锁的效率明显高于synchronized关键字
3. ReetrantReadWriteLock读写锁的实现中,读锁使用共享模式;写锁使用独占模式,换句话说,读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的
4. ReetrantReadWriteLock读写锁的实现中,需要注意的,当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁

# 读可以被多线程同时读,写的时候只能有一个线程去写

//  ReadWriteLock:
// 读-读 可以共存,读-写 不能共存,写-写 不能共存
// 独占锁(写锁),共享锁(写锁)
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCacheLock myCacheLock = new MyCacheLock();
        for (int i = 0; i < 5 ; i++) {
            final int temp = i;
            new Thread(() -> {
                myCacheLock.put(temp + "", temp);
            }, String.valueOf(i)).start();
        }
        for (int i = 0; i < 5 ; i++) {
            final int temp = i;
            new Thread(() -> {
                myCacheLock.get(temp + "");
            }, String.valueOf(i)).start();
        }
    }
}
class MyCacheLock{
    private volatile Map<String, Object> map = new HashMap<>();
    // 读写锁:更加细粒度的控制
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private Lock lock = new ReentrantLock();
    public void put(String key, Object value){
        readWriteLock.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 {
            readWriteLock.writeLock().unlock();
        }
    }
    public void get(String key){
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 读取" + key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName() + " 读取OK");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            readWriteLock.readLock().unlock();
        }
    }
}


阻塞队列:
# BlockingQueue 

# 作用:BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利。

# 队列基本概念:
	写入时如果队列满了,就必须阻塞等待
	取时如果队列是空的,必须阻塞等待生产
	
# 使用场景:多线程并发处理、线程池!

# Queue 跟 List Set 同级,BlockingQueue 继承 Queue

四组API:
方式抛出异常有返回值,不抛出异常阻塞 等待超时等待
添加addoffer()put()offer(,)
移除removepoll()take()poll(,)
检测队首元素elementpeek
# ArrayBlockingQueue 常用API详解 :基于数组的阻塞队列实现


public class Test {
    public static void main(String[] args) throws InterruptedException {
        // 方式一
        // test1();
        // 方式二
        // test2();
        // 方式三
        // test3();
        // 方式四
        test4();
    }
    
    // 添加超出范围:java.lang.IllegalStateException: Queue full
    // 移除超出范围:java.util.NoSuchElementException
    public static void test1(){
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
        System.out.println(arrayBlockingQueue.add("A"));
        System.out.println(arrayBlockingQueue.add("B"));
        System.out.println(arrayBlockingQueue.add("C"));
        // System.out.println(arrayBlockingQueue.add("D"));
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        // System.out.println(arrayBlockingQueue.remove());
    }
    
    // 添加超出范围:返回false
    // 移除超出范围:返回null
    public static void test2(){
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
        System.out.println(arrayBlockingQueue.offer("A"));
        System.out.println(arrayBlockingQueue.offer("B"));
        System.out.println(arrayBlockingQueue.offer("C"));
        // System.out.println(arrayBlockingQueue.offer("D"));
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        // System.out.println(arrayBlockingQueue.poll());
    }
    
    // 添加超出范围:阻塞,一直等待
    // 移除超出范围:阻塞,一直等待
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
        arrayBlockingQueue.put("A");
        arrayBlockingQueue.put("B");
        arrayBlockingQueue.put("C");
        // arrayBlockingQueue.put("D");
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        // System.out.println(arrayBlockingQueue.take());
    }
    
    // 添加超出范围:超时等待,如果没有位置则返回 false
    // 移除超出范围:超时等待,如果没有移除东西则返回 null
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
        System.out.println(arrayBlockingQueue.offer("A"));
        System.out.println(arrayBlockingQueue.offer("B"));
        System.out.println(arrayBlockingQueue.offer("C"));
        // System.out.println(arrayBlockingQueue.offer("D", 2, TimeUnit.SECONDS));
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        // System.out.println(arrayBlockingQueue.poll(2, TimeUnit.SECONDS));
    }
}


# SynchronousQueue 同步队列:

// 只能存在一个值,移除后才能加入值
public class SynchronousQueueDeom {
    public static void main(String[] args) {
        // 同步队列
        BlockingQueue 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();
    }
}

线程池(重点):
# 三大线程池:(官方不建议使用线程池现有的工具类方法,建议自己使用 ThreadPoolExecutor 创建)
# newSingleThreadExecutor:创建一个单线程的线程池,适用于需要保证顺序执行各个任务。
# newFixedThreadPool:创建一个固定大小的线程池
# newCachedThreadPool:用来创建一个可以扩大的线程池(扩大量根据自己的CPU性能来决定)


// Executors 工具类 三大方法
public class Demo01 {
    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();
        }
    }
}

# 七大参数:(官方不建议使用线程池现有的工具类方法,建议自己使用 ThreadPoolExecutor 创建)

	1. corePoolSize(线程池基本大小):当向线程池提交一个任务时,若线程池已创建的线程数小于				   	corePoolSize,即便此时存在空闲线程,也会通过创建一个新线程来执行该任务,直到已创建的线程数大于		或等于corePoolSize时,(除了利用提交新任务来创建和启动线程(按需构造),也可以通过 				prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程。)
	
	2. maximumPoolSize(线程池最大大小):线程池所允许的最大线程个数。当队列满了,且已创建的线程数小于	    maximumPoolSize,则线程池会创建新的线程来执行任务。另外,对于无界队列,可忽略该参数。

	3. keepAliveTime(线程存活保持时间)当线程池中线程数大于核心线程数时,线程的空闲时间如果超过线程存		活时间,那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数。
	
	4. unit:TimeUnit. 设置时间的单位(为keepAliveTime服务的单位)5. workQueue(任务队列):用于传输和保存等待执行任务的阻塞队列。

	6. threadFactory(线程工厂):用于创建新线程。threadFactory创建的线程也是采用new Thread()方式			threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线		程编号)。

	7. handler(线程饱和策略):当线程池和队列都满了,再加入线程会执行此策略。


public class Demo02 {
    public static void main(String[] args) {
        // 自定义线程池
        ExecutorService threadPool = new ThreadPoolExecutor(
                        2,
                        5,
                        3,
                        TimeUnit.SECONDS,
                        new LinkedBlockingQueue<>(3),
                        Executors.defaultThreadFactory(),
                        new ThreadPoolExecutor.AbortPolicy()
        );
        try{
            // # 最大承载线程量:Deque + Max
            for (int i = 1; i <= 9 ; i++) {
                // 使用线程池来创建线程
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + " OK");
                });
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // # 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

# 四大拒绝策略:
	1. new ThreadPoolExecutor.AbortPolicy() : 队列和线程池满了还有线程进入会直接抛出异常
	2. new ThreadPoolExecutor.CallerRunsPolicy() : main线程会去处理
	3. new ThreadPoolExecutor.DiscardPolicy() 队列和线程池满了还有线程进入不抛出异常
	4. new ThreadPoolExecutor.DiscardOldestPolicy() 队列满了,尝试和最早的竞争,失败不抛出异常

# 线程池调优(池的大小如何设置)(这里的大小为最大线程池大小)
	CPU密集型任务:尽量使用较小的线程池,一般为CPU核心数+1。 因为CPU密集型任务使得CPU使用率很高,若开过				  多的线程数,会造成CPU过度切换。
	IO密集型任务:可以使用稍大的线程池,一般为2*CPU核心数。 IO密集型任务CPU使用率并不高,因此可以让CPU				 在等待IO的时候有其他线程去处理别的任务,充分利用CPU时间。
	混合型任务:可以将任务分成IO密集型和CPU密集型任务,然后分别用不同的线程池去处理。 只要分完之后两个任务 				 的执行时间相差不大,那么就会比串行执行来的高效。

# 获取处理器的数量 : Runtime.getRuntime().availableProcessors();数量等于上述的CPU核心数

四大函数式接口:
# 函数式接口的作用: 方便lambda表达式的使用,简化代码编程


# 函数型接口:Function 
# Function函数式接口:传入一个参数T,返回R
# 只要是 函数型接口 可以用 lambda 表达式简化

public class Demo01 {
    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 = str -> {
            return str;
        };
        System.out.println(function.apply("33"));

    }
}

# 断定型接口: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 = str -> {
            return str.isEmpty();
        };
        System.out.println(predicate.test(""));
    }
}


# 消费型接口:Consumer
# 传入一个参数,没有返回值

public class Demo03 {
    public static void main(String[] args) {
//        Consumer<String> consumer = new Consumer<String>() {
//            @Override
//            public void accept(String str) {
//                System.out.println(str);
//            }
//        };
        Consumer<String> consumer = (str) ->{
            System.out.println(str);
        };
        consumer.accept("消费型接口!");
    }
}

# 供给型接口:Supplier
# 不传入参数,返回一个值

public class Demo04 {
    public static void main(String[] args) {
//        Supplier<Integer> supplier = new Supplier<Integer>() {
//            @Override
//            public Integer get() {
//                return 1;
//            }
//        };
        Supplier<Integer> supplier = () -> { return 1; };
        System.out.println(supplier.get());
    }
}

Stream流式计算:

// 例如下:
// 1. ID 必须是偶数
// 2. 年龄大于22岁
// 3. 用户名转为大写
// 4. 用户名字母倒序
// 5. 只输出一个用户!
public class Test01 {
    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(5, "e", 25);
        User u6 = new User(6, "f", 26);
        // # 集合就是存储
        List<User> list = Arrays.asList(u1, u2, u3, u4, u5, u6);
        // 计算交给 Stream 流
        // # lambda 表达式、链式编程、函数式接口、Stream流式计算
        list.stream()
                .filter(u -> u.getId()%2 == 0)
                .filter(u -> u.getAge() > 22)
                .map(u -> u.getName().toUpperCase())
                .sorted((uu1, uu2) -> {return uu2.compareTo(uu1);})
                .limit(1)
                .forEach(System.out :: println);
    }
}
@Data
@AllArgsConstructor // 全参构造
@NoArgsConstructor  // 无参构造
class User{
    private int id;
    private String name;
    private int age;
}

ForkJoin:
# 分支合并
# ForkJoin 在 JDK1.7,并行执行任务!提高效率,必须在大数据量情况下使用!

	1. ForkJoinPool 的每个工作线程都维护着一个工作队列(WorkQueue),这是一个双端队列(Deque),里面存放的对象是任务(ForkJoinTask)。
	2. 每个工作线程在运行中产生新的任务(通常是因为调用了 fork())时,会放入工作队列的队首,并且工作线程在处理自己的工作队列时,使用的是 FIFO 方式,也就是说每次从队首取出任务来执行。
	3. 每个工作线程在处理自己的工作队列同时,会尝试窃取一个任务(或是来自于刚刚提交到 pool 的任务,或是来自于其他工作线程的工作队列),窃取的任务位于其他线程的工作队列的队尾,也就是说工作线程在窃取其他工作线程的任务时,使用的是 LIFO 方式。
	4. 在遇到 join() 时,如果需要 join 的任务尚未完成,则会先处理其他任务,并等待其完成。
	5. 在既没有自己的任务,也没有可以窃取的任务时,进入休眠。

# 自动装箱使得基本类型和装箱基本类型之间的差别变得模糊起来,但是并没有完全消除。
# 要优先使用基本类型(int,long)而不是装箱基本类型(Integer,Long),要当心无意识的自动装箱。
# 避免在循环下使用装箱基本类型,影响效率

# 拆箱:就是对象类型转基本数据类型时,会隐式的调用valueOf()方法去比较值的大小. 
# 装箱:就是在进行类似Integer a=100时类似的操作时,就如同执行了Integer a=new Integer(100)的构造方法去创建a这个对象.

// 求和:1 至 10_0000_0000
public class ForkJoinDemo1 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        test1();// 3112
        test2();
        test3();// 284
    }

    public static void test1(){
        long startTime = System.currentTimeMillis();
        Calculator calculator = new Calculator();
        System.out.println(calculator.test1(1L, 10_0000_0000L));
        long endTime = System.currentTimeMillis();
        System.out.println("所用时间为:" + (endTime-startTime));
    }

    public static void test2() throws ExecutionException, InterruptedException {
        long startTime = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> forkJoinTask = new ForkJoinDemo(1L, 10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(forkJoinTask);
        System.out.println(submit.get());
        long endTime = System.currentTimeMillis();
        System.out.println("所用时间为:" + (endTime-startTime));
    }

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

}

class Calculator {
    // 一般方法求和
    public long test1(Long start, Long end) {
        long total = 0;
        for (Long i = start; i <=  end; i++) {
            total += i;
        }
        return total;
    }
}

class ForkJoinDemo extends RecursiveTask<Long> {
    private long start;
    private long end;
    // 临界值
    private long temp = 10000;
    public ForkJoinDemo(long start, long end){
        this.start = start;
        this.end = end;
    }
    @Override
    protected Long compute() {
        if((end - start) < temp){
            long sum = 0;
            for (long i = start; i <= end ; i++) {
                sum += i;
            }
            return sum;
        }else {
            long middle = (start + end) / 2;// 中间值
            ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
            task1.fork();
            ForkJoinDemo task2 = new ForkJoinDemo(middle + 1, end);
            task2.fork();
            return task1.join() + task2.join();
        }
    }
}

异步回调:
# 在正常的业务中使用同步线程,如果服务器每处理一个请求,就创建一个线程的话,会对服务器的资源造成浪费。因为这些线程可能会浪费时间在等待网络传输,等待数据库连接等其他事情上,真正处理业务逻辑的时间很短很短,但是其他线程在线程池满了之后又会阻塞,等待前面的线程处理完成。而且,会出现一个奇怪的现象,客户端的请求被阻塞,但是cpu的资源使用却很低,大部分线程都浪费在处理其他事情上了。所以,这就导致服务器并发量不高。而异步,则可以解决这个问题。我们可以把需要用到cpu的业务处理使用异步来实现,这样其他请求就不会被阻塞,而且cpu会保持比较高的使用率。综上,可以使用回调来实现异步的方法。

# 异步回调的实现依赖于多线程或者多进程。

// 异步调用
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("11111");
//        // 获取阻塞执行结果
//        completableFuture.get();

        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 301;
        }).get());

    }
}

JMM:
# Java Memory Model(Java 内存模型)
# 关于 JMM 的一些同步约定:
	1. 线程解锁前,必须把共享变量立刻刷回主存
	2. 线程加锁前,必须读取主存中的最新值到工作内存中!
	3. 加锁和解锁是同一把锁

# 线程对【共享变量】的所有操作都必须在【工作内存】中进行,不能直接读写【主内存】中的变量。
# 【工作内存】所更新的【变量】并不会立即同步到主内存。
	
# 如下代码体现了线程变量没有同步情况!

public class JMMDemo {
    private static int num = 0;
    public static void main(String[] args) {
        new Thread(() -> {
            while (num == 0){
                System.out.println(Thread.currentThread().getName() + " : " + num);
            }
        }, "Demo").start();

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

    }
}

Volatile:
# 什么是 Volatile
# Volatile 是Java虚拟机提供轻量级的同步机制
	1. 保证可见性
	2. 不保证原子性
	3. 禁止指令重排

# 1. 保证可见性
public class JMMDemo {
    // 加 volatile 可以保证可见性
    private volatile static int num = 0;
    public static void main(String[] args) {
        new Thread(() -> {
            while (num == 0){

            }
        }, "Demo").start();

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

    }
}


# 2. 不保证原子性
// #  一般回答:加 Lock 和 synchronized 可以解决
public class VDemo02 {
    private volatile static int num = 0;
    public static void add(){
    	// num++ 不是原子性操作
        num++;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 20 ; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000 ; j++) {
                    add();
                }
            }, String.valueOf(i)).start();
        }
        while (Thread.activeCount() > 2) {// main gc
            // 礼让,让出CPU调度时间
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " " + num);
    }
}

// # 使用原子类包装类实现  
// # 这些类的底层都直接和操作系统挂钩(Native方法),在内存中修改值,Unsafe类是一个很特殊的存在!
public class VDemo02 {
    // 原子类的 Integer
    private static AtomicInteger num = new AtomicInteger();
    public static void add(){
        // AtomicInteger + 1 方法,底层CAS实现
        num.getAndIncrement();
    }
    public static void main(String[] args) {
        for (int i = 0; i < 20 ; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000 ; j++) {
                    add();
                }
            }, String.valueOf(i)).start();
        }
        while (Thread.activeCount() > 2) {// main gc
            // 礼让,让出CPU调度时间
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + " " + num);
    }
}


# 3. 禁止指令重排
# 什么是指令重排?
#  指令重排序是指: JVM在编译Java代码的时候,或者CPU在执行JVM字节码的时候,对现有的【指令顺序】进行【重新排序】。

# 指令重排的目的:
# 为了在不改变程序执行结果的前提下,优化程序的运行效率。(指的是【单线程】下的程序执行结果。)

# 指令重排可能导致的现象:(多线程情况下)
# 两条不同线程分别操作对方的元素值,可能因为代码执行的顺序不同而得出不同的结果!

# 由于内存屏障,可以保证避免指令重排的现象产生
# 内存屏障,内存屏障(Memory Barrier)是一种CPU指令,作用:
	1. 保证特定的操作的执行顺序!
	2. 可以保证某些变量的内存可见性(利用这些特性volatile实现了可见性)

单例模式:
# 饿汉式: 可能存在浪费空间

// 饿汉式单例
public class Hungry {
    // 浪费空间
    private byte[] data1 = new byte[1024*1024];
    private byte[] data2 = new byte[1024*1024];
    private byte[] data3 = new byte[1024*1024];
    private Hungry(){

    }
    private final static Hungry HUNGRY = new Hungry();
    public static Hungry getInstance(){
        return HUNGRY;
    }
}

# 懒汉式:

// # 一般懒汉式:多线程下不安全
public class LazyMan {
    private LazyMan(){
        System.out.println(Thread.currentThread().getName() + " OK");
    }
    private static LazyMan lazyMan;
    public static LazyMan getInstance(){
        if(lazyMan == null){
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }
    // 多线程并发
    public static void main(String[] args) {
        for (int i = 0; i < 10 ; i++) {
            new Thread(() -> {
                LazyMan.getInstance();
            }, String.valueOf(i)).start();
        }
    }
}


# 创建实例的方法中加锁,锁 类对象:多线程是可以,但是反射可以强制破坏
# 单例模式之DCL懒汉式解析(双重检验锁)
public static LazyMan getInstance(){
        if(lazyMan == null){
            synchronized (LazyMan.class){
                if(lazyMan == null){
                    lazyMan = new LazyMan();
                }
            }
        }
        return lazyMan;
    }
  
# 双重检验锁不安全的原因: 
# 创建对象是一个非原子性操作, 需要加 volatile 关键字防止指令重排
	private volatile static LazyMan lazyMan;
    public static LazyMan getInstance(){
        if(lazyMan == null){
            synchronized (LazyMan.class){
                if(lazyMan == null){
                    lazyMan = new LazyMan();// 不是原子性操作
                    /**
                     * 创建对象后进行的操作:
                     * 1. 分配内存空间
                     * 2. 执行构造方法, 初始化对象
                     * 3. 把这个对象指向这个空间
                     * 在这里会发生指令重排现象, 所以需要加 volatile 关键字
                     */
                }
            }
        }
        return lazyMan;
    }
    
    
# 下面都是通过反射破坏单例模式:
public class LazyMan {
    private LazyMan(){
        synchronized (LazyMan.class){
            if(lazyMan != null){
               throw new RuntimeException("不要试图通过反射破坏单例");
            }
        }
        System.out.println(Thread.currentThread().getName() + " OK");
    }
    private volatile static LazyMan lazyMan;
    public static LazyMan getInstance(){
        if(lazyMan == null){
            synchronized (LazyMan.class){
                if(lazyMan == null){
                    lazyMan = new LazyMan();
                }
            }
        }
        return lazyMan;
    }
    // 多线程并发
    public static void main(String[] args) throws Exception{
        LazyMan instance = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyMan instance2 = declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(instance2);

    }
}

# 上述如果都是通过反射得到实例,还是会报错,解决方案如下:
# 错误示例:
public static void main(String[] args) throws Exception{
        //LazyMan instance = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyMan instance = declaredConstructor.newInstance();
        LazyMan instance2 = declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(instance2);

    }
# 修改方法:通过标志位(信号灯法解决)
# 但是还是存在问题,通过反射可以改变标志位的值
private static boolean bzw = false;
    private LazyMan(){
        synchronized (LazyMan.class){
            if(bzw == false){
                bzw = true;
            }else {
                throw new RuntimeException("不要试图通过反射破坏单例");
            }
        }
        System.out.println(Thread.currentThread().getName() + " OK");
    }

# 进阶修改方法:通过查看反射获取实例方法 newInstance() 可以看出, 如果是枚举类那么就不能通过反射获取!
# 示例:
// 枚举本身是一个 CLASS 类
// 枚举类 底层有一个 (String, int) 的构造方法, 通过反编译工具可以看出
public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}
class Test{
    public static void main(String[] args) throws Exception {
        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 class Holder {
    private Holder(){

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

深入理解CAS:
# CAS:比较当前工作内存中的值和主内存中的值, 如果这个值是期望的, 那么则执行操作!如果不是就一直循环!
# 优点:保证原子性
# 缺点:
	1. 循环会耗时(底层有自旋锁)
	2. 一次性只能保证一个共享变量的原子性
	3. 存在ABA问题

public class CASDemo {
    // # CAS compareAndSet:比较并交换!
    // CAS 是CPU的并发原语
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
        // Java无法操作内存, 但是可以通过调用C++操作内存(native方法)
        // 可以通过 Unsafe 类操作
        // getAndIncrement() 底层有自旋锁
        atomicInteger.getAndIncrement();

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

原子引用:
# 原子引用(AtomicReference):解决ABA问题

AtomicReference的作用是对”对象”进行原子操作。 
提供了一种读和写都是原子性的对象引用变量。原子意味着多个线程试图改变同一个AtomicReference(例如比较和交换操作)将不会使得AtomicReference处于不一致的状态。

AtomicReference和AtomicInteger非常类似,不同之处就在于AtomicInteger是对整数的封装,底层采用的是compareAndSwapInt实现CAS,比较的是数值是否相等,而AtomicReference则对应普通的对象引用,底层使用的是compareAndSwapObject实现CAS,比较的是两个对象的地址是否相等。也就是它可以保证你在修改对象引用时的线程安全性。

# ABA问题:中间有做过修改操作但是没有被记录
public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        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());
    }

# 解决:
# 下面示例需要注意:因为泛型定义为Integer, 所以值在-128至127之间,超过就会重新new新对象,所操作的就不会是同一个包装类对象
// 通过加版本号解决:乐观锁思想
public class CASDemo02 {
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);
    public static void main(String[] args) {
        new Thread(() -> {
            // 获得版本号
            int stamp = atomicStampedReference.getStamp();
            System.out.println("a1 => " + stamp);
            try {
                TimeUnit.SECONDS.sleep(1);
            }catch (Exception e){
                e.printStackTrace();
            }
            System.out.println("a2 => " + atomicStampedReference.compareAndSet(1, 2,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("a2 => " + atomicStampedReference.getStamp());

            System.out.println("a3 => " + 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 (Exception e){
                e.printStackTrace();
            }
            System.out.println("b2 => " + atomicStampedReference.compareAndSet(1, 3,
                    stamp, stamp + 1));
            System.out.println("b2 => " + atomicStampedReference.getStamp());
        }, "b").start();
    }
}

可重入锁:
# 公平锁与非公平锁:
# 公平锁:
	多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁。
	优点:所有的线程都能得到资源,不会饿死在队列中。
	缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大。
# 非公平锁:
	多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。
	优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数		 量。
	缺点:这样可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死。

# 可重入锁:可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。(递归获取锁而不会死锁)
# synchronized、ReentrantLock 都属于可重入锁

// # synchronized
public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sms();
        }, "A").start();
        new Thread(() -> {
            phone.sms();
        }, "B").start();
    }
}
class Phone{
    public synchronized void sms(){
        System.out.println(Thread.currentThread().getName() + " sms");
        call();
    }
    public synchronized void call(){
        System.out.println(Thread.currentThread().getName() + " call");
    }
}

// # ReentrantLock:注意:上了几把锁就要解几把锁,否则会死锁!
public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sms();
        }, "A").start();
        new Thread(() -> {
            phone.sms();
        }, "B").start();
    }
}
class Phone{
    Lock lock = new ReentrantLock();
    public void sms(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " sms");
            call();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void call(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " call");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

自旋锁:
# Spinlock:自旋锁
是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

缺点:获取锁的线程一直处于活跃状态,但是并没有执行任何有效的任务,使用这种锁会造成busy-waiting。

// # 自旋锁:通过CAS实现
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);
    }
}

public class TestSpinLock {
    public static void main(String[] args) throws InterruptedException {
        // 底层使用自旋锁, 通过CAS实现
        SpinlockDemo lock = new SpinlockDemo();
        new Thread(() -> {
            lock.myLock();
            try{
                TimeUnit.SECONDS.sleep(5);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.myUnlock();
            }
        }, "T1").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(() -> {
            lock.myLock();
            try{

            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.myUnlock();
            }
        }, "T2").start();
    }
}

死锁排查:
# 死锁示例:
public class DeadLockDemo {
    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";
        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 (Exception e) {
                e.printStackTrace();
            }
            synchronized (lockB) {
                System.out.println(Thread.currentThread().getName() + " lock: " + lockB + " => get " + lockA);
            }
        }
    }
}

# 死锁排查:
# 命令在idea中断输入(Terminal)
1. 使用 jps 定位进程号: jps -l
2. jstack 进程号(查看进程的堆栈信息)

# 排查问题:
1. 看日志
2. 查看堆栈信息
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值