• 多线程


    创建线程的三种方式

    1、继承Thread类

    编写一个类MyThread,继承Thread,重写run方法。

    调用:创建一个MyThread对象,调用start方法

    2、实现Runnable接口

    编写一个类MyThread,实现Runnable接口,重写run方法。

    调用:创建一个MyThread对象,开启一个Thread对象,将MyThread分配给它,调用Thread对象的start方法。

    3、Callable接口

    有返回值,可以抛出异常,方法不同 call()

    有缓存(多个线程操作同一个FutureTask,只会执行一次?),返回结果可能需要等待,会阻塞

    线程的状态

    preview

    JUC

    synchronized 和 Lock 的区别

    1、synchronized 是java内置关键字,Lock 是java的一个类

    2、synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁

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

    4、Synchronized 线程1(获得锁,阻塞),线程2(等待,一直等待);Lock锁就不一定会等待下去!

    5、Synchronized 可重入锁,不可以中断的(是指线程2在等待线程1释放锁的过程中不可中断,并不是说synchronized方法不可中断),非公平的;Lock,可重入锁,可以判断锁,非公平或公平

    6、Synchronized 适合锁少量的代码同步问题

    锁是什么,如果判断锁的是谁!

    虚假唤醒

    线程可以在没有被通知中断超时的情况下唤醒 ,即所谓的虚假唤醒

    知识点

    1、Condition 可以精准的通知和唤醒线程(LockSupport实现)

    2、static synchronized 锁的是Class

    3、class锁和对象锁不是同一个锁,互不干涉

    集合

    安全的List

    1、Vector #使用synchronized

    2、Collections.synchronizedList #使用synchronized

    3、CopyOnWriteArrayList #使用ReentrantLock

    底层实现原理是数组

    安全的Set

    1、Collections.synchronizedSet(底层实现原理是HashMap,数组+链表)

    2、CopyOnWriteArraySet(底层实现原理是CopyOnWriteArrayList.addIfAbsent,数组)

    安全的Map

    1、HashTable #使用synchronized

    2、Collections.synchronizedMap #使用synchronized

    3、ConcurrentHashMap

    研究HashMap和ConcurrentHashMap底层实现原理

    常用辅助类

    CountDownLatch

    减法计数器

    coutDown:数量减一

    await:等待计数器归零,然后再向下执行

    只能使用一次

    CyclicBarrier

    加法计数器

    通俗的来讲是一个屏障,可循环使用

    主要方法wait

    Semaphore

    信号量

    acquire获得信号,获得信号量的线程已达到限制,其余线程等待信号的释放

    release释放信号,唤醒等待的线程?如何实现的

    可以用作限流操作

    ReadWriteLock

    其实现类为ReentrantReadWriteLock,有readLock和writeLock

    注意事项:读写锁除了保证了线程安全,还可以保证读写之间的可靠行。

    1、写-写操作,只允许同时只有一个写

    2、读-读操作,没有限制

    3、读-写/写-读操作,只允许同时只有一个进行操作,要么读等写,要么写等读!读写互斥

    4、对于ConcurrentHashMap,如果读写方法没有synchronized,读写之间没有等待!

    队列

    BlockingQueue

    ArrayBlockingQueue可阻塞队列

    FIFO队列

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

    阻塞读取:如果队列空了则必须等待

    img

    img

    4组API:

    异常是指队列满了或者队列为空

    1、抛出异常

    2、不抛出异常

    3、阻塞等待

    4、超时等待

    方式抛出异常不抛异常,有返回值阻塞等待超时等待
    添加 add offer() put offer(,,)
    移除 remove poll() take poll(,)
    获取队首 element peek()    
    /**
     * 抛出异常情况
    */
    private static void test1(){
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
        queue.add("a"); //返回true
        queue.add("b"); //返回true
        queue.add("c"); //返回true
        //队列已满
        //java.lang.IllegalStateException: Queue full
        //queue.add("d");
        System.out.println(queue.remove());
        System.out.println(queue.remove());
        System.out.println(queue.remove());
        //队列已空
        //java.util.NoSuchElementException
        //System.out.println(queue.remove());
    }
    /**
     * 不抛出异常情况,有返回值
    */
    private static void test2(){
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
        System.out.println(queue.offer("a"));
        System.out.println(queue.offer("b"));
        System.out.println(queue.offer("c"));
        //队列已满
        //不抛出异常,返回false
        System.out.println(queue.offer("d"));
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        //队列已空
        //不抛出异常,返回null
        System.out.println(queue.poll());
    }
    /**
     * 等待,阻塞(一直阻塞)
    */
    private static void test3() throws InterruptedException {
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
        //无返回值
        queue.put("a");
        queue.put("b");
        queue.put("c");
        //队列已满
        //一直阻塞
        //queue.put("d");
        System.out.println(queue.take());
        System.out.println(queue.take());
        System.out.println(queue.take());
        //队列已空
        //一直阻塞
        System.out.println(queue.take());
    }
    /**
     * 等待,阻塞(等待超时)
    */
    private static void test4() throws InterruptedException {
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
        System.out.println(queue.offer("a"));
        System.out.println(queue.offer("b"));
        System.out.println(queue.offer("c"));
        //队列已满
        //等待超时退出
        System.out.println(queue.offer("d",2, TimeUnit.SECONDS));
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        //队列已空
        //等待超时退出
        System.out.println(queue.poll(2, TimeUnit.SECONDS));
    }

    SynchronousQueue同步队列

    容量为1

    put/offer进去一个元素,必须等待take/poll取出来之后,才能再往里面放一个元素。

    线程池

    三大方法,七大参数,四种拒绝策略

    好处

    1、降低资源的消耗

    2、提高响应速度

    3、方便管理

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

    三大方法

    package tmp;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    public class TesPool {
        public static void main(String[] args) {
    //        ExecutorService executorService = Executors.newSingleThreadExecutor();
    //        ExecutorService executorService = Executors.newFixedThreadPool(5);
            ExecutorService executorService = Executors.newCachedThreadPool();
            try {
                for (int i = 0; i < 100; i++) {
                    executorService.execute(() -> {
                        try {
                            TimeUnit.MILLISECONDS.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + " ok");
                    });
                }
            }
            finally {
                System.out.println("shutdown...");
                executorService.shutdown();
                System.out.println("shutdown ok");
            }
        }
    }

    七大参数

    源码分析

    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;
        }

    1、线程池最大容量=最大核心线程池大小+阻塞队列大小

    2、核心线程池大小:这些线程创建后一直存在

    3、最大核心线程池大小:当线程量超过阻塞队列大小时,系统会新增线程,最多新增最大核心线程池大小-核心线程池大小。

    4、keepAliveTime:新增线程(最大核心线程池大小-核心线程池大小)的空闲存活时间

    四种拒绝策略

    AbortPolicy // 当线程数量到达线程池最大容量后,之后再有任务则会丢弃,并抛出RejectedExecutionException.
    CallerRunsPolicy // 达到容量后,会将任务退回原处,即谁启动的该任务,谁负责执行
    DiscardPolicy // 直接丢弃任务,不会抛出异常
    DiscardOldestPolicy // 尝试与最早的线程竞争,失败则丢弃任务,不会抛出异常

    小结

    了解:IO密集型和CPU密集型

    最大核心线程池大小该如何定义

    1、CPU密集型:程序CPU占用较高,比较少的IO操作。几核就设置为几,可以保证CPU的效率最高 //Runtime.getRuntime().availableProcessors();

    2、IO密集型:IO操作频繁,CPU占用较低。多少个IO任务,设置为其1.5倍。

    四大函数式接口

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

    @FunctionalInterface
    public interface Runnable {
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see     java.lang.Thread#run()
         */
        public abstract void run();
    }

    Function

    @FunctionalInterface
    public interface Function<T, R> {
        /**
         * Applies this function to the given argument.
         *
         * @param t the function argument
         * @return the function result
         */
        R apply(T t);
        //...
    }

    函数型接口:输入一个参数,返回一个值

    Predicate

    @FunctionalInterface
    public interface Predicate<T> {
        /**
         * Evaluates this predicate on the given argument.
         *
         * @param t the input argument
         * @return {@code true} if the input argument matches the predicate,
         * otherwise {@code false}
         */
        boolean test(T t);
        //...
    }

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

    Consumer

    @FunctionalInterface
    public interface Consumer<T> {
        /**
         * Performs this operation on the given argument.
         *
         * @param t the input argument
         */
        void accept(T t);
        //...
    }

    消费型接口:输入一个参数,无返回值

    Supplier

    @FunctionalInterface
    public interface Supplier<T> {
    
        /**
         * Gets a result.
         *
         * @return a result
         */
        T get();
    }

    供给型接口:无输入参数,返回一个值

    ForkJoin

    大数据量下把大任务拆分为多个子任务,并行执行!分支合并计算!

    特点:工作窃取。一个线程执行完任务后,会窃取其他线程的任务进行执行。

    这个里面维护的是双端队列。

    package tmp;
    
    import java.util.concurrent.ForkJoinPool;
    import java.util.concurrent.ForkJoinTask;
    import java.util.concurrent.RecursiveTask;
    import java.util.stream.LongStream;
    
    public class TestStream {
    
        public static void main(String[] args) {
            ForkJoinPool forkJoinPool = new ForkJoinPool();
            long st = System.currentTimeMillis();
    //        Long invoke = forkJoinPool.invoke(new MyForkTask(1, 10_0000_0000));
    //       long invoke =  sum(1,10_0000_0000);
            long invoke = streamSum(1,10_0000_0000);
            long et = System.currentTimeMillis();
            System.out.println("结果:"+invoke+",耗时:"+(et-st));
        }
    
        private static long sum(long start,long end){
            long sum = 0;
            for (long i = start; i <= end; i++){
                sum += i;
            }
            return sum;
        }
    
        private static long streamSum(long start,long end){
            return LongStream.rangeClosed(start,end).parallel().sum();
        }
    }
    
    class MyForkTask extends RecursiveTask<Long> {
    
        long start;
        long end;
        long temp=100000;
    
        public MyForkTask(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;
                MyForkTask task1 = new MyForkTask(start,middle);
                task1.fork();
                MyForkTask task2 = new MyForkTask(middle+1,end);
                task2.fork();
    
                return task1.join()+ task2.join();
            }
        }
    }

    Future

    为未来进行建模

    CompletableFuture //异步调用
    //方法
        runAsync // 异步执行,无返回值
        supplyAsync //异步执行,有返回值

    CAS

    原子引用

    Integer(基本数据类型-整数类型的包装类)使用了对象缓存机制,默认范围是-128-127,推荐使用静态工厂方法valueOf获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间

    AtomicStampedReference泛型如果是Integer等,需要注意一下这个点

    公平锁和非公平锁

    公平锁:非常公平,不能够插队,必须先来后到

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

    可重入锁

    简单来讲就是当获取到某个资源的锁时,在获得锁的作用范围内,访问属于该资源内的属性或方法时,不需要重新加锁。

    自旋锁

  • 相关阅读:
    CentOS安装thrift
    6个用于大数据分析的最好工具
    我的助理辞职了!——给不听话的下属看看
    用Redis bitmap统计活跃用户、留存
    Java从入门到精通——数据库篇Oracle 11g服务详解
    Java从入门到精通——数据库篇之OJDBC版本区别
    非常有用!eclipse与myeclipse恢复已删除的文件和代码
    Redis 代理服务Twemproxy
    Redis集群明细文档
    正则表达式
  • 原文地址:https://www.cnblogs.com/java-meng/p/15189244.html
Copyright © 2020-2023  润新知