• 2022.09.19 学习笔记


    lambda表达式

    sort

    public class ComparatorTest {
        public static void main(String[] args) {
            Apple a1 = new Apple("red",120.33);
            Apple a2 = new Apple("red",129.33);
            Apple a3 = new Apple("red",119.33);
            List<Apple> appleList = Arrays.asList(a1,a2,a3);
            test1(appleList);
        }
    
        /**
         * 方式1 创建类并实现Comparator
         */
        public static void test1(List<Apple> appleList){
            appleList.sort(new AppleComparator());
            System.out.println(JSON.toJSONString(appleList));
        }
    
        /**
         * 方式2 匿名内部类
         */
        public static void test2(List<Apple> appleList){
            appleList.sort(new Comparator<Apple>() {
                @Override
                public int compare(Apple o1, Apple o2) {
                    return o1.getWeight().compareTo(o2.getWeight());
                }
            });
        }
    
        /**
         * 方式3 使用Lambda表达式
         */
        public static void test3(List<Apple> appleList){
            appleList.sort(((o1, o2) -> o1.getWeight().compareTo(o2.getWeight())));
            System.out.println(JSON.toJSONString(appleList));
        }
    
        /**
         * 方式4 方法3改进
         */
        public static void test4(List<Apple> appleList){
            //comparing中的参数是Function的实现
            //其中返回的参数是用于排序的key
            appleList.sort(Comparator.comparing((a -> a.getWeight())));
            System.out.println(JSON.toJSONString(appleList));
        }
    
        /**
         * 方式5 使用方法引用
         */
        public static void test5(List<Apple> appleList){
            appleList.sort(Comparator.comparing((Apple::getWeight)));
            System.out.println(JSON.toJSONString(appleList));
        }
    }
    

    自定义lambda表达式实现接口

    定义一个只有一个抽象方法的接口,其实际上是一个函数式接口,可在接口前面加上@FunctionalInterface注解进行验证。

    public class LambdaTestB {
        public static void main(String[] args) {
            Fu2 fu  = a->a*5;
            System.out.println(fu.add(100));
        }
    }
    
    @FunctionalInterface
    interface Fu2{
        int add(int a);
    }
    

    注意其lambda表达式的格式应该和add()一致的函数类型

    内置的函数式接口

    Consumer:消费型接口(void accept(T t))。有参数,无返回值 (上文forEach的参数类型就是Consumer)

    Supplier:供给型接口(T get())。只有返回值,没有入参

    Function:函数型接口(R apply(T t))。一个输入参数,一个输出参数,两种类型不可不同、可以一致

    Predicate:断言型接口(boolean test(T t))。输入一个参数,输出一个boolean类型得返回值

    线程池

    //这个属性是用来存放 当前运行的worker数量以及线程池状态的
    //int是32位的,这里把int的高3位拿来充当线程池状态的标志位,后29位拿来充当当前运行worker的数量
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    //存放任务的阻塞队列
    private final BlockingQueue<Runnable> workQueue;
    //worker的集合,用set来存放
    private final HashSet<Worker> workers = new HashSet<Worker>();
    //历史达到的worker数最大值
    private int largestPoolSize;
    //当队列满了并且worker的数量达到maxSize的时候,执行具体的拒绝策略
    private volatile RejectedExecutionHandler handler;
    //超出coreSize的worker的生存时间
    private volatile long keepAliveTime;
    //常驻worker的数量
    private volatile int corePoolSize;
    //最大worker的数量,一般当workQueue满了才会用到这个参数
    private volatile int maximumPoolSize;
    
    处理流程

    1.查看核心线程池是否已满,不满就创建一条线程执行任务,否则执行第二步。

    2.查看任务队列是否已满(Blocking queue),不满就将任务存储在任务队列中,否则执行第三步。

    3.查看线程池是否已满,即就是是否达到最大线程池数,不满就创建一条线程执行任务,否则就按照策略处理无法执行的任务。

    线程状态切换

    运行态->等待态(WAITING)

    • Object.join():把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行
      比如线程B中调用了线程A的join()方法,线程A执行完成后,线程B才会开始执行

    • Object.wait():线程进入等待状态,进入等待状态的线程需要依赖其他线程的通知(notify/notifyAll)才能够返回到运行状态

    • LockSupport.park():park函数是将当前调用Thread阻塞,而unpark函数则是将指定线程Thread唤醒。与Object类的wait/notify机制相比,

      1. park/unpark有两个优点:

        ① 以thread为操作对象更符合阻塞线程的直观定义

        ② 操作更精准,可以准确地唤醒某一个线程(notify随机唤醒一个线程,notifyAll唤醒所有等待的线程),增加了灵活性。

      2. park是等待一个许可,unpark是为某线程提供一个许可。
        如果某线程A调用park,那么除非另外一个线程调用unpark(A)给A一个许可,否则线程A将阻塞在park操作上

      3. unpark操作可以在park操作之前

      4. “许可”是不能叠加的,“许可”是一次性的:线程B连续调用了三次unpark函数,当线程A调用park函数就使用掉这个“许可”,如果线程A再次调用park,则进入等待状态

    等待态->运行态

    • Object.notify()
    • Object.notifyAll()
    • LockSupport.unpark(Thread)

    运行态->超时等待(TIMED_WAITING)

    超时等待状态在等待状态的基础上增加了超时限制,当达到超时时间后会返回运行状态

    • Thread.sleep(long) 这里单位是millisconds,为了便于控制sleep的时间,可以使用

    • TimeUnit.SECONDS.sleep(long):TimeUnit是java.util.concurrent包下面的一个类,表示给定单元粒度的时间段,作用包括:

      1. 时间颗粒度转换

        //结果:24  
        System.out.println( TimeUnit.DAYS.toHours( 1 ) );  
        //结果:3600  
        System.out.println( TimeUnit.HOURS.toSeconds( 1 ));  
        //结果是:72  
        System.out.println( TimeUnit.HOURS.convert( 3 , TimeUnit.DAYS ) );           
        
      2. 延时

        TimeUnit.SECONDS.sleep(long);
        
    • Object.wait(long)

    • Thread.join(long)

    • LockSupport.parkNanos(long) LockSupport.parkUntil(long)

      超时等待(TIMED_WAITING)->运行态

    • Object.notify()

    • Object.notifyAll()

    • LockSupport.unpark(Thread)

    运行态->阻塞态

    • 等待进入synchronized方法
    • 等待进入synchronized块

    阻塞态->运行态

    • 获取到锁

    安全地终止线程

    • 使用interrupt() 中断操作来终止线程
    • 通过标示位判断来终止

    线程间通信

    volatile

    用于修饰成员变量,告知程序任何对该变量的访问均需要从共享内存中获取,并且对于它的改变必须同步刷新回共享内存,能保证所有线程对变量访问的可见性。

    过多地使用会降低程序执行的效率

    synchronized

    用于修饰方法或者以同步块的形式来进行使用。确保多个线程在同一时刻,只能有一个线程处于方法或者同步块中,保证了线程对变量访问的可见性和排他性。

    任意线程对Object的访问,首先要获取Object的监视器。如果获取失败,线程进入同步队列,状态变为Blocked,当访问Object的前驱(获得了锁的线程)释放了锁,那么该释放操作会唤醒阻塞在同步队列中的线程,使其重新尝试对监视器的获取。

    等待/通知机制

    • 使用wait()、notify()、notifyAll()的时候需要先对调用对象加锁
    • 调用wait()方法后线程进入等待状态,并且释放锁
    • 其他线程调用notify()、notifyAll()之后,还需要等调用这两个方法的线程释放锁,等待线程获得了调用对象的锁之后,才会从wait()返回
  • 相关阅读:
    MySQL 基础知识(八)之用户权限管理
    上海亚商投顾:两市成交创23个月新低 医疗器械板块强势
    (muduo) 基础demo
    国学---佛系算吉凶~
    OpenTiny 前端组件库正式开源啦!面向未来,为开发者而生
    智慧工地解决方案,智慧工地平台源码
    @AutoConfigureAfter注解
    windows安装redis
    小米汽车SU7全色系H5自适应展示源码
    【c++ 封装、继承、多态】
  • 原文地址:https://blog.csdn.net/merryghoet/article/details/126940292