Java多线程

多线程基础

一、创建线程

仅仅只有new Thread这种方法创建线程

Java中无法销毁一个线程,只能表现一个线程的状态。

通过thread.start()启动线程(仅仅只是通知线程启动)

thread.join()用于控制线程必须执行完成,调整优先级并不能保证优先级高的线程先执行。

1、继承Thread

  • 继承Thread
  • 覆盖run()方法
  • 直接调用Thread#start()执行
public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("Thread");
    }
}

public class ThreadMain {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}

2、实现Runnable

  • 实现Runnable
  • 获取实现Runnable接口的实例,作为参数,创建Thread实例
  • 执行Thread#start()启动线程
public class RunnableThread implements Runnable{
    @Override
    public void run() {
        System.out.println("Runnable");
    }
}

public class ThreadMain {
    public static void main(String[] args) {
        Thread thread = new Thread(new RunnableThread());
        thread.start();
    }
}

3、CallableFuture创建线程

Callable相对于Runnable会有一个线程返回值

  • 实现Callable接口
  • Callable的实现类为参数,创建FutureTask实例
  • FutureTask作为Thread的参数,创建Thread实例
  • 通过Thread#start启动线程
  • 通过FutureTask#get()阻塞获取线程的返回值
public class CallableThread implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("Callable");
        return 90;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Integer> futureTask = new FutureTask<Integer>(new CallableThread());
        Thread thread = new Thread(futureTask, "Thread1");
        thread.start();
        System.out.println(futureTask.get());
    }
}

4、使用线程池创建

public class MultiThreadStudy {
    public static void main(String[] args) {
        int corePoolSize = 5;
        int maximumPoolSize = 10;
        long keepAliveTime = 2;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue <Runnable>(3);
        ThreadPoolExecutor threadPoolExecutor = null;

        /**
         * ThreadPoolExecutor 线程池,几个参数的含义如下:
         * int corePoolSize,    核心线程数大小
         * int maximumPoolSize, 最大线程数
         * long keepAliveTime,  存活时间
         * TimeUnit unit,       keepAliveTime的时间单位
         * BlockingQueue<Runnable> workQueue,  存放待执行任务的队列
         * RejectedExecutionHandler handler    拒绝策略
         */
        try {
            threadPoolExecutor =  new ThreadPoolExecutor(corePoolSize,
                    maximumPoolSize,
                    keepAliveTime,
                    unit,
                    workQueue,
                    new ThreadPoolExecutor.AbortPolicy());
            //循环提交任务
            for (int i = 0; i < 13; i++) {
                //提交任务的索引
                final int index = (i + 1);
                threadPoolExecutor.submit(() -> {
                    //线程打印输出
                    System.out.println("大家好,我是线程:" + index);
                    try {
                        //模拟线程执行时间,10s
                        Thread.sleep(7000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
                //每个任务提交后休眠500ms再提交下一个任务,用于保证提交顺序
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPoolExecutor.shutdown();
        }
    }
}

5、通过Java创建进程

public class ProcessCreationQuestion {

    public static void main(String[] args) throws IOException {

        // 获取 Java Runtime
        Runtime runtime = Runtime.getRuntime();
        Process process = runtime.exec("cmd /k start http://www.baidu.com");
        process.exitValue();
    }

}

二、停止线程

无法真正停止一个线程,真正停止的只能是线程的逻辑。

请说明Thread interrupt()、isInterrupted()、interrupted()的区别以及意义?

Thread interrupt(): 设置状态,调JVM的本地(native)interrupt0()方法。

    public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();

        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
                interrupt0();  // Just to set the interrupt flag
                              //--> private native void interrupt0();
                b.interrupt(this);
                return;
            }
        }
        interrupt0();
    }

isInterrupted() 调的是静态方法isInterrupted(),当且仅当状态设置为中断时,返回false,并不清除状态。

public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}

public boolean isInterrupted() {
    return isInterrupted(false);
}

interrupted(): 私有本地方法,即判断中断状态,又清除状态。

 private native boolean isInterrupted(boolean ClearInterrupted);

三、线程异常

当线程遇到异常的时候,线程会挂

public class ThreadExceptionQuestion {

