1504 - Java多线程面试题

少年,思无邪,最最动人。

1.Java中有哪几种创建线程的方式

1.1 继承Thread类

代码示例

class HelloWorld01 extends Thread{
    @Override
    public void run() {
        System.out.println("这是继承 Thread 类方式实现多线程!");
    }
}

public class CreateThread01 {
    public static void main(String[] args) {
        new HelloWorld01().start();
        System.out.println("这是主线程的业务逻辑!");
    }
}

运行结果

这是主线程的业务逻辑!
这是继承 Thread 类方式实现多线程!

缺点:类是单继承的。

1.2 实现Runnable接口

代码示例

class HelloWorld02 implements Runnable{
    @Override
    public void run() {
        System.out.println("这是实现 Runnable 接口方式实现多线程!");
    }
}

public class CreateThread02 {
    public static void main(String[] args) {
        new Thread(new HelloWorld02()).start();
        System.out.println("这是主线程的业务逻辑!");
    }
}

运行结果

这是主线程的业务逻辑!
这是实现 Runnable 接口方式实现多线程!

优缺点:解决单继承问题,但是没有回调。

1.3 实现Callable接口

代码示例

class HelloWorld03 implements Callable {
    @Override
    public Object call() throws Exception {
        return "这是实现 Runnable 接口方式实现多线程!";
    }
}


public class CreateThread03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask<>(new HelloWorld03());
        new Thread(futureTask).start();
        System.out.println(futureTask.get());
        System.out.println("这是主线程的业务逻辑!");
    }
}

运行结果

这是实现 Runnable 接口方式实现多线程!
这是主线程的业务逻辑!

1.4 创建线程池

代码示例

class HelloWorld04 extends Thread{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

public class CreateThread04 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            executorService.execute(new HelloWorld04());
        }
        executorService.shutdownNow();
    }
}

 运行结果

pool-1-thread-1
pool-1-thread-3
pool-1-thread-4
pool-1-thread-5
pool-1-thread-2

2.为什么不建议使用Executors来创建线程池

查看 JDK 源码

可以看到,创建的队列为 LinkedBlockingQueue ,这是一个无界的阻塞队列。使用该线程池执行任务,如果任务过多就会不断地添加到这个队列中,任务越多占用的内存就越多,最终可能耗尽内存,导致OOM。

除此之外,使用这种方式创建线程池不能自定义线程的名字,不利于排查问题。

3.线程池有哪几种状态?每种状态分别表示什么

3.1 RUNNING

Accept new tasks and process queued tasks

表示线程池正常运行,既能接受新的任务,也会正常处理队列中的任务。一切正常状态。

3.2 SHUTDOWN

Don't accept new tasks, but process queued tasks

当调用线程池的 shutdown()方法时,线程池就进入 SHUTDOWN 状态,表示线程池处于正在关闭状态,此状态下线程池不会接受新任务,但是会继续把队列中的任务处理完。

3.3 STOP

Don't accept new tasks, don't process queued tasks, and interrupt in-process tasks 

当调用线程池的 shutdownnow()方法时,线程池马上停止,此状态下线程池既不会接受新任务了,也不会处理队列中的任务,并且正在运行的线程也会被中断

3.4 TIDYING

All tasks have terminated, workerCount is zero, the thread transitioning to state TIDYING will run the terminated() hook method

线程池中没有线程在运行后,线程池的状态。

3.5 TERMINATED 

当调用线程池的 terminated()方法时,线程池的状态。

4.Sychronized 和 ReentrantLock 有哪些不同点

SychronizedReentrantLock
Java中的一个关键字JDK提供的一个类
自动加锁与释放锁需要手动加锁与释放锁
JVM层面的锁API层面的锁
非公平锁公平锁或非公平锁
锁的是对象,锁信息保存在对象头中int类型的state标识来标识锁的状态
底层有锁升级的过程没有锁升级的过程

5.ThreadLocal有哪些应用场景,它的底层是如何实现的

ThreadLocal是Java中提供的线程本地缓存机制,可以利用该机制将数据缓存在某个线程内部,该线程可以在任意时刻、任意方法中获取缓存的数据。

ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象(注意不是ThreadLocal对象)中都存在一个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的值。

代码示例

public class ThreadLocalDemo {
    // 定义一个ThreadLocal,底层是Map实现
    private static ThreadLocal<String> local = new ThreadLocal<>();

    public static void a() {
        local.set("路明非");
    }

    public static void b() {
        String s = local.get();
        System.out.println(s);
        // 使用完了之后,移除,防止内存泄漏
        local.remove();
    }

    public static void main(String[] args) {
        a();
        b();
    }
}

注意:如果在线程池中使用ThreadLocal会造成内存泄漏,因为当 ThreadLocal 对象使用完之后,应该要把设置的key,value也就是Entry对象进行回收,但线程池中的线程不会回收,而线程对象是通过强引用指向ThreadLocalMap,ThreadLocalMap也是通过强引用指向Entry对象,线程不被回收,Entry对象也就不会被回收,从而出现内存泄漏,解决方法是,手动调用remove方法,手动清除Entry对象。

ThreadLocal经典的应用场景就是连接管理。

  • 16
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值