JUC学习笔记(下)

继续分享总结的JUC学习笔记在这里插入图片描述

wait和sleep的区别

  1. sleep是Thread的静态方法,wait是Object方法
  2. sleep不会释放锁,它也不需要占用锁,wait会释放锁但调用它的前提是当前线程占有锁
  3. wait必须在同步代码块中

Lock锁

public class LockTest {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) ticket.sale();
        }, "AA").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) ticket.sale();
        }, "BB").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) ticket.sale();
        }, "CC").start();
    }
}

class Ticket {
    private int number = 40;

    //定义可重入锁
    Lock lock = new ReentrantLock();

    public void sale() {
        lock.lock();
        try {
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + number--);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Lock和Synchronized的区别

  1. Synchronized内置的Java关键字,Lock是一个Java类
  2. Synchronized无法判断获取锁的状态 ,Lock可以判断是否获取到了锁
  3. Synchronized会自动释放锁,Lock锁必须要手动释放,如果不释放锁,死锁
  4. Synchronized可重入锁,不可以中断的,非公平,Lock,可重入锁,可以判断锁,非公平和公平可以自己设置
  5. Synchronized适合锁少量的代码同步问题,Lock适合锁大量的同步代码

生产者消费者(虚假唤醒)

老版synchronized

public class WakeThread {
    public static void main(String[] args) {
        Date date = new Date();
        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                try {
                    date.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();
        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                try {
                    date.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"BB").start();
        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                try {
                    date.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"CC").start();
        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                try {
                    date.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"DD").start();
    }
}

class Date {
    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();
    }
}

这种情况出现虚假唤醒状态,防止虚假唤醒

class Date {
    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

在这里插入图片描述

public class LockPC {
    public static void main(String[] args) {
        Date1 date1 = new Date1();
        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                try {
                    date1.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();
        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                try {
                    date1.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"BB").start();
        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                try {
                    date1.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"CC").start();
        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                try {
                    date1.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"DD").start();
    }
}

class Date1 {

    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 (InterruptedException 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 (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Condition实现精准通知唤醒

class shareResource {
    //设置标志信号 1代表AA线程 2代表BB线程 3代表CC线程
    int flag = 1;

    //创建可重入锁
    private final ReentrantLock lock = new ReentrantLock();

    //创建AA,BB,CC的条件
    Condition a1 = lock.newCondition();
    Condition b2 = lock.newCondition();
    Condition c3 = lock.newCondition();

    public void print5(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag != 1) {
                a1.await();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + ": " + i + " 轮数: " + loop);
            }
            flag = 2;
            b2.signal();
        } finally {
            lock.unlock();
        }
    }

    public void print10(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag != 2) {
                b2.await();
            }
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + ": " + i + " 轮数: " + loop);
            }
            flag = 3;
            c3.signal();
        } finally {
            lock.unlock();
        }
    }

    public void print15(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag != 3) {
                c3.await();
            }
            for (int i = 0; i < 15; i++) {
                System.out.println(Thread.currentThread().getName() + ": " + i + " 轮数: " + loop);
            }
            flag = 1;
            a1.signal();
        } finally {
            lock.unlock();
        }
    }
}

public class ThreadDemo3 {
    public static void main(String[] args) {
        shareResource shareResource = new shareResource();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 10; i++) {
                    try {
                        shareResource.print5(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "AA").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 10; i++) {
                    try {
                        shareResource.print10(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "BB").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 10; i++) {
                    try {
                        shareResource.print15(i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "CC").start();
    }
}

线程八锁理解锁

小结

Synchronized修饰非静态方法,实际上是对调用该方法的对象加锁,俗称“对象锁”

Synchronized修饰静态方法,实际上是对该类对象加锁,俗称“类锁”。

/**
 * 1.标准情况下,两个线程先打印发短信还是打电话? 1.发短信 2.打电话 ---> 这里不要有因为发短信是先调用的这一个误区,是因为synchronized
 * 锁的机制只有当前释放了锁下个线程才会从就绪状态变为启动状态
 * 2.发短信延迟4s,先打印发短信还是打电话? 1.发短信 2.打电话
 */

public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();

        new Thread(() -> {
            phone.sendSms();
        }, "AA").start();

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

        new Thread(() -> {
            phone.call();
        }, "BB").start();
    }
}

class Phone {

