Java并发编程基础

1、8锁现象

2、集合类不安全

  • CopyOnWriteArrayList
  • CopyOnWriteArraySet
  • ConcurrentHashMap

3、常用的辅助类

  1. CountDownLatch

    public class CountDownLatchDemo {
    
        public static void main(String[] args) throws InterruptedException {
            // 总数是6
            CountDownLatch countDownLatch = new CountDownLatch(6);
    
            for (int i = 0; i < 6; i++) {
                new Thread(() -> {
                    System.out.println(Thread.currentThread().getName() + "go out");
                    countDownLatch.countDown();
                }, String.valueOf(i)).start();
            }
    
            countDownLatch.await();
            System.out.println("close door");
        }
    }
    

    原理:

    countDownLatch.countDown() :数量-1

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

    每次有线程调用countDown数量-1,假设计数器为0,countDownLatch.await()就会被唤醒,继续执行

  2. CyclicBarrier

    加法计数器

    public class CyclicBarrierDemo {
    
        public static void main(String[] args) {
            /**
             * 集齐7颗龙珠召唤神龙
             */
    
            CyclicBarrier barrier = new CyclicBarrier(7, () -> {
                System.out.println("召唤神龙成功");
            });
    
            for (int i = 1; i <= 7; i++) {
                final int temp = i;
                new Thread(() -> {
                    System.out.println(Thread.currentThread().getName() + "收集" + temp + "个龙珠");
                    try {
                        barrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }, String.valueOf(i)).start();
            }
    
        }
    }
    
  3. Semaphore

    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(2);
                        System.out.println(Thread.currentThread().getName() + "离开车位");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        semaphore.release();
                    }
                }, String.valueOf(i)).start();
            }
        }
    }
    

    原理:

    semaphore.acquire():获取,假设如果已经满了,等待,等待被释放为止

    semaphore.release():释放,会将当前的信号量释放,然后唤醒等待的线程

    作用:多个共享资源互斥使用!并发限流,控制最大的线程数

4、读写锁

/**
 * @Classname ReadWriteLockDemo
 * @Date 2021/4/1 下午4:41
 * @Created by fei.liu
 *
 * 独占锁(写锁):一次只能被一个线程持有
 * 共享锁(读锁):多个线程可以同时占有
 *
 * 读-读:可以共存
 * 读-写:不能共存
 * 写-写:不能共存
 */
public class ReadWriteLockDemo {

    public static void main(String[] args) {
        MyCache cache = new MyCache();
//        MyCacheLock cache = new MyCacheLock();

        for (int i = 1; i <= 5; i++) {
            final int tmp = i;
            new Thread(() -> {
                cache.put(tmp + "", tmp + "");
            }, String.valueOf(i)).start();
        }

        for (int i = 1; i <= 5; i++) {
            final int tmp = i;
            new Thread(() -> {
                cache.get(tmp + "");
            }, String.valueOf(i)).start();
        }
    }
}

/**
 * 自定义缓存 + 锁
 */
class MyCacheLock {
    private volatile Map<String, Object> map = new HashMap<>();

    private ReadWriteLock lock = new ReentrantReadWriteLock();

    /**
     * 写入的时候只希望同时只有一个线程写数据
     * @param key
     * @param value
     */
    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() + "写入ok");
        } catch (Exception e) {
            e.getMessage();
        } finally {
            lock.writeLock().unlock();
        }
    }

    /**
     * 读取数据多线程可以同时读取
     * @param key
     * @return
     */
    public Object get(String key) {
        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "获取" + key);
            return map.get(key);
        } catch (Exception e) {
            e.getMessage();
        } finally {
            lock.readLock().unlock();
        }
        return null;
    }
}

/**
 * 自定义缓存
 */
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() + "写入ok");
    }

    public Object get(String key) {
        System.out.println(Thread.currentThread().getName() + "获取" + key);
        return map.get(key);
    }
}

5、阻塞队列