    public static void main(String[] args) throws InterruptedException {
        //...
        // main 线程 -> 子线程
        Thread t1 = new Thread(() -> {
            throw new RuntimeException("数据达到阈值");
        }, "t1");

        t1.start();
        // main 线程会中止吗?
        t1.join();

        // Java Thread 是一个包装,它由 GC 做垃圾回收
        // JVM Thread 可能是一个 OS Thread,JVM 管理,
        // 当线程执行完毕(正常或者异常)
        System.out.println(t1.isAlive());
    }
}

当线程遇到异常时,如何捕获?

...
        Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
            System.out.printf("线程[%s] 遇到了异常,详细信息:%s\n",
                    thread.getName(),
                    throwable.getMessage());
        });
...

当线程遇到异常时,ThreadPoolExecutor如何捕获异常?

public class ThreadPoolExecutorExceptionQuestion {

    public static void main(String[] args) throws InterruptedException {

//        ExecutorService executorService = Executors.newFixedThreadPool(2);

        ThreadPoolExecutor executorService = new ThreadPoolExecutor(
                1,
                1,
                0,
                TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>()
        ) {

            /**
             * 通过覆盖 {@link ThreadPoolExecutor#afterExecute(Runnable, Throwable)} 达到获取异常的信息
             * @param r
             * @param t
             */
            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                System.out.printf("线程[%s] 遇到了异常,详细信息:%s\n",
                        Thread.currentThread().getName(),
                        t.getMessage());
            }

        };

        executorService.execute(() -> {
            throw new RuntimeException("数据达到阈值");
        });

        // 等待一秒钟,确保提交的任务完成
        executorService.awaitTermination(1, TimeUnit.SECONDS);

        // 关闭线程池
        executorService.shutdown();
    }
}

四、线程状态

新建 -> 就绪 -> 阻塞 -> 运行 -> 死亡

获取当前jvm所有的现场状态

  • 可以直接使用jstack命令
  • ThreadMXBean
public class AllThreadStackQuestion {

    public static void main(String[] args) {
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        long[] threadIds = threadMXBean.getAllThreadIds();

        for (long threadId : threadIds) {
            ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId);
            System.out.println(threadInfo.toString());
        }

    }
}

获取线程的资源消费情况

public class AllThreadInfoQuestion {

    public static void main(String[] args) {
        ThreadMXBean threadMXBean = (ThreadMXBean) ManagementFactory.getThreadMXBean();
        long[] threadIds = threadMXBean.getAllThreadIds();

        for (long threadId : threadIds) {
//            ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId);
//            System.out.println(threadInfo.toString());
            long bytes = threadMXBean.getThreadAllocatedBytes(threadId);
            long kBytes = bytes / 1024;
            System.out.printf("线程[ID:%d] 分配内存: %s KB\n", threadId, kBytes);
        }

    }
}

五、线程同步

synchronized

synchronized关键字在修饰方法与代码块中的区别:字节码的区别,代码块用monitor,方法用synchronized关键字

synchronized关键字与ReentrantLock之间的区别:

  • 两者都是可重入锁
  • synchronized依赖于JVM,而ReentrantLock依赖于API
  • ReentrantLocksynchronized增加了一些高级功能
    • 等待可中断
    • 可实现公平锁
    • 可实现选择性通知(锁可以绑定多个条件)

偏向锁只对synchronized有用,而ReentrantLock已经实现了偏向锁.

六、线程通讯

wait()、notify()、notifyAll()方法是Object的本地final方法,无法被重写

wait():获得锁的对象,释放锁,当前线程又被阻塞,等同于Java 5 LockSupport中的park方法。

notify():已经获得锁,唤起一个被阻塞的线程,等同于Java 5 LockSupport中的unpark方法

notifyAll():会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会

七、线程退出

当主线程退出时,守护线程不一定执行完毕

public class DaemonThreadQuestion {

    public static void main(String[] args) {
        // main 线程
        Thread t1 = new Thread(() -> {
            System.out.println("Hello,World");
//            Thread currentThread = Thread.currentThread();
//            System.out.printf("线程[name : %s, daemon:%s]: Hello,World\n",
//                    currentThread.getName(),
//                    currentThread.isDaemon()
//            );
        }, "daemon");
        // 编程守候线程
        t1.setDaemon(true);
        t1.start();

        // 守候线程的执行依赖于执行时间(非唯一评判)
    }
}

ShutdownHook线程的使用场景,以及触发执行

使用场景:SpringAbstractApplicationContextregisterShutdownHook()

public class ShutdownHookQuestion {

