线程面试题

线程

java有哪几种方式来创建线程执行任务
  1. 继承Thread类
  2. 实现Runnable接口,重写run()方法
  3. 实现Callable接口,重写call()方法
  4. 利用线程池来创建线程
    以上几种方式底层都是基于Runnable
run()和start()区别
  1. start()用于启动线程,run()用于执行线程运行时的代码。
  2. run()可以重复调用,而start()只能调用一次。
  3. run()是在本线程里的,只是线程里的一个函数,而不是多线程,如果直接调用run(),其实就相当于调用了一个普通函数而已;调用start()方法,会启动一个线程并使线程进入就绪状态,当分配到时间片后就可以开始运行,start()会执行线程的相应准备工作,然后自动执行run()方法;
sleep和wait区别
  1. 所属类不同:对于sleep方法,我们首先要知道该方法属于Thread类中的,而wait方法则是属于Object类中的;
  2. 语法使用不同:wait方法必须配合synchronized一起使用,不然在运行时就会抛出IllegalMonitorStateException异常,sleep可以单独使用;
  3. 释放锁资源不同:sleep方法导致程序暂停执行指定的时间,让出cpu的使用权,线程不会释放对象锁,当指定时间到了又会自动恢复运行状态;而当调用wait方法的时候,线程会放弃对象锁,主动释放锁;
  4. 唤醒方式不同:sleep方法必须要传递一个时间参数,调用之后线程进入TIMED_WAITING有时限等待状态,时间到了之后,线程会自动唤醒;而wait方法如果不指定时间的话,调用之后线程会进入WAITING无时限等待状态,直到另一个线程调用了notify或者notifyAll之后,休眠的线程才会被唤醒;
线程有哪些中断方式?都有什么区别?
  1. 正常退出:当线程中run()或者call()按照逻辑流程正常的执行结束,线程自然也就停止了;
  2. stop暴力停止:直接在程序中使用Thread.stop(),线程会马上停止,但是可能导致数据不同步,或者资源得不到回收的问题,而且stop已经被标注为废弃方法;
  3. interrupt()异常法:只是改变中断状态,不会中断一个正在运行的线程,需要线程处于阻塞状态,或调用interrupt()后使线程处于阻塞状态才会抛出一个InterruptException异常
线程状态

在这里插入图片描述

  1. 新建状态(NEW):当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值;
  2. 就绪状态(RUNNABLE):当线程对象调用了start()方法之后,该线程就处于就绪状态,java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行;
  3. 运行状态(RUNNING):如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程就处于运行状态;
  4. 阻塞状态(BLOCKED):阻塞状态是指线程因为某种原因放弃了CPU的使用权,也就是让出了cpu timeslice,暂时停止运行,直到线程进入可运行状态(Runnable),才有机会再次获得CPU timeslice转到运行状态。
  5. 线程死亡(DEAD):run()或call()方法执行完成,线程正常结束;线程抛出一个未捕获的Exception或Error;调用stop()方法。死亡的线程不可再次复生。
阻塞状态
  1. 等待阻塞(o.wait->等待队列):运行状态(RUNNING)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中;
  2. 同步阻塞(lock->锁池):运行状态(RUNNING)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM就会把该线程放入锁池(lock pool)中;
  3. 其他阻塞(sleep/join):运行状态(RUNNING)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态(RUNNABLE);
怎么理解线程安全

在多个线程去访问某个方法或者对象的时候,不管通过任何的方式调用或者线程如何去交替执行,在程序中不做任何同步干预的情况下,这个方法或者对象的执行或者修改都能够按照预期的结果来反馈,那么这个类我们认为它是线程安全的。
线程安全的表现有原子性、有序性和可见性。

线程池

线程池的理解

线程池本质上是一种池化技术,而池化技术是一种资源复用的一个设计思想,线程池里边复用的是线程资源。线程池首先可以减少线程的频繁创建和销毁带来的性能开销,因为线程创建会涉及到cpu上下文切换、内存分配等工作,再者线程池本身会有参数来控制线程创建的数量,这样就可以避免无休止的创建线程带来的资源利用率过高的问题,起到了资源保护的作用。

线程池核心属性
  1. corePoolSize(核心线程数):线程池中会维护一个最小的线程数量,即使这些线程处于空闲状态,也不会被销毁,除非设置了allowCoreThreadTimeOut为true。
  2. workQueue(队列):用于保留任务并移交给工作线程的阻塞队列
  3. maximumPoolSize(最大线程数):线程池允许开启的最大线程数
  4. handler(拒绝策略):线程池对拒绝任务(无线程可用)的处理策略:AbortPolicy:直接抛出RejectedExecutionException异常,调用者可以捕获这个异常;CallerRunsPolicy:主线程直接执行该任务,执行完之后尝试添加下一个任务到线程池中,可以有效降低向线程池内添加任务的速度;DiscardOldestPolicy:抛弃旧的任务、暂不支持;会导致被丢弃的任务无法再次被执行;DiscardPolicy:抛弃当前任务、暂不支持;会导致被丢弃的任务无法再次被执行
  5. keepAliveTime(保持存活时间):如果线程池当前线程数超过corePoolSize,则多余的线程空闲时间超过keepAliveTime时会被终止
  6. unit(空闲线程存活时间单位):keepAliveTime的计量单位
  7. threadFactory(线程工厂):用于创建工作线程的工厂
线程池的执行流程

在这里插入图片描述

线程池为什么用阻塞队列

线程池创建线程需要获取mainlock这个全局锁,影响并发效率,阻塞队列可以很多好的缓冲。如果新任务的到达速率超过了线程池的处理速率,那么新到来的请求将累加起来,这样的话可能会耗尽资源。

线程池有哪几种转态?每种状态分别表示什么?
  1. RUNNING:表示线程正常运行,既能接收新任务,也会正常处理队列中的任务;
  2. SHUTDOWN:当调用线程池的shutdown()方法时,线程池就会进入SHUTDOWN状态,表示线程池处于正在关闭状态,此状态下线程池不会接收新任务,但是会继续把队列中的任务处理完;
  3. STOP:当调用线程池的shutdownnow() 方法时,线程池就会进入STOP状态,表示线程池处于正在停止转态,此转态下线程池不会接收新任务,也不会继续处理队列中的任务,并且正在运行的线程也会被中断;
  4. TIDYING:线程池中没有线程在运行后,线程池的状态就会自动变为TIDYING,并且会调用terminated(),该方法是空方法,留给程序员进行扩展;
  5. TERMINATED:terminated()方法执行完之后,线程池状态就会变为TERMINATED
submit()和execute()的区别

submit()和execute()都是用来执行线程池的,只不过使用execute()执行线程池不能有返回方法,而使用submit()可以使用Future接收线程池执行的返回值

Semaphore 信号量

Semaphore是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做完自己的申请后归还,超过阈值后,线程申请许可信息会被阻塞。Semaphore可以用来构键一些线程池、资源池之类的额,比如数据库连接池。

 public static void main(String[] args) {
        //创建一个计数阈值为5的信号量对象,只能5个线程同时访问
        Semaphore semp = new Semaphore(5);
        for (int i = 0; i < 10; i++) {
            try {
                //申请许可
                System.out.println("申请许可");
                semp.acquire();
                System.out.println("申请成功");
                //执行业务逻辑
                testBiz(semp);
            } catch (InterruptedException e) {
                System.out.println("申请失败");
            }
        }
    }

    private static void testBiz(Semaphore semp) {
        Runnable runnable = () -> {
            try {
                Thread.sleep(10000);
                System.out.println("aaaa");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //释放许可
                System.out.println("释放许可");
                semp.release();
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值