什么情况下我们会使用阻塞队列?

多线程并发处理、线程池

使用队列

方式抛出异常有返回值,不抛出异常阻塞 等待超时等待
添加addoffer()put()offer()
移除removepoll()take()poll()
判断队列首element()peek()--

6、线程池(重点)

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

线程池、连接池、内存池、对象池……

池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我

线程池的好处:

  • 降低资源消耗
  • 降低响应速度
  • 方便管理

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

线程池三大方法:

public class ThreadPoolDemo1 {

    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) {
            System.out.println(e.getMessage());
        } finally {
            // 线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

线程池7大参数:

源码分析:

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }


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

workQueue:SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue

最大线程到底如何定义?

  • CPU密集型:几核,就是几,可以保证CPU的效率最高
  • IO 密集型:判断程序中十分耗IO的线程

线程池规则(假设任务队列没有大小限制):

  • 如果线程数<=核心线程数量,那么直接启动一个核心线程来执行任务,不会放入队列中
  • 如果线程数量>核心线程数,但<=最大线程数,并且队列是LinkedBlockingQueue的时候,超过核心线程数量的任务会被放在任务队列中排队
  • 如果线程数量>核心线程数,但<=最大线程数,并且队列是SynchronousQueue的时候,线程池会创建新的线程执行任务,这些任务不会放在任务队列中。这些线程属于非核心线程,在任务完成后,闲置时间打到了超时时间就会被清除
  • 如果线程数量>核心线程数,并且>最大线程数,并且队列是LinkedBlockingQueue的时候,超过核心线程的任务放在任务队列中排队。也就是LinkedBlockingQueue没有大小限制时,线程池最大线程数设置是无效的,他的线程数最多不会超过核心线程数
  • 如果线程数量>核心线程数,并且>最大线程数,并且队列是SynchronousQueue的时候,会因为线程池拒绝添加任务而抛出异常

线程池规则(假设任务队列有大小限制):

  • 当LinkedBlockQueue塞满时,新增的任务会直接直接创建新线程来执行,当创建的新线程超过最大线程数量就会抛出异常
  • SynchronousQueue没有数量限制,因为他不保存任务,直接交给线程池去执行,任务超过最大线程数就会抛出异常

7、四大函数式接口(必须掌握)

新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流式计算

  1. 函数式接口:只有一个方法的接口
@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

// 超级多FunctionalInterface
// 简化编程模型,在新版本的框架底层大量应用
  1. 断定型接口:有一个输入参数,返回值只能是布尔值
@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}
  1. consumer:消费型接口:接受参数,没有返回值
@FunctionalInterface
public interface Consumer<T> {

    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}
  1. supplier:供给型接口:没有参数,只有返回值
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

8、Stream流式计算

什么是流式计算?

9、ForkJoin

什么是ForkJoin?

ForkJoin 在 JDK 1.7,并行执行任务!提高效率,大数据量!

ForkJoin特点:工作窃取

10、异步回调

Future 设计的初衷:对将来的某个事件的结果进行建模

public class FutureDemo {

    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() + " async");
//        });

//        System.out.println("1111");
//
//        try {
//            completableFuture.get(); //获取执行结果
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        } catch (ExecutionException e) {
//            e.printStackTrace();
//        }

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "supply");
            int i = 10 / 0;
            return 1024;
        });

        System.out.println(future.whenComplete((t, u) -> {
            System.out.println("t = " + t);
            System.out.println("u = " + u);
        }).exceptionally((e) -> {
            System.out.println(e.getMessage());
            return 233;
        }).get());

    }
}

16、JMM

请谈谈对Volatile的理解?

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

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

如何保证可见性?什么是JMM?

JMM:java内存模型,不存在的东西,概念,约定!

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

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

11、volatile

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排
    1. 保证特定的操作的执行顺序
    2. 可以保证某些变量的内存可见性(利用这些特性就可以保证volatile的可见性)

原子类:底层CAS