    public static void main(String[] args) {

        Runtime runtime = Runtime.getRuntime();

        runtime.addShutdownHook(new Thread(ShutdownHookQuestion::action, "Shutdown Hook Question"));

    }

    private static void action() {
        System.out.printf("线程[%s] 正在执行...\n", Thread.currentThread().getName());  // 2
    }
}

如何确保主线程退出前,所有线程执行完毕?

public class CompleteAllThreadsQuestion {

    public static void main(String[] args) throws InterruptedException {

        // main 线程 -> 子线程
        Thread t1 = new Thread(CompleteAllThreadsQuestion::action, "t1");
        Thread t2 = new Thread(CompleteAllThreadsQuestion::action, "t2");
        Thread t3 = new Thread(CompleteAllThreadsQuestion::action, "t3");

        // 不确定 t1、t2、t3 是否调用 start()

        t1.start();
        t2.start();
        t3.start();

        // 创建了 N Thread

        Thread mainThread = Thread.currentThread();
        // 获取 main 线程组
        ThreadGroup threadGroup = mainThread.getThreadGroup();
        // 活跃的线程数
        int count = threadGroup.activeCount();
        Thread[] threads = new Thread[count];
        // 把所有的线程复制 threads 数组
        threadGroup.enumerate(threads, true);

        for (Thread thread : threads) {
            System.out.printf("当前活跃线程: %s\n", thread.getName());
        }
    }

    private static void action() {
        System.out.printf("线程[%s] 正在执行...\n", Thread.currentThread().getName());  // 2
    }

}

J.U.C并发集合框架

public class ThreadSafeCollectionQuestion {

    public static void main(String[] args) {

        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

        Set<Integer> set = Set.of(1, 2, 3, 4, 5);

        Map<Integer, String> map = Map.of(1, "A");

        // 以上实现都是不变对象,不过第一个除外

        // 通过 Collections#sychronized* 方法返回

        // Wrapper 设计模式(所有的方法都被 synchronized 同步或互斥)
        list = Collections.synchronizedList(list);

        set = Collections.synchronizedSet(set);

        map = Collections.synchronizedMap(map);
        
        list = new CopyOnWriteArrayList<>(list);
        set = new CopyOnWriteArraySet<>(set);
        map = new ConcurrentHashMap<>(map);

    }
}

一、List部分

Vector:底层方法使用synchronized进行同步操作,是List的实现。

CopyOnWriteArrayList:读的时候不加锁,弱一致性,添加数据的时候使用的是Arrays.copy()方法进行数据拷贝,和synchronized来保证线程的安全性。

SynchronizedList:底层方法使用synchronized进行同步操作,返回list,实现原理方式是Wrapper实现

二、Set部分

SynchronizedSet:底层方法使用synchronized进行同步操作

CopyOnWriteArraySet:底层借助CopyOnWriteArrayList数组实现。

ConcurrentSkipListSet:

当 Set#iterator() 方法返回 Iterator 对象后,能否在其迭代中,给 Set 对象添加新的元素?

不一定;Set 在传统实现中,会有fail-fast问题;而在J.U.C中会出现弱一致性,对数据的一致性要求较低,是可以给 Set 对象添加新的元素。

三、Map部分

Hashtable:底层方法使用synchronized进行同步操作,同步粒度较大。

ConcurrentHashMap:8以后底层使用CASsynchronized进行同步操作,数据结构为数组 + 链表 + 红黑树

ConcurrentSkipListMap

SynchronizedMap:底层方法使用synchronized进行同步操作

请说明 ConcurrentHashMap 与 ConcurrentSkipListMap 各自的优势与不足?

在 java 6 和 8 中,ConcurrentHashMap 写的时候,是加锁的,所以内存占得比较小,而 ConcurrentSkipListMap 写的时候是不加锁的,内存占得相对比较大,通过空间换取时间上的成本,速度较快,但比前者要慢,ConcurrentHashMap 基本上是常量时间。ConcurrentSkipListMap 读和写都是\(log N\)实现,高性能相对稳定。

四、Queue部分

请说明 BlockingQueue 与 Queue 的区别?

BlockingQueue 继承了 Queue 的实现;put 方法中有个阻塞的操作(InterruptedException),当队列满的时候,put 会被阻塞;当队列空的时候,put方法可用。take 方法中,当数据存在时,才可以返回,否则为空。

请说明 LinkedBlockingQueue 与 ArrayBlockingQueue 的区别?

