java面经自用4

本文探讨了Java中`synchronized`关键字和ReentrantLock API的区别,包括锁的获取释放机制、中断性、公平性以及条件变量的使用示例。还介绍了线程池的创建、常用类型及其参数配置,以及死锁的原理和自定义线程池的示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java面经自用4

  1. synchronized和lock有什么区别?

    • synchronized是关键字,jvm层面支持,lock是api层面的锁
    • synchronized不需要手动释放,lock需要手动释放不然会死锁(lock(),unlock()方法需要配合try/finally使用)
    • synchronized不可中断,lock可中断(设置超时trylock(Long timeout,TimeUnit unit),调用interrupt可中断
    ReentrantLock lock = new ReentrantLock();
    //获取锁
    lock.lockInterruptibly();
    //中断
    new Thread().interrupt();
    
    • synchronized是不公平锁,lock两者都可以实现boolean为true公平
    • synchronized要么随机唤醒一个要么全部唤醒,lock有条件的唤醒利用condition
    class bean {
    
        private Lock lock = new ReentrantLock();
        private Condition c1 = lock.newCondition();
        private Condition c2 = lock.newCondition();
        private Condition c3 = lock.newCondition();
    
        public void print(int number) {
            lock.lock();
            if (number == 1) {
                for (int i = 1; i <= 5; i++) {
                    System.out.println(Thread.currentThread().getName() + "==" + i);
                }
                c2.signal();
                try {
                    c3.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (number == 2) {
                for (int i = 1; i <= 10; i++) {
                    System.out.println(Thread.currentThread().getName() + "==" + i);
                }
                c3.signal();
                try {
                    c1.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (number == 3) {
                for (int i = 1; i <= 15; i++) {
                    System.out.println(Thread.currentThread().getName() + "==" + i);
                }
                c1.signal();
                try {
                    c2.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    public class condition {
        public static void main(String[] args) {
            bean b = new bean();
    
            new Thread("A") {
                @Override
                public void run() {
                    for (int i = 1; i <= 10; i++) {
                        b.print(1);
                    }
                }
            }.start();
            new Thread("B") {
                @Override
                public void run() {
                    for (int i = 1; i <= 10; i++) {
                        b.print(2);
                    }
                }
            }.start();
            new Thread("C") {
                @Override
                public void run() {
                    for (int i = 1; i <= 10; i++) {
                        b.print(3);
                    }
                }
            }.start();
        }
    
    
  2. 线程池

    创建线程4种方式

    为什么出现Callable接口?

    Callable接口(带返回值的),适合并发,解决阻塞绕过执行时间长的线程

    class myth implements Callable<Integer> {
    
        @Override
        public Integer call() throws Exception {
            //只会调用一次,没必要重复执行
            System.out.println(Thread.currentThread().getName()+"====="+"我叼你妈的");
            return 1024;
        }
    }
    
    public class mythread {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            FutureTask<Integer> task = new FutureTask<>(new myth());
            FutureTask<Integer> task2 = new FutureTask<>(new myth());
            new Thread(task, "AA").start();
            new Thread(task, "BB").start();
    		int result01=100;
            int result02=task.get();//建议放在最后使用,也就是说计算完成之后再去调用。如果想提前调用可能会出现自旋,while(!task.idDone)一直判断
            System.out.println(result01+result02);
        }
    }
    
    

    5种线程池,常用的有三种

    newWorkStealingPool(),java8新推出

    newScheduledThreadPool()带调度的,每隔时间段执行

    newSingleThreadExecutor()一个任务一个任务执行

    newCachedThreadPool()带缓存的,适合执行短期任务,灵活回收线程

    newFixedThreadPool(int),固定数线程,执行长期任务执行性能好

    都是通过new ThreadPoolExecutor()实现

     public static void main(String[] args) {
            ExecutorService pool1 = Executors.newFixedThreadPool(5);//一池五个线程
            ExecutorService pool2 = Executors.newSingleThreadExecutor();//一池一个线程
            ExecutorService pool3 = Executors.newCachedThreadPool();//一池N个线程
            try {
                for (int i = 1; i <= 10; i++) {
    //                pool1.execute(new Thread() {
    //                    @Override
    //                    public void run() {
    //                        System.out.println();
    //                    }
    //                });
                    pool1.execute(() -> {
    //                只有五个线程去执行
                        System.out.println(Thread.currentThread().getName()+"执行业务");
                    });
                    pool3.execute(()->{
                        System.out.println(Thread.currentThread().getName()+"执行业务");
                    });
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                pool1.shutdown();
            }
        }
    

    七大参数:

    • int corePoolSize:线程池中的常驻核心线程数
    • int maximumPoolSize:线程池能够容纳同时执行的最大线程数
    • Long KeepAliveTime:多余的空闲线程的存活时间,当线程池数量超过corePoolSize时,当空闲时间达到KeepAliveTime时,多余的空闲线程会被回收到只剩下corePoolSize
    • TimeUnit unit:KeepAliveTime的时间单位
    • BlockingQueue workQueue 任务队列,被提交暂未执行的任务
    • ThreadFactory threadFactory 表示生成线程池中工作线程的线程工厂,用于创建线程
    • RejectedExecutionHandler handler 拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数

    在这里插入图片描述

    4中拒绝策略:

    AbortPolicy:直接抛出RejectedExecutionHandler异常,阻止系统正常运行

    CallerRunsPolicy:将某些任务回退到调用者(main )

    DiscardOldestPolicy:抛弃队列中等待最久的任务,把当前任务加入到队列中并尝试再次提交当前任务

    DiscardPolicy:直接丢弃任务,无不给予任何处理也不抛出异常

    以上策略均实现RejectedExecutionHandler接口,以上线程池在实际开发中并不会使用而是我们自定义线程池

  3. 自定义线程池

    ThreadPoolExecutor pool = new ThreadPoolExecutor(
            2, 5, 1L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(3),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );
    //核心线程数为2,同时运行的最大线程数5(包括核心线程数),空闲队列的生存时间,时间的单位,创建线程的工厂,拒绝策略的设置
     public static void main(String[] args) {
    
            ThreadPoolExecutor pool = new ThreadPoolExecutor(
                    2, 5, 1L, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>(3),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.AbortPolicy()
            );
            try {
                for (int i = 1; i <= 10; i++) {
    //                pool1.execute(new Thread() {
    //                    @Override
    //                    public void run() {
    //                        System.out.println();
    //                    }
    //                });
    //                最大线程池为5+3=8,但是10个请求则报错
                    pool.execute(() -> {
    //                只有五个线程去执行
                        System.out.println(Thread.currentThread().getName()+"执行业务");
                    });
    
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                pool.shutdown();
            }
        }
    
    
  4. 如何配置合理线程数

    按照业务进行选择:CPU密集型,IO密集型

    首先调用Runtime.getRuntime().availableProcessors()后

    CPU密集型:

    线程数=CPU核数+1

    IO密集型分两种情况

    • 由于Io密集型任务线程并不是一直在运行任务,则配置尽可能多的线程,线程数= CPU核数*2
    • 线程数=CPU核数/1-阻塞系数,阻塞系数在0.8-0.9
  5. 产生死锁的主要原因?

    指两个或以上的进程在执行过程中,因为争夺资源而造成的一种互相等待的现象,若无外力干涉则永远推进下去,如果系统资源充足,进程的资源都本分得到满足,死锁出现的可能性就很低,否则就会因为争夺资源而陷入死锁

    手写死锁:

    class lock implements Runnable {
    
        private String lockA;
        private String lockB;
    
        public lock(String lockA, String lockB) {
            this.lockA = lockA;
            this.lockB = lockB;
        }
    
    
        @Override
        public void run() {
            synchronized (lockA) {
                System.out.println(Thread.currentThread().getName()+"持有"+lockA+"尝试获取"+lockB);
                synchronized (lockB){
                    System.out.println(Thread.currentThread().getName()+"持有"+lockB+"尝试获取"+lockA);
                }
            }
        }
    }
    
    
    public class dieLock {
    
        public static void main(String[] args) {
            String lockA = "lockA";
            String lockB = "lockB";
            new Thread(new lock(lockA, lockB), "ThreadAAA").start();
            new Thread(new lock(lockB, lockA), "ThreadBBB").start();
        }
    }
     public class   dieLock {
    
        public static void main(String[] args) {
            String lockA = "lockA";
            String lockB = "lockB";
            new Thread(new lock(lockA, lockB), "ThreadAAA").start();
            new Thread(new lock(lockB, lockA), "ThreadBBB").start();
        }
    }
    

    ``
    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值