    //synchronized锁的对象是方法的调用者
    //谁先拿到谁执行
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }
}
/**
 * 3.添加普通方法,普通方法和同步方法不受锁的影响 1.说hello 2.发短信
 * 4.两个对象分别执行两个同步方法,因为synchronized锁的是调用者 1.因为有延时的存在所以先是打电话 2. 发短信
 */
public class Test2 {
    public static void main(String[] args) {
        Phone1 phone1 = new Phone1();
        Phone1 phone2 = new Phone1();

        new Thread(() -> {
            phone1.sendSms();
        }, "AA").start();

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

        new Thread(() -> {
            phone2.call();
        }, "BB").start();
    }
}

class Phone1 {

    //synchronized锁的对象是方法的调用者
//谁先拿到谁执行
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }

    public void hello() {
        System.out.println("说Hello");
    }
}
/**
 * 5.增加两个静态方法,只有一个对象 1.发短信 2.打电话
 * 6.两个对象调用两个静态同步方法 1.发短信 2.打电话 和之前的情况不同了原因就在于synchronized锁住的是Class
 */
public class Test3 {
    public static void main(String[] args) {
        Phone3 phone1 = new Phone3();
        Phone3 phone3 = new Phone3();

        new Thread(() -> {
            phone1.sendSms();
        }, "AA").start();

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

        new Thread(() -> {
            phone3.call();
        }, "BB").start();
    }
}

//Phone2唯一的一个Class对象
class Phone2 {
    //synchronized锁的对象是方法的调用者
    //static 静态方法,随着类的加载而加载,锁的是Class
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call() {
        System.out.println("打电话");
    }

    public void hello() {
        System.out.println("说Hello");
    }
}
/**
 * 7.同步方法和静态同步方法,一个对象,synchronized如果是静态方法那么锁住的就是.Class模板如果是非静态模板那么锁住的是调用者
 * 8.同步方法和静态同步方法,两个对象
 */
public class Test4 {
    public static void main(String[] args) {
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();

        new Thread(() -> {
            phone1.sendSms();
        }, "AA").start();

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

        new Thread(() -> {
            phone2.call();
        }, "BB").start();
    }
}

//Phone2唯一的一个Class对象
class Phone3 {
    //synchronized锁的对象是方法的调用者
    //static 静态方法,随着类的加载而加载,锁的是Class
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    //锁的调用者,普通的同步方法,
    public synchronized void call() {
        System.out.println("打电话");
    }
}

JUC辅助类

CountDownLatch

加法计数器,允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        /**
         * 不加入CountDownLatch会出现其他线程还没结束但是主线程已经结束
         */
        CountDownLatch count = new CountDownLatch(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "号学生离开教室");
                //计数器 -1
                count.countDown();
            }, String.valueOf(i)).start();
        }
        count.await();
        System.out.println(Thread.currentThread().getName() + "班长锁门");
    }
}

count.countDown() 数量-1

count.await() 等待计数器归零,然后向下执行

CyclicBarrierDemo

减法计数器

public class CyclicBarrierDemo {
    public static final int NUMBER = 7;

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {
            System.out.println("集齐七颗龙珠就可以召唤神龙");
        });

        for (int i = 1; i <= 7; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "颗龙珠收集到了");
                try {
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }
}

SemaphoreDemo