LinkedBlockingQueue 是链表结构;有两个构造器,一个是(Integer.MAX_VALUE),无边界,另一个是(int capacity),有边界;ArrayBlockingQueue 是数组结构;有边界。

请说明 LinkedTransferQueue 与 LinkedBlockingQueue 的区别?

LinkedTransferQueue 是java 7中提供的新接口,性能比后者更优化。

五、PriorityBlockingQueue

public class PriorityBlockingQueueQuiz{
    public static void main(String[] args) throw Exception {
        BlockingQueue<Integer> queue = new PriorityBlockingQueue<>(2);
        // 1. PriorityBlockingQueue put(Object) 方法不阻塞,不抛异常
        // 2. PriorityBlockingQueue offer(Object) 方法不限制,允许长度变长
        // 3. PriorityBlockingQueue 插入对象会做排序,默认参照元素 Comparable 实现,
        //    或者显示地传递 Comparator
        queue.put(9);
        queue.put(1);
        queue.put(8);
        System.out.println("queue.size() =" + queue.size());
        System.out.println("queue.take() =" + queue.take());
        System.out.println("queue =" + queue);
    }
}

/**
queue.size() =3
queue.take() =1
queue =[8, 9]
*/

六、SynchronusQueue

public class SynchronusQueueQuiz{
    
    public static void main(String[] args) throws Exception {
        BlockingQueue<Integer> queue = new SynchronousQueue<>();
        // 1. SynchronousQueue 是无空间,offer 永远返回 false
        // 2. SynchronousQueue take() 方法会被阻塞,必须被其他线程显示地调用 put(Object);
        System.out.pringln("queue.offer(1) = " + queue.offer(1));
        System.out.pringln("queue.offer(2) = " + queue.offer(2));
        System.out.pringln("queue.offer(3) = " + queue.offer(3));
        System.out.println("queue.take() = " + queue.take());
        System.out.println("queue.size = " + queue.size());
    }
}

七、BlockingQueue offer()

请评估以下程序的运行结果?

public class BlockingQueueQuiz{
    public static void main(String[] args) throws Exception {
        offer(new ArrayBlockingQueue<>(2));
        offer(new LinkedBlockingQueue<>(2));
        offer(new PriorityBlockingQueue<>(2));
        offer(new SynchronousQueue<>());
    }
}

private static void offer(BlockingQueue<Integer> queue) throws Exception {
    System.out.println("queue.getClass() = " +queue.getClass().getName());
    System.out.println("queue.offer(1) = " + queue.offer(1));
    System.out.println("queue.offer(2) = " + queue.offer(2));
    System.out.println("queue.offer(3) = " + queue.offer(3));
    System.out.println("queue.size() = " + queue.size());
    System.out.println("queue.take() = " + queue.take());
    }
}

Java并发框架

一、锁Lock

锁主要提供了两种特性:互斥性和不可见性

请说明ReentranLockReentrantReadWriteLock的区别?

jdk 1.5以后,ReentranLock(重进入锁)与ReentrantReadWriteLock都是可重进入的锁,ReentranLock 都是互斥的,而ReentrantReadWriteLock是共享的,其中里面有两个类,一个是 ReadLock(共享,并行,强调数据一致性或者说可见性),另一个是 WriteLock(互斥,串行)。

请解释ReentrantLock为什么命名为重进入?

public class ReentrantLockQuestion {

    /**
     * T1 , T2 , T3
     *
     * T1(lock) , T2(park), T3(park)
     * Waited Queue -> Head-> T2 next -> T3
     * T1(unlock) -> unpark all
     * Waited Queue -> Head-> T2 next -> T3
     * T2(free), T3(free)
     *
     * -> T2(lock) , T3(park)
     * Waited Queue -> Head-> T3
     * T2(unlock) -> unpark all
     * T3(free)
     */
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        // thread[main] ->
        // lock     lock           lock
        // main -> action1() -> action2() -> action3()
        synchronizedAction(ReentrantLockQuestion::action1);
    }


    private static void action1() {
        synchronizedAction(ReentrantLockQuestion::action2);
    }

    private static void action2() {
        synchronizedAction(ReentrantLockQuestion::action3);
    }

    private static void action3() {
        System.out.println("Hello,World");
    }

    private static void synchronizedAction(Runnable runnable) {
        lock.lock();
        try {
            runnable.run();
        } finally {
            lock.unlock();
        }
    }
}

