为什么要使用多线程

多线程能够提高程序的并发性和响应性。通过多线程可以充分利用多核CPU的计算能力,提高程序的执行效率。多线程适用于需要同时处理多个任务的场景,如网络请求、文件IO、GUI界面更新等。

创建线程有几种方式

定义Thread类的子类,并重写该类的run方法
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running");
    }
}
定义Runnable接口的实现类,并重写该接口的run()方法
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable running");
    }
}
定义Callable接口的实现类,并重写该接口的call()方法
class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "Callable result";
    }
}
线程池的方式
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> System.out.println("Task executed"));

start()方法和run()方法的区别

start()方法会启动一个新线程,并在新线程中执行run()方法。run()方法只是普通方法调用,不会创建新线程。

线程和进程的区别

进程是操作系统分配资源的基本单位,线程是CPU调度的基本单位。一个进程可以包含多个线程,线程共享进程的资源,但拥有独立的执行栈和程序计数器。

Runnable和Callable有什么区别

Runnablerun()方法没有返回值,也不能抛出异常。Callablecall()方法可以返回结果并抛出异常,通常与Future配合使用。

volatile作用,原理

volatile保证变量的可见性和禁止指令重排序。原理是通过内存屏障强制将变量的修改立即写入主内存,并使其他线程的缓存失效。

并发与并行的区别

并发是多个任务交替执行,并行是多个任务同时执行。并发适用于单核CPU,并行需要多核CPU支持。

synchronized的实现原理以及锁优化

synchronized通过monitorentermonitorexit指令实现,锁优化包括偏向锁、轻量级锁、重量级锁和自旋锁。

monitorenter、monitorexit、ACC_SYNCHRONIZED

monitorentermonitorexit用于同步代码块,ACC_SYNCHRONIZED用于同步方法。

monitor监视器

每个对象都有一个关联的monitor,用于控制线程的访问。

Java Monitor的工作机理

线程通过获取monitor的锁来进入同步代码块,其他线程必须等待锁释放。

对象与monitor关联

对象的Mark Word中存储了指向monitor的指针。

线程有哪些状态

线程状态包括NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED

synchronized和ReentrantLock的区别

synchronized是关键字,ReentrantLock是类。ReentrantLock提供更灵活的锁机制,如公平锁、可中断锁和超时锁。

wait(),notify()和suspend(),resume()之间的区别

wait()notify()Object的方法,需要配合synchronized使用。suspend()resume()已废弃,容易导致死锁。

CAS?CAS有什么缺陷,如何解决

CAS(Compare-And-Swap)是一种无锁算法,缺陷包括ABA问题和循环时间长开销大。ABA问题可以通过版本号解决。

CountDownLatch与CyclicBarrier区别

CountDownLatch是一次性的,CyclicBarrier可重复使用。CountDownLatch等待事件,CyclicBarrier等待线程。

什么是多线程环境下的伪共享

伪共享是由于多个线程频繁修改同一缓存行的不同变量,导致缓存失效。可以通过填充或对齐解决。

如何解决伪共享问题

使用@Contended注解或手动填充字段。

Fork/Join框架的理解

Fork/Join框架用于并行执行任务,采用工作窃取算法提高效率。

ThreadLocal原理

ThreadLocal为每个线程提供独立的变量副本,通过ThreadLocalMap存储数据。

TreadLocal为什么会导致内存泄漏

ThreadLocalMapkey是弱引用,但value是强引用。如果线程长时间运行,value不会被回收。

弱引用导致的内存泄漏

key被回收后,value仍然存在。

key是弱引用,GC回收会影响ThreadLocal的正常工作嘛

不会,ThreadLocal本身是强引用。

ThreadLocal内存泄漏的demo
ThreadLocal<Object> tl = new ThreadLocal<>();
tl.set(new Object());
tl = null; // key被回收,value仍在

为什么ThreadLocalMap的key是弱引用,设计理念是

避免ThreadLocal对象无法被回收,但需手动清理value

如何保证父子线程间的共享ThreadLocal数据

使用InheritableThreadLocal,子线程会继承父线程的ThreadLocal值。

如何保证多线程下i++结果正确

使用AtomicIntegersynchronized

如何检测死锁?怎么预防死锁?死锁四个必要条件

检测死锁使用jstackThreadMXBean。预防死锁通过破坏互斥、占有且等待、不可抢占和循环等待条件。

如果线程过多,会怎样

线程过多会导致上下文切换频繁,系统资源耗尽,性能下降。

happens-before原则

happens-before原则定义了操作之间的可见性关系,如程序顺序规则、锁规则等。

