1、Java 中的线程池是如何实现的?
Java中的线程池是通过Executor框架实现的。一般采用ThreadPoolExecutor实现。
(1)创建线程池: 创建一个 ThreadPoolExecutor 对象,并指定核心线程数、最大线程数、空闲线程存活时间、阻塞队列和线程工厂等参数。
(2)提交任务: 使用 submit() 或 execute() 方法将任务提交到线程池中。如果线程池中有空闲线程,任务会被分配给其中一个线程执行。如果没有可用的空闲线程,任务会被放入阻塞队列中等待。
(3)执行任务: 当线程池中有空闲线程时,会从阻塞队列中取出任务并分配给线程执行。线程执行完任务后会重新进入线程池中等待下一个任务。
(4)处理任务拒绝策略: 当线程池无法接受新任务时,会触发任务拒绝策略,处理方式包括抛出异常、直接丢弃任务、阻塞等待或者让调用者自己执行任务。
(5)关闭线程池:使用 shutdown() 或 shutdownNow() 方法关闭线程池。shutdown() 方法会等待线程池中的任务全部执行完毕后关闭线程池,而 shutdownNow() 方法会立即关闭线程池,并尝试终止正在执行的任务。
2、什么是AQS?
AQS 全称为 AbstractQueuedSynchronizer,是 Java 并发包中用于实现同步器的基础框架。它提供了一种实现阻塞锁和相关同步器的基础设施,并且在 Java 并发包中有广泛应用,如 ReentrantLock、CountDownLatch、Semaphore 等。
AQS 是通过一个双向队列(FIFO) 来管理等待线程的,队列中的节点表示等待锁的线程,锁的拥有者是队列中的第一个节点。当锁被释放时,AQS 会唤醒等待队列中的下一个节点来获取锁。
AQS 的核心思想是将同步状态抽象成一个整数值,并且使用 CAS 操作来修改同步状态的值,实现多线程的互斥和协作。具体来说,AQS 提供了 acquire() 和 release() 方法,用于获取锁和释放锁,并且支持可重入锁和条件变量等特性。
3、AQS 对资源的共享方式?
**(1)独占模式(Exclusive Mode):**在独占模式下,只有一个线程能够获取锁,其他线程必须等待该线程释放锁才能获取。这种方式适用于需要对共享资源进行修改的情况。
**(2)共享模式(Shared Mode):**在共享模式下,多个线程可以同时获取同一个锁,但是不能同时修改共享资源。这种方式适用于需要对共享资源进行读取的情况。
**(3)混合模式(Mixed Mode):**混合模式可以同时支持独占和共享两种模式。这种方式适用于同时需要读取和修改共享资源的情况。
4、什么是锁?
在多线程编程中,锁是用于控制对共享资源访问的同步机制。当多个线程同时访问共享资源时,可能会出现数据竞争(Data Race)的问题,导致程序出现错误或者崩溃。为了避免这种情况发生,需要使用锁来协调线程之间的访问,保证共享资源的正确性和一致性。
5、什么是同步锁?
同步锁(Synchronization Lock),也称为互斥锁(Mutual Exclusion Lock),是一种用于同步多个线程对共享资源的访问的机制。同步锁可以确保同一时间只有一个线程可以访问共享资源,其他线程必须等待该线程释放锁之后才能继续访问共享资源,避免了数据竞争问题的发生。
常见的同步锁有互斥锁、读写锁、信号量等。不同类型的同步锁适用于不同的场景,例如:
(1)互斥锁(Mutex):适用于需要独占共享资源的场景,同一时间只有一个线程可以访问共享资源。
(2)读写锁(Read-Write Lock):适用于读多写少的场景,允许多个线程同时读取共享资源,但是只有一个线程可以进行写操作。
(3)信号量(Semaphore):适用于控制并发访问数量的场景,可以限制同时访问某个共享资源的线程数。
同步锁是保证多线程程序正确运行的重要手段,合理使用同步锁可以避免多线程程序中出现的各种问题。
6、线程的run()和start()有什么区别?
(1)run()方法是线程的核心执行方法,其中包含了线程要执行的具体任务代码,当线程调用run()方法时,它会在当前线程上运行,不会创建新的线程。因此,直接调用run()方法并不会启动新的线程,只会在当前线程上执行run()方法中的代码。
(2)start()方法是启动一个新线程的方法,当调用start()方法时,会创建一个新的线程,并在新的线程上执行run()方法中的代码。因此,只有通过调用start()方法才能启动一个新的线程。
总结来说,run()方法是线程的任务执行方法,start()方法是启动线程的方法。直接调用run()方法不会启动新的线程,而是在当前线程上执行run()方法中的代码,这种方式没有并发效果;而通过调用start()方法可以启动新的线程,在新的线程上并发执行run()方法中的代码,从而实现并发的效果。
需要注意的是,在一个线程对象中,start()方法只能调用一次,如果多次调用会抛出IllegalThreadStateException异常。