请说明 Lock#lock() 与 Lock#lockInterruptibly() 的区别?

    /**
     * java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued
     * 如果当前线程已被其他线程调用了 interrupt() 方法时,这时会返回 true
     * acquireQueued 执行完时,interrupt 清空(false)
     * 再通过 selfInterrupt() 方法将状态恢复(interrupt=true)
     */
     public static void main(String[] args) {
         lockVsLockInterruptibly();
     }
     
        private static void lockVsLockInterruptibly() {

        try {
            lock.lockInterruptibly();
            action1();
        } catch (InterruptedException e) {
            // 显示地恢复中断状态
            Thread.currentThread().interrupt();
            // 当前线程并未消亡,线程池可能还在存活
        } finally {
            lock.unlock();
        }
    }

lock() 优先考虑获取锁,待获取锁成功后,才响应中断。

**lockInterruptibly() ** 优先考虑响应中断,而不是响应锁的普通获取或重入获取。

ReentrantLock.lockInterruptibly 允许在等待时由其它线程调用等待线程的 Thread.interrupt 方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个 InterruptedException。

ReentrantLock.lock 方法不允许 Thread.interrupt 中断,即使检测到 Thread.isInterrupted ,一样会继续尝试获取锁,失败则继续休眠。只是在最后获取锁成功后再把当前线程置为 interrupted 状态,然后再中断线程。

二、条件变量

Condition使用场景

  1. CountDownLatch(condition变种)
  2. CyclicBarrier(循环屏障)
  3. 信号量/灯(Semaphore)java9
  4. 生产者和消费者
  5. 阻塞队列

请解释 Condition await() 和 signal() 与 Object wait () 和 notify() 的相同与差异?

相同:阻塞和释放

差异:Java Thread 对象和实际 JVM 执行的 OS Thread 不是相同对象,JVM Thread 回调 Java Thread.run() 方法,同时 Thread 提供一些 native 方法来获取 JVM Thread 状态,当JVM thread 执行后,自动 notify()了。

三、屏障Barriers

请说明 CountDownLatch 与 CyclicBarrier 的区别?

CountDownLatch : 不可循环的,一次性操作(倒计时)。

public class CountDownLatchQuestion {

    public static void main(String[] args) throws InterruptedException {

        // 倒数计数 5
        CountDownLatch latch = new CountDownLatch(5);

        ExecutorService executorService = Executors.newFixedThreadPool(5);

        for (int i = 0; i < 4; i++) {
            executorService.submit(() -> {
                action();
                latch.countDown(); // -1
            });
        }

        // 等待完成
        // 当计数 > 0,会被阻塞
        latch.await();

        System.out.println("Done");

        // 关闭线程池
        executorService.shutdown();
    }

    private static void action() {
        System.out.printf("线程[%s] 正在执行...\n", Thread.currentThread().getName());  // 2
    }

}

CyclicBarrier:可循环的, 先计数 -1,再判断当计数 > 0 时候,才阻塞。

public class CyclicBarrierQuestion {

    public static void main(String[] args) throws InterruptedException {

        CyclicBarrier barrier = new CyclicBarrier(5); // 5

        ExecutorService executorService = Executors.newFixedThreadPool(5); // 3

        for (int i = 0; i < 20; i++) {
            executorService.submit(() -> {
                action();
                try {
                    // CyclicBarrier.await() = CountDownLatch.countDown() + await()
                    // 先计数 -1,再判断当计数 > 0 时候,才阻塞
                    barrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });
        }

        // 尽可能不要执行完成再 reset
        // 先等待 3 ms
        executorService.awaitTermination(3, TimeUnit.MILLISECONDS);
        // 再执行 CyclicBarrier reset
        // reset 方法是一个废操作
        barrier.reset();

        System.out.println("Done");

        // 关闭线程池
        executorService.shutdown();
    }

    private static void action() {
        System.out.printf("线程[%s] 正在执行...\n", Thread.currentThread().getName());  // 2
    }

}

请说明 Semaphore(信号量/灯) 的使用场景?

Semaphore 和Lock类似,比Lock灵活。其中有 acquire() 和 release() 两种方法,arg 都等于 1。acquire() 会抛出 InterruptedException,同时从 sync.acquireSharedInterruptibly(arg:1)可以看出是读模式(shared); release()中可以计数,可以控制数量,permits可以传递N个数量。

public class SemaphoreTest {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 5; i++) {
            new SecurityCheckThread(i, semaphore).start();
        }
    }

    private static class SecurityCheckThread extends Thread{
        private int seq;
        private Semaphore semaphore;

        public SecurityCheckThread(int seq, Semaphore semaphore){
            this.seq = seq;
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println("No." + seq + "乘客,正在查验中....");

                if (seq%2 == 0){
                    Thread.sleep(1000);
                    System.out.println("No." + seq + "乘客,身份可疑,不能出国!");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
                System.out.println("No." + seq + "乘客已完成服务。");
            }
        }
    }
}