指令重排:编写的程序,计算机并不是按照写的那样去执行的

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

12、彻底玩转单例模式

13、深入理解CAS

什么是CAS?

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

缺点:

  1. 循环会耗时
  2. 一次性只能保证一个共享变量的原子性
  3. ABA问题

14、原子引用

public class AtomicReferenceDemo {    public static void main(String[] args) {        // AtomicStampedReference 注意:如果泛型是包装类,注意对象的引用问题        AtomicStampedReference<Integer> reference = new AtomicStampedReference<>(1, 1);        new Thread(() -> {            int stamp = reference.getStamp(); // 获取版本号            System.out.println("a stamp = " + stamp);            try {                TimeUnit.SECONDS.sleep(2);            } catch (InterruptedException e) {                e.printStackTrace();            }            reference.compareAndSet(1, 2, reference.getStamp(), reference.getStamp() + 1);            System.out.println("a 2 stamp = " + reference.getStamp());            reference.compareAndSet(2, 1, reference.getStamp(), reference.getStamp() + 1);            System.out.println("a 3 stamp = " + reference.getStamp());        }, "a").start();        new Thread(() -> {            int stamp = reference.getStamp(); // 获取版本号            System.out.println("b stamp = " + stamp);            try {                TimeUnit.SECONDS.sleep(2);            } catch (InterruptedException e) {                e.printStackTrace();            }            reference.compareAndSet(1, 66, stamp, stamp + 1);            System.out.println("b 2 stamp = " + reference.getStamp());        }, "b").start();    }}

15、各种锁的理解

  1. 公平锁、非公平锁

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

    非公平锁:不公平,可以插队;

    public ReentrantLock() {    sync = new NonfairSync();}public ReentrantLock(boolean fair) {    sync = fair ? new FairSync() : new NonfairSync();}
    
  2. 可重入锁

    可重入锁(递归锁)

    public class ReEnterLock {    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");    }}
    
  3. 自旋锁

    spinlock:自定义自旋锁测试

    public class SpinLockDemo {    AtomicReference<Thread> atomicReference = new AtomicReference<>();    // 加锁    public void myLock() {        Thread thread = Thread.currentThread();        System.out.println(Thread.currentThread().getName() + "--> my lock");        // 自旋锁        while (!atomicReference.compareAndSet(null, thread)) {        }    }    // 解锁    public void myUnLock() {        Thread thread = Thread.currentThread();        System.out.println(Thread.currentThread().getName() + "--> my un lock");        atomicReference.compareAndSet(thread, null);    }}
    

    16、死锁

    死锁测试,怎么样排除死锁?

    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 + ", want " + lockB);            try {                TimeUnit.SECONDS.sleep(2);            } catch (InterruptedException e) {                e.printStackTrace();            }            synchronized (lockB) {                System.out.println(Thread.currentThread().getName() + "lock" + lockB + ", want " + lockA);            }        }    }}
    

16、并发编程中的三个概念

  • 原子性
  • 可见性
  • 有序性

17、happens-before原则

  • 程序次序院子:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
  • 锁定原则:一个unLock操作先行于后面对同一个锁的lock操作
  • volatile变量原则:对一个变量的写操作先行发生于后面对这个变量的读操作
  • 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C
  • 线程启动规则:Thread对象的start()方法先行于此线程的每一个动作
  • 线程中断规则:对线程的interruput()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
  • 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已终止执行
  • 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始

18、死锁

public class DeadLock implements Runnable {    public int flag = 1;    private static Object o1 = new Object(), o2 = new Object();    @Override    public void run() {        System.out.println("flag = " + flag);        if (flag == 1) {            synchronized (o1) {                try {                    Thread.sleep(500);                } catch (InterruptedException e) {                    e.printStackTrace();                }                synchronized (o2) {                    System.out.println("1");                }            }        }        if (flag == 2) {            synchronized (o2) {                try {                    Thread.sleep(500);                } catch (InterruptedException e) {                    e.printStackTrace();                }                synchronized (o1) {                    System.out.println(2);                }            }        }    }    public static void main(String[] args) {        DeadLock dt1 = new DeadLock();        DeadLock dt2 = new DeadLock();        dt1.flag = 1;        dt2.flag = 2;        new Thread(dt1).start();        new Thread(dt2).start();    }}