public class SemaphoreDemo {    public static void main(String[] args) {        //定义三个车位        Semaphore semaphore = new Semaphore(3);        //模拟六辆车        for (int i = 1; i <= 6; i++) {            new Thread(() -> {                try {                    //获得凭证                    semaphore.acquire();                    System.out.println(Thread.currentThread().getName() + "找到了车位");                    //模拟停车时间                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));                } catch (Exception e) {                    e.printStackTrace();                } finally {                    //释放凭证                    semaphore.release();                    System.out.println(Thread.currentThread().getName() + "离开车位");                }            }, String.valueOf(i)).start();        }    }}

读写锁

问题引出:我们希望涉及读操作的时候线程1开始写,写完之后其它线程才开始操作

public class ReadWriteLockDemo {    public static void main(String[] args) {        MyCache cache = new MyCache();        for (int i = 0; i < 5; i++) {            final int temp = i;            new Thread(() -> {                cache.put(temp + "", temp + "");            }, String.valueOf(i)).start();        }        for (int i = 0; i < 5; i++) {            final int temp = i;            new Thread(() -> {                cache.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) {        map.get(key);    }}

在这里插入图片描述

public class ReadWriteLockDemo {    public static void main(String[] args) {        MyCache cache = new MyCache();        for (int i = 0; i < 5; i++) {            final int temp = i;            new Thread(() -> {                cache.put(temp + "", temp + "");            }, String.valueOf(i)).start();        }        for (int i = 0; i < 5; i++) {            final int temp = i;            new Thread(() -> {                cache.get(temp + "");            }, String.valueOf(i)).start();        }    }}class MyCache {    //普通锁    private volatile Map<String, Object> map = new HashMap<>();    //读写锁    private ReadWriteLock lock = new ReentrantReadWriteLock();    // 存,我们希望写的时候只有一个线程写    public void put(String key, Object value) {        lock.writeLock().lock();        try {            System.out.println(Thread.currentThread().getName() + "写入" + key);            map.put(key, value);            System.out.println(Thread.currentThread().getName() + "写入完毕");        } catch (Exception e) {            e.printStackTrace();        } finally {            lock.writeLock().unlock();        }    }    // 取,读的时候可以并发读    public void get(String key) {        lock.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 {            lock.readLock().unlock();        }    }}

在这里插入图片描述

独占锁指的就是写锁

共享锁指的就是读锁

读写锁降级

//演示读写锁降级public class ReadDowngradeDemo {    public static void main(String[] args) {        //可重入读写锁        ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();        //获取写锁        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();        //获取读锁        ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();        //写锁上锁        writeLock.lock();        System.out.println("写");        //读锁上锁!这时写锁就降级成了读锁        readLock.lock();        System.out.println("读");        readLock.unlock();        writeLock.unlock();        /*        //读锁上锁        readLock.lock();        System.out.println("读");        //写锁上锁        writeLock.lock();        System.out.println("写");        readLock.unlock();        writeLock.unlock();*/    }}

阻塞队列BlockingQueue

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

在这里插入图片描述

四组API

  1. 抛出异常
  2. 不会抛出异常
  3. 阻塞队列
  4. 超时等待
方式抛出异常有返回值,不抛出异常阻塞等待超时等待
添加addofferputoffer(…)
移除removepolltakepoll(…)
判断队列首elementpeek
public class TestBlock {    //抛出异常    @Test    public 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.element());        System.out.println(blockingQueue.remove());        System.out.println(blockingQueue.remove());        System.out.println(blockingQueue.remove());        //移除元素查看队头元素,java.util.NoSuchElementException        System.out.println(blockingQueue.element());        //移除元素,java.util.NoSuchElementException        //System.out.println(blockingQueue.remove());    }    //有返回值,没有异常    @Test    public 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"));        //添加元素,抛出布尔值        //System.out.println(blockingQueue.offer("d"));        System.out.println(blockingQueue.poll());        System.out.println(blockingQueue.poll());        System.out.println(blockingQueue.poll());        //移除元素查看队头元素,null        System.out.println(blockingQueue.peek());        //移除元素,取不到值返回null        //System.out.println(blockingQueue.poll());    }    //阻塞等待    @Test    public void test3() throws Exception {        ArrayBlockingQueue 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());    }    //超时等待(是有返回值的重载方法)    @Test    public void test4() throws Exception {        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue(3);        blockingQueue.offer("a");        blockingQueue.offer("b");        blockingQueue.offer("c");        System.out.println("放不下了,超时等待");        blockingQueue.offer("d", 3, TimeUnit.SECONDS);        System.out.println(blockingQueue.poll());        System.out.println(blockingQueue.poll());        System.out.println(blockingQueue.poll());        System.out.println("取不出来了,超时等待");        System.out.println(blockingQueue.poll(3,TimeUnit.SECONDS));    }}

同步队列SynchronousQueue

没有容量,进去一个元素,必须等待取出来以后,才能再放进一个元素

SynchronousQueue<String> queue = new SynchronousQueue<>();new Thread(() -> {    try {        System.out.println(Thread.currentThread().getName() + " put 1");        queue.put("1");        System.out.println(Thread.currentThread().getName() + " put 2");        queue.put("2");        System.out.println(Thread.currentThread().getName() + " put 3");        queue.put("3");    } catch (InterruptedException e) {        e.printStackTrace();    }}).start();new Thread(() -> {    try {        TimeUnit.SECONDS.sleep(2);        System.out.println(queue.take());        TimeUnit.SECONDS.sleep(2);        System.out.println(queue.take());        TimeUnit.SECONDS.sleep(2);        System.out.println(queue.take());    } catch (Exception e) {        e.printStackTrace();    }}).start();

线程池

池化技术

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

线程池的好处