CountDownLatch实现

public class LegacyCountDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {

        // 倒数计数 5
        MyCountDownLatch latch = new MyCountDownLatch(5);

        ExecutorService executorService = Executors.newFixedThreadPool(5);

        for (int i = 0; i < 5; i++) {
            executorService.submit(() -> {
                action();
                latch.countDown(); // -1
            });
        }

        // 等待完成
        // 当计数 > 0,会被阻塞
        latch.await();

        System.out.println("Done");

        // 关闭线程池
        executorService.shutdown();
    }

    private static void action() {
        System.out.printf("线程[%s] 正在执行...\n", Thread.currentThread().getName());  // 2
    }

    /**
     * Java 1.5+ Lock 实现
     */
    private static class MyCountDownLatch {

        private int count;

        private final Lock lock = new ReentrantLock();

        private final Condition condition = lock.newCondition();

        private MyCountDownLatch(int count) {
            this.count = count;
        }

        public void await() throws InterruptedException {
            // 当 count > 0 等待
            if (Thread.interrupted()) {
                throw new InterruptedException();
            }

            lock.lock();
            try {
                while (count > 0) {
                    condition.await(); // 阻塞当前线程
                }
            } finally {
                lock.unlock();
            }
        }

        public void countDown() {

            lock.lock();
            try {
                if (count < 1) {
                    return;
                }
                count--;
                if (count < 1) { // 当数量减少至0时,唤起被阻塞的线程
                    condition.signalAll();
                }
            } finally {
                lock.unlock();
            }
        }
    }

    /**
     * Java < 1.5 实现
     */
    private static class LegacyCountDownLatch {

        private int count;

        private LegacyCountDownLatch(int count) {
            this.count = count;
        }

        public void await() throws InterruptedException {
            // 当 count > 0 等待
            if (Thread.interrupted()) {
                throw new InterruptedException();
            }

            synchronized (this) {
                while (count > 0) {
                    wait(); // 阻塞当前线程
                }
            }
        }

        public void countDown() {
            synchronized (this) {
                if (count < 1) {
                    return;
                }
                count--;
                if (count < 1) { // 当数量减少至0时,唤起被阻塞的线程
                    notifyAll();
                }
            }
        }
    }
}

四、线程池

请问J.U.C中内建了几种ExceptionService实现?

1.5:ThreadPoolExecutor、ScheduledThreadPoolExecutor

1.7:ForkJoinPool

public class ExecutorServiceQuestion {

    public static void main(String[] args) {
        /**
         * 1.5
         *  ThreadPoolExecutor
         *  ScheduledThreadPoolExecutor :: ThreadPoolExecutor
         * 1.7
         *  ForkJoinPool
         */
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        executorService = Executors.newScheduledThreadPool(2);

        // executorService 不再被引用,它会被 GC -> finalize() -> shutdown()
        ExecutorService executorService2 = Executors.newSingleThreadExecutor();
    }
}

如何获取 ThreadPoolExecutor 正在运行的线程?

public class ThreadPoolExecutorThreadQuestion {

    public static void main(String[] args) throws InterruptedException {

        // main 线程启动子线程,子线程的创造来自于 Executors.defaultThreadFactory()

        ExecutorService executorService = Executors.newCachedThreadPool();
        // 之前了解 ThreadPoolExecutor beforeExecute 和 afterExecute 能够获取当前线程数量

        Set<Thread> threadsContainer = new HashSet<>();

        setThreadFactory(executorService, threadsContainer);
        for (int i = 0; i < 9; i++) { // 开启 9 个线程
            executorService.submit(() -> {
            });
        }

        // 线程池等待执行 3 ms
        executorService.awaitTermination(3, TimeUnit.MILLISECONDS);

        threadsContainer.stream()
                .filter(Thread::isAlive)
                .forEach(thread -> {
                    System.out.println("线程池创造的线程 : " + thread);
                });

        Thread mainThread = Thread.currentThread();

        ThreadGroup mainThreadGroup = mainThread.getThreadGroup();

        int count = mainThreadGroup.activeCount();
        Thread[] threads = new Thread[count];
        mainThreadGroup.enumerate(threads, true);

        Stream.of(threads)
                .filter(Thread::isAlive)
                .forEach(thread -> {
                    System.out.println("线程 : " + thread);
                });

        // 关闭线程池
        executorService.shutdown();
    }