19、Callable

1、通过Thread来提交任务

public class CallableAndFuture {    public static void main(String[] args) {        Callable<Integer> callable = new Callable<Integer>() {            @Override            public Integer call() throws Exception {                return new Random().nextInt(100);            }        };        FutureTask<Integer> futureTask = new FutureTask<>(callable);        new Thread(futureTask).start();                try {            Thread.sleep(5000);            System.out.println(futureTask.get());        } catch (Exception e) {            e.printStackTrace();        }    }}

2、通过ExecutorService的submit执行Callable

public class CallableAndFuture {    public static void main(String[] args) {        ExecutorService threadPool = Executors.newSingleThreadExecutor();        Future<Integer> future = threadPool.submit(new Callable<Integer>() {            @Override            public Integer call() throws Exception {                return new Random().nextInt(100);            }        });        try {            Thread.sleep(5000);            System.out.println(future.get());        } catch (Exception e) {            e.printStackTrace();        }    }}

3、执行多个带返回值的任务,并取得多个返回值

public class CallableAndFuture {    public static void main(String[] args) {        ExecutorService threadPool = Executors.newCachedThreadPool();        CompletionService<Integer> cs = new ExecutorCompletionService<>(threadPool);        for (int i = 1; i < 5; i++) {            final int taskId = i;            cs.submit(new Callable<Integer>() {                @Override                public Integer call() throws Exception {                    return taskId;                }            });        }        for (int i = 1; i < 5; i++) {            try {                System.out.println(cs.take().get());            } catch (Exception e) {                e.printStackTrace();            }        }    }}

20、ListenableFuture根据结果,主动中断流程

public class ListenableFutureDemo {    public static void main(String[] args) {        ExecutorService executor = Executors.newFixedThreadPool(2);        ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(executor);        final Queue<ListenableFuture<Boolean>> currentMiningTasks = new ConcurrentLinkedQueue<>();        currentMiningTasks.add(listeningExecutorService.submit(new A()));        currentMiningTasks.add(listeningExecutorService.submit(new A()));        currentMiningTasks.add(listeningExecutorService.submit(new A(true)));        currentMiningTasks.add(listeningExecutorService.submit(new A()));        currentMiningTasks.add(listeningExecutorService.submit(new A()));        currentMiningTasks.add(listeningExecutorService.submit(new A()));        currentMiningTasks.add(listeningExecutorService.submit(new A()));        currentMiningTasks.add(listeningExecutorService.submit(new A()));        currentMiningTasks.add(listeningExecutorService.submit(new A()));        currentMiningTasks.add(listeningExecutorService.submit(new A()));        currentMiningTasks.add(listeningExecutorService.submit(new A()));        // 一定要执行这个方法,        listeningExecutorService.shutdown();        executor.shutdown();        for (final ListenableFuture<Boolean> task : currentMiningTasks) {            try {                System.out.println("结果..." + task.get());                if (task.get()) {                    for (final ListenableFuture<Boolean> t : currentMiningTasks) {                        if (t != null && !t.isCancelled()) {                            t.cancel(true);                        }                    }                }            } catch (Exception e) {                e.printStackTrace();            }        }        while (true) {            if (listeningExecutorService.isTerminated() && executor.isTerminated()) {                System.out.println("listeningExecutorService executor 结束");                break;            }        }        System.out.println("结束。。。。。");    }}class A implements Callable<Boolean> {    boolean flag = false;    public A() {    }    public A(boolean flag) {        this.flag = flag;    }    @Override    public Boolean call() throws Exception {        Thread.sleep(1000);        return flag;    }}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值