如何实现两个线程间共享数据

使用共享变量、volatilesynchronized或并发容器。

LockSupport作用是

LockSupport提供线程阻塞和唤醒的基本操作,比wait()/notify()更灵活。

线程池如何调优,如何确认最佳线程数

最佳线程数取决于任务类型和系统资源,通常为CPU核心数乘以期望的CPU利用率。

为什么要用线程池

线程池减少线程创建和销毁的开销,提高资源利用率和管理便捷性。

Java的线程池执行原理

线程池通过核心线程、任务队列和最大线程数控制任务执行。

聊聊线程池的核心参数

核心线程数、最大线程数、空闲时间、任务队列和拒绝策略。

当提交新任务时,异常如何处理

通过Future.get()捕获异常或设置UncaughtExceptionHandler

AQS组件,实现原理

AQS(AbstractQueuedSynchronizer)是并发包的基础,通过state和CLH队列实现同步。

state状态的维护

通过CAS操作修改state

CLH队列

双向队列管理等待线程。

ConditionObject通知

实现条件变量,支持await()signal()

模板方法设计模式

子类实现tryAcquiretryRelease

独占与共享模式

独占锁如ReentrantLock,共享锁如Semaphore

自定义同步器

通过继承AQS实现自定义锁。

Semaphore原理

Semaphore通过AQS实现,控制同时访问资源的线程数。

Semaphore使用demo
Semaphore semaphore = new Semaphore(3);
semaphore.acquire();
semaphore.release();
Semaphore原理

通过state表示可用许可数。

synchronized做了哪些优化?什么是偏向锁?什么是自旋锁?锁粗化?

偏向锁减少无竞争时的开销,自旋锁避免线程切换,锁粗化合并连续锁操作。

什么是上下文切换?什么是CPU上下文?什么是CPU上下文切换?

CPU上下文是线程的执行状态,上下文切换是保存和恢复线程状态的过程。

为什么wait(),notify(),notifyAll()在Object中,而不在Thread类中

因为这些方法需要操作对象的监视器锁,与对象关联。

线程池中submit()和execute()方法有什么区别

submit()返回Future,可以获取结果或异常;execute()没有返回值。

AtomicInteger的原理

通过CAS操作保证原子性。

Java中用到的线程调度算法是什么

时间片轮转和优先级调度。

shutdown()和shutdownNow()的区别

shutdown()平滑关闭,shutdownNow()立即中断所有线程。

说说几种常见的线程池及使用场景

newFixedThreadPool

固定大小,适用于负载稳定的场景。

newCachedThreadPool

弹性大小,适用于短期异步任务。

newSingleThreadExecutor

单线程,适用于任务顺序执行。

newScheduledThreadPool

定时任务,适用于周期性执行。

什么是FutureTask

FutureTask是可取消的异步计算任务,实现RunnableFuture

java中interrupt(),interrupted()和isInterrupted()的区别

interrupt()中断线程,interrupted()检查并清除中断状态,isInterrupted()仅检查状态。

有三个线程T1,T2,T3,怎么确保它们按顺序执行

使用join()CountDownLatch

有哪些阻塞队列

ArrayBlockingQueueLinkedBlockingQueuePriorityBlockingQueue等。

Java中ConcurrentHashMap的并发度是什么

并发度是分段锁的数量,影响并发性能。

Java线程有哪些常用的调度方法

线程休眠

Thread.sleep()

线程中断

Thread.interrupt()

线程等待

Object.wait()

线程让步

Thread.yield()

线程通知

Object.notify()

ReentrantLock的加锁原理

ReentrantLock使用的模板
lock.lock();
try {
    // critical section
} finally {
    lock.unlock();
}
什么是非公平锁,什么是公平锁?

非公平锁允许插队,公平锁按顺序获取锁。

lock()加锁流程

通过AQS的tryAcquire实现。

线程间的通讯方式

volatile和synchronized关键字

保证可见性和同步。

等待/通知机制

wait()notify()

管道输入/输出流

PipedInputStreamPipedOutputStream

join()方法

等待线程结束。

ThreadLocal

线程局部变量。

写出3条你遵循的多线程最佳实践

  1. 使用线程池管理线程。
  2. 避免共享可变状态,使用不可变对象或线程安全容器。
  3. 优先使用高级并发工具如ConcurrentHashMapCountDownLatch

为什么阿里发布的Java开发手册中强制线程池不允许使用Executors去创建

Executors提供的默认线程池可能导致资源耗尽,如newFixedThreadPoolnewCachedThreadPool的队列无界或线程数无界。推荐手动配置ThreadPoolExecutor

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