    private static void setThreadFactory(ExecutorService executorService, Set<Thread> threadsContainer) {

        if (executorService instanceof ThreadPoolExecutor) {
            ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorService;
            ThreadFactory oldThreadFactory = threadPoolExecutor.getThreadFactory();
            threadPoolExecutor.setThreadFactory(new DelegatingThreadFactory(oldThreadFactory, threadsContainer));
        }
    }

    private static class DelegatingThreadFactory implements ThreadFactory {

        private final ThreadFactory delegate;

        private final Set<Thread> threadsContainer;

        private DelegatingThreadFactory(ThreadFactory delegate, Set<Thread> threadsContainer) {
            this.delegate = delegate;
            this.threadsContainer = threadsContainer;
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = delegate.newThread(r);
            // cache thread
            threadsContainer.add(thread);
            return thread;
        }
    }
}

五、Future

如何获取 Future 对象?

submit()

请举例 Future get() 以及 get(Long,TimeUnit) 方法的使用场景?

  • 超时等待
  • InterruptedException
  • ExcutionException
  • TimeOutException

如何利用 Future 优雅地取消一个任务的执行?

public class CancellableFutureQuestion {

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newSingleThreadExecutor();

        Future future = executorService.submit(() -> { // 3秒内执行完成,才算正常
            action(5);
        });

        try {
            future.get(3, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            // Thread 恢复中断状态
            Thread.currentThread().interrupt();
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (TimeoutException e) {
            // 执行超时,适当地关闭
            Thread.currentThread().interrupt(); // 设置中断状态
            future.cancel(true); // 尝试取消
        }

        executorService.shutdown();
    }

    private static void action(int seconds) {
        try {
            Thread.sleep(TimeUnit.SECONDS.toMillis(seconds)); // 5 - 3
            // seconds - timeout = 剩余执行时间
            if (Thread.interrupted()) { // 判断并且清除中断状态
                return;
            }
            action();
        } catch (InterruptedException e) {
        }
    }

    private static void action() {
        System.out.printf("线程[%s] 正在执行...\n", Thread.currentThread().getName());  // 2
    }
}

六、Volatile

在 Java 中,volatile 保证的是可见性还是原子性?

volatile 既有可见性又有原子性(非我及彼),可见性是一定的,原子性是看情况的。对象类型和原生类型都是可见性,原生类型是原子性。

在 Java 中,volatile long 和 double 是线程安全的吗?

volatile long 和 double 是线程安全的。

在 Java 中,volatile 底层实现是基于什么机制?

内存屏障(变量 Lock)机制:一个变量的原子性的保证。

七、原子操作Atomic

为什么 AtomicBoolean 内部变量使用 int 实现,而非 boolean?

操作系统有 X86 和 X64,在虚拟机中,基于boolean 实现就是用 int 实现的,用哪一种实现都可以。虚拟机只有32位和64位的,所以用32位的实现。

在变量原子操作时,Atomic CAS 操作比 synchronized 关键字哪个更重?*

Synchronization

同线程的时候,synchronized 更快;而多线程的时候则要分情况讨论。

public class AtomicQuestion {

    private static int actualValue = 3;

    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(3);
        // if( value == 3 )
        //     value = 5
        atomicInteger.compareAndSet(3, 5);
        // 偏向锁 < CAS 操作 < 重锁(完全互斥)
        // CAS 操作也是相对重的操作,它也是实现 synchronized 瘦锁(thin lock)的关键
        // 偏向锁就是避免 CAS(Compare And Set/Swap)操作
    }

    private synchronized static void compareAndSet(int expectedValue, int newValue) {
        if (actualValue == expectedValue) {
            actualValue = newValue;
        }
    }
}

Atomic CAS 的底层是如何实现?*

汇编指令:cpmxchg (Compare and Exchange)

posted @ 2021-03-17 08:36  ice_image  阅读(643)  评论(0编辑  收藏  举报