多线程

创建线程的三种方式

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等,需要注意一下这个点

公平锁和非公平锁

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

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

可重入锁

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

自旋锁

posted @ 2021-08-26 14:08  梦玄庭  阅读(197)  评论(0编辑  收藏  举报