  1. 降低资源的消耗
  2. 提高响应速度
  3. 方便管理

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

public static void main(String[] args) {    //单个线程    //ExecutorService threadPool = Executors.newSingleThreadExecutor();    //创建一个固定的线程池    //ExecutorService threadPool = Executors.newFixedThreadPool(5);    //可伸缩的线程池    ExecutorService threadPool = Executors.newCachedThreadPool();    for (int i = 0; i < 10; i++) {        threadPool.execute(() -> {            System.out.println(Thread.currentThread().getName());        });    }    threadPool.shutdown();}

七大参数

源码分析

public static ExecutorService newSingleThreadExecutor() {    return new FinalizableDelegatedExecutorService        (new ThreadPoolExecutor(1, 1,                                0L, TimeUnit.MILLISECONDS,                                new LinkedBlockingQueue<Runnable>()));}public static ExecutorService newFixedThreadPool(int nThreads) {    return new ThreadPoolExecutor(nThreads, nThreads,                                  0L, TimeUnit.MILLISECONDS,                                  new LinkedBlockingQueue<Runnable>());}public static ExecutorService newCachedThreadPool() {    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,                                  60L, TimeUnit.SECONDS,                                  new SynchronousQueue<Runnable>());}

发现它们都来自ThreadPoolExecutor

/**     * 线程池七大参数     * @param corePoolSize 核心线程池大小     * @param maximumPoolSize 最大核心线程池大小     * @param keepAliveTime 超时了没有人调用就会释放     * @param unit 超时单位     * @param workQueue 阻塞队列     * @param threadFactory 线程工厂创建线程一般不用动     * @param handler 拒绝策略     */public ThreadPoolExecutor(int corePoolSize,                          int maximumPoolSize,                          long keepAliveTime,                          TimeUnit unit,                          BlockingQueue<Runnable> workQueue,                          ThreadFactory threadFactory,                          RejectedExecutionHandler handler) {    if (corePoolSize < 0 ||        maximumPoolSize <= 0 ||        maximumPoolSize < corePoolSize ||        keepAliveTime < 0)        throw new IllegalArgumentException();    if (workQueue == null || threadFactory == null || handler == null)        throw new NullPointerException();    this.acc = System.getSecurityManager() == null ?        null :    AccessController.getContext();    this.corePoolSize = corePoolSize;    this.maximumPoolSize = maximumPoolSize;    this.workQueue = workQueue;    this.keepAliveTime = unit.toNanos(keepAliveTime);    this.threadFactory = threadFactory;    this.handler = handler;}

四种拒绝策略

在这里插入图片描述

/** * 拒绝策略 * 默认 ThreadPoolExecutor.AbortPolicy() 抛出异常 * ThreadPoolExecutor.CallerRunsPolicy() 将任务分给调用线程来执行 * ThreadPoolExecutor.DiscardPolicy() 直接丢弃 * ThreadPoolExecutor.DiscardOldestPolicy() 丢弃队列中最老的任务 * * @author ccy * @version 1.0 * @date 2021/12/18 11:30 */public class MyPool {    public static void main(String[] args) {        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(                2,                5,                3,                TimeUnit.SECONDS,                new LinkedBlockingQueue<>(3),                Executors.defaultThreadFactory(),                new ThreadPoolExecutor.DiscardOldestPolicy());        try {            //最大承载:queue + max            for (int i = 0; i < 9; i++) {                threadPoolExecutor.execute(() -> {                    System.out.println(Thread.currentThread().getName());                });            }        } catch (Exception e) {            e.printStackTrace();        } finally {            threadPoolExecutor.shutdown();        }    }}

CPU密集型和IO密集型保证最大线程定义

CPU密集型,最大线程定义Runtime.getRuntime().availableProcessors() 可以保持CPU效率最高

IO密集型,最大线程定义一般为大型IO任务数量的两倍

*四大函数式接口

@FunctionalInterface 函数式接口,这要是函数式接口就可以用lambda表达式简化

在这里插入图片描述

函数型接口

public static void main(String[] args) {    Function<String, String> function = (str) -> {        return str;    };    System.out.println(function.apply("ccyccyccyccy"));}

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

public static void main(String[] args) {    Predicate<String> predicate = (str) -> {        return str.isEmpty();    };    System.out.println(predicate.test("ccy"));}

消费型接口

public static void main(String[] args) {    Consumer<String> Consumer = (str) -> {        System.out.println(str);    };    Consumer.accept("ccy");}

供给型接口

public class Demo01 {    public static void main(String[] args) {        Supplier<String> supplier = () -> {            return "ccy";        };        System.out.println(supplier.get());    }}

Stream流式计算

public class Test {    public static void main(String[] args) {        User u1 = new User(1, "a", 21);        User u2 = new User(1, "a", 22);        User u3 = new User(1, "a", 23);        User u4 = new User(1, "a", 24);        User u5 = new User(1, "a", 25);        List<User> list = Arrays.asList(u1, u2, u3, u4, u5);        //lambda表达式,链式编程,函数式接口,Stream流计算        list.stream().filter(u -> {            return u.getId() % 2 == 0;        }).filter(u -> {            return u.getAge() > 23;        }).map(u -> {            return u.getName().toUpperCase();        }).sorted((uu1, uu2) -> {            return uu2.compareTo(uu1);        }).limit(1).forEach(System.out::println);    }}

ForkJoin

ForkJoin并发执行任务,提高效率,大数据量

在这里插入图片描述

ForkJoin特点:工作窃取

  1. forkjoinPool 通过它来执行
  2. 计算任务 forkjoinPool.execute(ForkJoinTask task)
  3. 计算类要继承 RecursiveTask
class MyTask extends RecursiveTask<Integer> {    private final static Integer VALUE = 10;    private int begin;    private int end;    private int result;    public MyTask(int begin, int end) {        this.begin = begin;        this.end = end;    }    @Override    protected Integer compute() {        if ((end - begin) <= VALUE) {            for (int i = begin; i <= end; i++) {                result += i;            }        } else {            //获取中间值            int middle = (begin + end) / 2;            //拆分左边            MyTask task01 = new MyTask(begin, middle);            //拆分右边            MyTask task02 = new MyTask(middle + 1, end);            task01.fork();            task02.fork();            result = task01.join() + task02.join();        }        return result;    }}public class ForkJoinDemo {    public static void main(String[] args) throws ExecutionException, InterruptedException {        String s = "test";        //创建MyTask对象        MyTask myTask = new MyTask(0, 100);        //创建分支合并池对象        ForkJoinPool forkJoinPool = new ForkJoinPool();        ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(myTask);        //获取最终合并之后结果        Integer result = forkJoinTask.get();        System.out.println(result);        forkJoinPool.shutdown();    }}

还可以使用并行流来大幅度提高速度

异步回调

对将来的某个事件的结果进行建模

/** * 异步回调 * 成功回调 * 失败回调 */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() + "run");        });*/        CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {            System.out.println(Thread.currentThread().getName() + "supplyAsync => Integer");            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());    }}

JMM

请你谈谈对Volatile的理解

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

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

什么是JMM

JMM:Java内存模型,不存在的东西,是一种概念性

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

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

线程,工作内存主内存

在这里插入图片描述

Volatile

保证可见性

public class JMMDemo {    //volatile 保证可见性    private static volatile int num = 0;    public static void main(String[] args) throws Exception {        new Thread(() -> {            while (num == 0) { //为什么会陷入循环?因为线程1对主内存的变化是不知道的            }        }).start();        TimeUnit.SECONDS.sleep(1);        num = 1;        System.out.println("num变为了1但是为什么程序没有停止");    }}

不保证原子性

线程A在执行任务的时候,不能被打扰的,也不能被分割,要么同时成功要么同时失败

//不保证原子性public class Demo02 {    private static volatile int num = 0;    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();        }        //main gc        while (Thread.activeCount() > 2) {            Thread.yield();        }        System.out.println(Thread.currentThread().getName() + " " + num);    }}

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

使用原子类

public class Demo02 {    private static volatile AtomicInteger num = new AtomicInteger();    public static void add() {        //num++;        num.getAndIncrement(); // 这里不是简单的+1操作,CAS    }    public static void main(String[] args) {        for (int i = 1; i <= 20; i++) {            new Thread(() -> {                for (int j = 0; j < 1000; j++) {                    add();                }            }).start();        }        //main gc        while (Thread.activeCount() > 2) {            Thread.yield();        }        System.out.println(Thread.currentThread().getName() + " " + num);    }}

Unsafe类很关键

指令重排

你写的程序,计算机并不会按照你写的那样执行

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

处理器在进行指令重拍的时候,考虑:数据间的依赖性

在这里插入图片描述

volatile可以避免指令重排

内存屏障 CPU指令 作用

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

彻底玩转单例模式

单例模式

深入理解CAS

//compareAndSet() CAS:比较并交换public static void main(String[] args) {    AtomicInteger atomicInteger = new AtomicInteger(2021);    //期望,更新 如果我期望的值达到了那么就更新否则就不更新    //CAS是CPU的并发原语    atomicInteger.compareAndSet(2021, 2022);    System.out.println(atomicInteger);}

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

缺点

  1. 因为底层是自旋锁所以循环会耗时
  2. 一次性只能保证一个共享变量的原子性
  3. ABA问题

乐观锁和悲观锁和CAS

ABA

ABA问题示例

public class CASDemo {    //compareAndSet() CAS:比较并交换    public static void main(String[] args) {        AtomicInteger atomicInteger = new AtomicInteger(2021);        //捣乱线程        atomicInteger.compareAndSet(2021, 2022);        System.out.println(atomicInteger.get());        atomicInteger.compareAndSet(2022, 2021);        System.out.println(atomicInteger.get());        //期望线程        atomicInteger.compareAndSet(2021, 666);        System.out.println(atomicInteger.get());    }}

在这里插入图片描述

原子引用解决ABA问题

带版本号的缘故

public class CASDemo {    //compareAndSet() CAS:比较并交换    public static void main(String[] args) {        //Integer,是包装类注意对象的引用问题        AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1, 1);        new Thread(() -> {            //获得版本号            int stamp = atomicInteger.getStamp();            System.out.println("a=>" + stamp);            try {                TimeUnit.SECONDS.sleep(1);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(atomicInteger.compareAndSet(1, 2,                                                           atomicInteger.getStamp(), atomicInteger.getStamp() + 1));            System.out.println("a=>" + atomicInteger.getStamp());            System.out.println(atomicInteger.compareAndSet(2, 1,                                                           atomicInteger.getStamp(), atomicInteger.getStamp() + 1));            System.out.println("a=>" + atomicInteger.getStamp());        }, "a").start();        new Thread(() -> {            //获得版本号            int stamp = atomicInteger.getStamp();            System.out.println("b=>" + stamp);            try {                TimeUnit.SECONDS.sleep(1);            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(atomicInteger.compareAndSet(1, 3, stamp, stamp + 1));            System.out.println("b=>" + atomicInteger.getStamp());        }, "b").start();        try {            TimeUnit.SECONDS.sleep(3);        } catch (InterruptedException e) {            e.printStackTrace();        }        System.out.println(atomicInteger.getReference());    }}

在这里插入图片描述

公平锁和非公平锁

Lock lock = new ReentrantLock(true);//默认是非公平锁public ReentrantLock() {    sync = new NonfairSync();}public ReentrantLock(boolean fair) {    sync = fair ? new FairSync() : new NonfairSync();}

可重入锁

在这里插入图片描述

在获得外部锁的时候,内部锁也就自动获得了

public class SynDemo {    public static void main(String[] args) {        Phone phone = new Phone();        new Thread(() -> {            phone.sms();        }, "AA").start();        new Thread(() -> {            phone.call();        }, "BB").start();    }}class Phone {    public synchronized void sms() {        System.out.println(Thread.currentThread().getName() + "sms");        try {            TimeUnit.SECONDS.sleep(2);        } catch (InterruptedException e) {            e.printStackTrace();        }        call();    }    public synchronized void call() {        System.out.println(Thread.currentThread().getName() + "call");    }}
AAsmsAAcallBBcall

在这个例子中,BB线程必须要等待AA线程释放锁以后才可以原因就在于可重入锁

自旋锁

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));    return var5;}

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 Demo {
    public static void main(String[] args) {
        SpinLockDemo lockDemo = new SpinLockDemo();
        new Thread(() -> {
            lockDemo.mylock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lockDemo.myunlock();
            }
        }, "AA").start();
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            lockDemo.mylock();
            lockDemo.myunlock();
        }, "BB").start();
    }
}
AA=> mylock
BB=> mylock
AA=> myunlock
BB=> myunlock

死锁

如何解决死锁

  1. jps 查看进程

在这里插入图片描述

  1. jstack 进程号

在这里插入图片描述

  • 14
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

存量美团骑手

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值