zoukankan      html  css  js  c++  java
  • 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
    ",
                        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
    ",
                            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
    ", 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
    ",
    //                    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] 正在执行...
    ", 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
    ", thread.getName());
            }
        }
    
        private static void action() {
            System.out.printf("线程[%s] 正在执行...
    ", 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] 正在执行...
    ", 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] 正在执行...
    ", 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] 正在执行...
    ", 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] 正在执行...
    ", 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)

  • 相关阅读:
    Maven学习
    Oracle_SQL函数-单行函数
    Java 8新特性-5 内建函数式接口
    量子优势
    配置Emeditor编译运行JAVA,附私家珍藏版
    配置Emeditor编译运行JAVA,附私家珍藏版
    Notepad2-mod,轻量级文本编辑器、代替记事本的最佳选择
    三星S7短信不能提示的处理方法
    三星S7短信不能提示的处理方法
    说说宾得机身的十大人性化设定和功能[转]
  • 原文地址:https://www.cnblogs.com/ice-image/p/14547180.html
Copyright © 2011-2022 走看看