启动线程的方式
常规
继承Thread类
重写run方法
创建该类实例后
调用start方法
public class MyThread extends Thread { @Override public void run() { // 线程执行的代码逻辑 System.out.println("Thread is running..."); } public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); } }
实现Runnable接口
实现run方法。
创建实例后
创建Thread实例并传入上述实例
调用start方法
public class MyRunnable implements Runnable { @Override public void run() { // 线程执行的代码逻辑 System.out.println("Thread is running..."); } public static void main(String[] args) { MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start(); } }
ExecutorService
和Callable
:
-
ExecutorService
是 Java 中的线程池接口,可以通过它来管理和执行线程任务。 -
Callable
类似于Runnable
,但它可以返回结果。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class MyCallable implements Callable<String> { @Override public String call() throws Exception { // 线程执行的代码逻辑 return "Thread result"; } public static void main(String[] args) throws Exception { // 创建固定大小的线程池 ExecutorService executor = Executors.newFixedThreadPool(2); // 提交 Callable 任务 Future<String> future = executor.submit(new MyCallable()); // 获取任务结果 String result = future.get(); System.out.println("Thread result: " + result); // 关闭线程池 executor.shutdown(); } }
Spring
框架的@Async
注解:
-
Spring
框架提供了@Async
注解,可以方便地将方法标记为异步执行。
Spring 容器启动时,会扫描带有@Async
异步任务执行器,如ThreadPoolTaskExecutor
、SimpleAsyncTaskExecutor
等
使用线程池来管理线程
import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; @Component public class MyAsyncComponent { @Async public void asyncMethod() { // 异步执行的代码逻辑 System.out.println("Async method is running..."); } public static void main(String[] args) { MyAsyncComponent component = new MyAsyncComponent(); component.asyncMethod(); } }
线程池合理配置
-
任务类型:分析任务的性质,是 CPU 密集型还是 I/O 密集型。
-
CPU 密集型任务需要更多的 CPU 资源:CPU 核心数或略小于 CPU 核心数
-
I/O 密集型任务可能会因为 I/O 操作而阻塞线程:适当增加线程池的大小
-
-
任务数量:估计同时需要执行的任务数量。如果任务数量较多,可能需要较大的线程池来提高并发处理能力。
-
系统资源:考虑系统的硬件资源,如 CPU 核心数、内存限制等。线程池的大小应该与系统资源相匹配,避免过度消耗资源。
-
响应时间要求:根据任务的响应时间要求来调整线程池的配置。如果需要快速响应,可以适当增加线程数量。
-
任务执行时间:了解任务的平均执行时间,以便合理设置线程池的大小和任务队列的容量。
-
线程池类型:根据具体需求选择合适的线程池类型,如固定大小线程池、可扩展线程池或定时线程池等。
建议
-
可以使用
ThreadPoolExecutor
类来创建线程池,并根据需要设置核心线程数、最大线程数、任务队列容量等参数。 -
监控线程池的指标,如线程池的活跃度、任务队列的长度等,根据实际情况进行调整。
-
进行性能测试和调优,通过实际运行场景来验证线程池的配置是否合理,并根据测试结果进行优化。
多线程的状态以及状态之间的转换关系
线程状态可以分为 3 态或 5 态,具体取决于不同的分类方式。
3 态模型通常将线程状态分为:就绪态、运行态和阻塞态。这种分类方式比较简单,主要关注线程是否可执行以及是否被阻塞。
5 态模型则将线程状态进一步细分,包括:新建态、就绪态、运行态、阻塞态和死亡态。这种分类方式更详细地描述了线程的生命周期和状态转换。
-
新建状态(New):当创建一个新的线程对象时,线程处于新建状态。此时,线程尚未开始执行。
-
就绪状态(Runnable):当调用线程的
start()
方法后,线程进入就绪状态。处于就绪状态的线程已经具备了执行的条件,但尚未被分配 CPU 时间片,需要等待系统调度。 -
运行状态(Running):当线程获得 CPU 时间片并开始执行时,线程进入运行状态。在运行状态下,线程执行其任务。
-
阻塞状态(Blocked):线程可能由于等待某些资源或条件而进入阻塞状态。例如,线程可能在等待输入/输出操作完成、等待获取锁或等待其他线程的通知。处于阻塞状态的线程不会占用 CPU 资源。
-
死亡状态(Dead):当线程执行完毕或被强制终止时,线程进入死亡状态。处于死亡状态的线程不再可执行。
线程状态之间的转换关系如下:
-
新建线程通过调用
start()
方法进入就绪状态。 -
就绪状态的线程在获得 CPU 时间片后进入运行状态。
-
运行状态的线程可能由于以下原因进入阻塞状态:
-
等待资源或条件,例如等待输入/输出操作完成。
-
调用
sleep()
方法或其他阻塞方法。 -
等待获取锁。
-
-
阻塞状态的线程在等待的资源或条件满足后,重新进入就绪状态。
-
运行状态的线程执行完毕或被强制终止后,进入死亡状态。
实现线程同步的方式
-
synchronized 关键字:可以修饰方法或代码块,确保在同一时刻只有一个线程能执行被修饰的代码。
-
ReentrantLock类:提供了更灵活的锁机制,可以实现公平锁(排队)和非公平锁(随机)。
synchronized关键字在Java中的实现原理
通过 Monitor 锁来实现同步。Monitor 是一种互斥锁,它可以被线程获取和释放。
具体来说,当一个线程进入synchronized
代码块或方法时,它会尝试获取对象的 Monitor 锁。如果锁可用,线程将获得锁并执行同步代码;否则,线程将被阻塞,直到锁被释放。
在获取锁的过程中,线程会进行一些操作,例如检查对象头中的锁状态、修改锁状态等。这些操作是通过底层的操作系统或硬件支持来实现的,以确保线程之间的同步和互斥。
可重入锁,当前线程持有锁,可以重复进入。
volatile关键字的作用,并解释什么是可见性和有序性
用于修饰变量
-
保证可见性:当一个线程修改了一个
volatile
变量的值,其他线程能够立即看到这个修改。 -
禁止指令重排序:
volatile
关键字可以防止编译器和处理器对代码进行重排序,从而保证代码的执行顺序与预期一致。
原理
-
线程 1 将修改后的值写入到自己的工作内存中。
-
线程 1 向主内存发送写入信号,通知其他线程
A
的值已经被修改。 -
内存屏障确保其他线程在读取
A
之前,必须先从主内存中获取最新的值。 -
当线程 2 读取
volatile
变量A
时,它会直接从主内存中获取最新的值,而不是从自己的工作内存中读取可能已经过期的数据
不能保证原子性
单个事务可以多线程执行吗
设计模式
说说单例模式,在设计单例模式时,如何防止反射或反序列化攻击
单例模式:当某个类,要求只能存在1个实例时,主要是为了
-
全局访问同个实例,不用到处创建,浪费资源(如数据库连接、文件句柄)
-
配置信息存储:如果应用程序需要存储全局的配置信息
-
状态的共享和同步
-
日志记录:只有一个实例来记录日志,避免多个实例之间的日志重复和混乱。
单例实现方式
-
正常
-
私有化:私有化构造函数
-
静态方法:通过静态方法创建1个实例
-
延迟加载:在第一次使用时创建实例
-
线程安全:在多线程场景中,需保证线程安全
-
-
枚举:枚举是天然的单例和线程安全,不能通过反射机制创建,也不能反序列化
防止反射:在私有构造函数中,增加“实例是否存在”的判断
防止反序列化攻击:重写readResolve方法,返回唯一的实例
-
默认的反序列化,会创建新实例
-
readResolve
方法是ObjectInputStream
在反序列化过程中调用的一个特殊方法,用于在反序列化完成后进行额外的处理
策略模式什么情况下使用
例如,在一个电商系统中,可能需要根据不同的支付方式(如信用卡、支付宝、微信支付等)来处理支付。可以使用策略模式将不同的支付方式封装在不同的策略类中,然后在运行时根据用户选择的支付方式来选择相应的策略类进行支付处理。
-
当需要在运行时动态地选择算法:策略模式允许在运行时根据不同的条件选择不同的算法。这对于需要根据用户输入、系统状态或其他因素来改变算法的情况非常有用。
-
当存在多个相关的算法变体:如果一个类有多个相关的算法变体,使用策略模式可以将这些算法封装在不同的策略类中,使代码更具可读性和可维护性。
-
当算法的实现细节可能会发生变化:如果算法的实现细节可能会发生变化,使用策略模式可以将算法的实现与使用它的代码分离,从而更容易进行修改和扩展。
-
当需要避免大量的条件语句:如果一个类中有大量的条件语句来选择不同的算法,使用策略模式可以将这些条件语句替换为对策略对象的调用,使代码更简洁和易于理解。
-
当需要提高代码的可扩展性:使用策略模式可以使代码更容易扩展,因为可以添加新的策略类来实现新的算法,而不需要修改现有的代码。
设计模式,并提供一些具体的设计模式示例
有 23 种设计模式,它们可以分为三大类:创建型模式、结构型模式和行为型模式
-
创建型模式(Creational Patterns):
-
工厂模式(Factory Pattern):通过工厂类创建对象,隐藏具体实现细节。例如,java.util.Calendar 类的 getInstance() 方法。
-
单例模式(Singleton Pattern):确保一个类只有一个实例,并提供全局访问点。例如,java.lang.Runtime 类。
-
原型模式(Prototype Pattern):通过复制现有对象来创建新对象。例如,java.lang.Object 类的 clone() 方法。
-
-
结构型模式(Structural Patterns):
-
适配器模式(Adapter Pattern):将一个类的接口转换为另一个类所期望的接口。例如,java.util.Arrays 类的 asList() 方法。
-
装饰器模式(Decorator Pattern):动态地给对象添加额外的功能。例如,java.io.BufferedReader 类。
-
代理模式(Proxy Pattern):为其他对象提供一个代理,以控制对该对象的访问。例如,java.lang.reflect.Proxy 类。
-
-
行为型模式(Behavioral Patterns):
-
观察者模式(Observer Pattern):定义对象间的一对多依赖关系,当一个对象状态改变时,其相关对象会收到通知并自动更新。例如,java.util.Observable 类和 java.util.Observer 接口。
-
策略模式(Strategy Pattern):定义一系列算法,将每个算法封装起来,并使它们可以互换。例如,java.util.Comparator 接口。
-
命令模式(Command Pattern):将请求封装为对象,使得可以用不同的请求对客户进行参数化。例如,java.util.concurrent.Callable 接口。
-
设计模式及应用场景
-
单例模式(Singleton Pattern):确保一个类只有一个实例,并提供全局访问点。应用场景包括日志记录、数据库连接池等。
-
应用场景:在电商项目中,数据库连接池是一个关键资源。使用单例模式可以确保只有一个数据库连接池实例存在,避免多个实例导致的资源竞争和浪费。
-
具体实现:创建一个私有构造函数,防止外部创建实例。提供一个公共的静态方法来获取唯一的实例,并在需要时初始化连接池。
-
-
工厂模式(Factory Pattern):将对象的创建与使用分离,以便更好地管理对象的创建过程。应用场景包括创建复杂对象、框架中的对象创建等。
-
应用场景:当创建订单时,可能有多种不同类型的订单,如普通订单、促销订单、团购订单等。使用工厂模式可以根据订单类型创建相应的订单对象。
-
具体实现:创建一个抽象工厂类,定义创建订单的接口。针对不同类型的订单,创建具体的工厂类来实现该接口,并在其中创建相应的订单对象。
-
-
建造者模式(Builder Pattern):用于构建复杂对象,将对象的构建过程分解为多个步骤。应用场景包括构建具有多个属性的对象。
-
应用场景:构建复杂的商品对象时,商品可能有多个属性,如名称、价格、描述、图片等。使用建造者模式可以将商品的构建过程分解为多个步骤,使代码更清晰、易读。
-
具体实现:创建一个商品建造者类,定义设置商品属性的方法。在创建商品时,使用建造者类逐步设置属性,最后通过构建方法获取完整的商品对象。
-
-
适配器模式(Adapter Pattern):将不兼容的接口转换为兼容的接口,使原本不兼容的类可以协同工作。应用场景包括系统集成、旧代码复用等。
-
应用场景:与外部支付系统集成时,支付系统的接口可能与项目内部的接口不兼容。使用适配器模式可以将外部支付系统的接口转换为项目内部使用的接口。
-
具体实现:创建一个适配器类,实现项目内部的接口,并在其中调用外部支付系统的接口进行适配。
-
-
装饰模式(Decorator Pattern):动态地为对象添加额外的功能,而不改变其基本结构。应用场景包括对现有功能的扩展、动态添加日志记录等。
-
应用场景:在订单处理过程中,可能需要为订单添加额外的功能,如优惠券应用、积分计算等。使用装饰模式可以动态地为订单添加这些功能。
-
具体实现:创建一个订单抽象类,定义订单的基本操作。针对不同的功能,创建具体的装饰类来扩展订单的功能。在处理订单时,通过装饰类的引用调用相应的功能。
-
-
外观模式(Facade Pattern):为复杂的子系统提供一个简单的接口,隐藏子系统的复杂性。应用场景包括系统的封装、提供简单易用的 API 等。
-
应用场景:提供一个简单的接口给外部系统调用,隐藏内部复杂的业务逻辑。
-
具体实现:创建一个外观类,提供一组简单的方法来处理复杂的业务逻辑。外部系统只需要调用外观类的方法,而不需要了解内部的具体实现。
-
-
观察者模式(Observer Pattern):定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。应用场景包括事件驱动系统、MVC 架构等。
-
应用场景:当订单状态发生变化时,需要通知相关的模块进行相应的处理。
-
具体实现:创建一个抽象的观察者类,定义接收通知的方法。针对不同的观察者,创建具体的观察者类来实现该抽象类。在订单状态发生变化时,通知所有注册的观察者。
-
-
原型模式(Prototype Pattern):通过复制现有对象来创建新对象,提高对象创建的效率。应用场景包括对象的克隆、避免重复创建相似对象等。
-
应用场景:在用户购物车中,可能需要频繁地复制购物车对象。使用原型模式可以通过复制现有购物车对象来创建新的购物车,提高效率。
-
具体实现:实现 Cloneable 接口,并在 clone() 方法中复制购物车的属性。在需要创建新购物车时,调用 clone() 方法复制现有购物车。
-
-
桥接模式(Bridge Pattern):将抽象部分与实现部分分离,使它们可以独立变化。应用场景包括跨平台应用、不同数据库的连接等。
-
应用场景:处理不同支付方式时,支付方式的抽象与具体实现可能会发生变化。使用桥接模式可以将支付方式的抽象与具体实现分离,使它们可以独立变化。
-
具体实现:创建一个支付方式的抽象类,定义支付的基本操作。针对不同的支付方式,创建具体的支付方式类来实现该抽象类。在支付时,通过抽象类的引用调用具体支付方式的实现。
-
-
组合模式(Composite Pattern):将对象组合成树形结构,以表示部分-整体的层次结构。应用场景包括文件系统、图形界面组件等。
-
应用场景:商品分类通常具有层次结构,使用组合模式可以将商品分类组织成树形结构,方便遍历和管理。
-
具体实现:创建一个抽象的商品分类组件类,包含添加、删除子组件的方法。针对具体的商品分类,创建叶子节点和组合节点类来实现该组件类。
-
-
享元模式(Flyweight Pattern):通过共享对象来减少内存的使用,提高性能。应用场景包括大量相似对象的创建、缓存等。
-
应用场景:处理商品图片时,相同的图片可能会被多次使用。使用享元模式可以共享相同的图片资源,减少内存消耗。
-
具体实现:创建一个图片享元工厂类,维护一个图片对象的池。在需要使用图片时,从池中获取或创建图片对象,并在使用后返回池中。
-
-
代理模式(Proxy Pattern):为其他对象提供一个代理,以控制对该对象的访问。应用场景包括远程代理、虚拟代理等。
-
应用场景:在访问用户信息时,可能需要控制访问权限。使用代理模式可以在访问用户信息之前进行权限检查。
-
具体实现:创建一个用户信息代理类,实现与用户信息相同的接口。在代理类中进行权限检查,并在有权限时调用真实的用户信息类进行操作。
-
-
责任链模式(Chain of Responsibility Pattern):将请求的发送者和接收者解耦,使多个对象都有机会处理请求。应用场景包括审批流程、事件处理等。
-
应用场景:处理订单退款时,可能需要经过多个审批环节。使用责任链模式可以定义不同的审批者,并将它们组织成链,依次处理退款请求。
-
具体实现:创建一个抽象的审批者类,包含处理请求和设置下一个审批者的方法。针对不同的审批者,创建具体的审批者类来实现该抽象类。在处理退款请求时,通过责任链依次传递请求,直到有审批者处理或拒绝。
-
-
命令模式(Command Pattern):将请求封装为对象,以便支持撤销、重做等操作。应用场景包括命令行工具、图形界面的操作等。
-
应用场景:将用户的操作封装为命令,方便进行撤销、重做等操作。
-
具体实现:创建一个命令接口,定义执行和撤销操作的方法。针对不同的操作,创建具体的命令类来实现该接口。在执行操作时,创建相应的命令对象并执行,同时将命令对象存储在历史记录中,以便进行撤销和重做。
-
-
迭代器模式(Iterator Pattern):提供一种顺序访问聚合对象元素的方法,而不需要暴露聚合对象的内部表示。应用场景包括遍历集合、迭代器的实现等。
-
应用场景:遍历商品列表时,需要提供一种统一的遍历方式,而不暴露列表的内部结构。
-
具体实现:创建一个迭代器接口,定义遍历元素的方法。针对商品列表,创建具体的迭代器类来实现该接口。在遍历商品列表时,通过迭代器的引用进行遍历。
-
单例模式(Singleton Pattern):确保一个类只有一个实例存在
public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }
工厂模式(Factory Pattern):定义一个创建对象的接口,但让子类决定实例化哪个类
public interface Shape { void draw(); } public class Circle implements Shape { @Override public void draw() { System.out.println("Drawing a circle"); } } public class Square implements Shape { @Override public void draw() { System.out.println("Drawing a square"); } } public class ShapeFactory { public Shape createShape(String shapeType) { if (shapeType.equalsIgnoreCase("circle")) { return new Circle(); } else if (shapeType.equalsIgnoreCase("square")) { return new Square(); } else { throw new IllegalArgumentException("Invalid shape type"); } } }
观察者模式(Observer Pattern):定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。 示例:
public interface Observer { void update(String message); } public class ConcreteObserver implements Observer { @Override public void update(String message) { System.out.println("Received message: " + message); } } public class Subject { private List<Observer> observers = new ArrayList<>(); public void attach(Observer observer) { observers.add(observer); } public void detach(Observer observer) { observers.remove(observer); } public void notifyObservers(String message) { for (Observer observer : observers) { observer.update(message); } } }
装饰器模式(Decorator Pattern):动态地给一个对象添加一些额外的职责。
public interface Component { void operation(); } public class ConcreteComponent implements Component { @Override public void operation() { System.out.println("Executing operation in ConcreteComponent"); } } public abstract class Decorator implements Component { protected Component component; public Decorator(Component component) { this.component = component; } @Override public void operation() { component.operation(); } } public class ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component component) { super(component); } @Override public void operation() { super.operation(); System.out.println("Executing additional operation in ConcreteDecoratorA"); } }
策略模式(Strategy Pattern):定义一系列算法,将每个算法封装起来,并使它们可以相互替换。
public interface Strategy { void execute(); } public class ConcreteStrategyA implements Strategy { @Override public void execute() { System.out.println("Executing strategy A"); } } public class ConcreteStrategyB implements Strategy { @Override public void execute() { System.out.println("Executing strategy B"); } } public class Context { private Strategy strategy; public Context(Strategy strategy) { this.strategy = strategy; } public void setStrategy(Strategy strategy) { this.strategy = strategy; } public void executeStrategy() { strategy.execute(); } }
模板方法模式(Template Method Pattern):定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。
public abstract class AbstractClass { public void templateMethod() { primitiveOperation1(); abstractOperation(); primitiveOperation2(); } protected void primitiveOperation1() { System.out.println("Executing primitive operation 1"); } protected abstract void abstractOperation(); protected void primitiveOperation2() { System.out.println("Executing primitive operation 2"); } } public class ConcreteClass extends AbstractClass { @Override protected void abstractOperation() { System.out.println("Executing abstract operation in ConcreteClass"); } }
迭代器模式(Iterator Pattern):提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
public interface Iterator<E> { boolean hasNext(); E next(); } public interface Aggregate<E> { Iterator<E> iterator(); } public class ConcreteAggregate implements Aggregate<Integer> { private List<Integer> items = new ArrayList<>(); public void addItem(Integer item) { items.add(item); } @Override public Iterator<Integer> iterator() { return new ConcreteIterator(this); } private class ConcreteIterator implements Iterator<Integer> { private int currentIndex = 0; private ConcreteAggregate aggregate; public ConcreteIterator(ConcreteAggregate aggregate) { this.aggregate = aggregate; } @Override public boolean hasNext() { return currentIndex < aggregate.items.size(); } @Override public Integer next() { if (hasNext()) { return aggregate.items.get(currentIndex++); } else { throw new NoSuchElementException(); } } } }