死锁指的是两个或多个线程在执行期间,因争夺资源而出现互相等待的状况,致使这些线程无法继续执行。为避免死锁,可从以下方面着手:
java
// 定义两个锁
Object lock1 = new Object();
Object lock2 = new Object();
// 线程 A
Thread threadA = new Thread(() -> {
synchronized (lock1) {
System.out.println("Thread A acquired lock1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("Thread A acquired lock2");
}
}
});
// 线程 B
Thread threadB = new Thread(() -> {
synchronized (lock1) {
System.out.println("Thread B acquired lock1");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("Thread B acquired lock2");
}
}
});
threadA.start();
threadB.start();
ReentrantLock
的 tryLock(long timeout, TimeUnit unit)
方法。若在规定时间内未获取到锁,线程可放弃,避免无限期等待。示例代码如下:java
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TimeLimitedLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void doSomething() {
try {
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
// 执行需要加锁的操作
System.out.println("Lock acquired, doing something...");
} finally {
lock.unlock();
}
} else {
System.out.println("Failed to acquire lock within 1 second.");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) {
TimeLimitedLockExample example = new TimeLimitedLockExample();
example.doSomething();
}
}
java
public class ReduceLockHoldingTime {
private final Object lock = new Object();
public void process() {
// 不需要加锁的操作
System.out.println("Doing some non - locked work...");
synchronized (lock) {
// 需要加锁的操作
System.out.println("Acquired lock, doing locked work...");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 不需要加锁的操作
System.out.println("Doing more non - locked work...");
}
public static void main(String[] args) {
ReduceLockHoldingTime example = new ReduceLockHoldingTime();
example.process();
}
}
原理
死锁的产生需满足四个必要条件:互斥条件、请求和保持条件、不剥夺条件和循环等待条件。避免死锁就是要破坏其中一个或多个条件。按顺序加锁和资源分级破坏了循环等待条件;限时加锁破坏了不剥夺条件;减少锁的持有时间可降低死锁发生概率。
可深入了解死锁的检测和恢复机制,如使用 jstack
工具检测死锁,以及在检测到死锁后如何进行恢复操作。还可研究分布式系统中的死锁问题及解决办法。
好处
问题
多线程借助操作系统的多任务处理能力,把一个程序分解为多个可并行执行的线程,以此提高程序执行效率。但多个线程同时访问共享资源时,若没有正确的同步机制,就会出现数据不一致问题。线程切换需操作系统调度,会消耗一定时间和资源。
可了解多线程的同步机制,如 synchronized
关键字、Lock
接口等,以及如何运用这些机制解决多线程问题。还可研究线程池的使用,更好地管理线程资源。
synchronized
关键字、Lock
接口、Atomic
类等。示例代码如下:java
import java.util.concurrent.atomic.AtomicInteger;
// 使用 AtomicInteger 保证线程安全
public class SharedVariableExample {
private static AtomicInteger counter = new AtomicInteger(0);
public static void increment() {
counter.incrementAndGet();
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
increment();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("Counter value: " + counter.get());
}
}
volatile
关键字保证变量的可见性。示例代码如下:java
public class VolatileExample {
private static volatile boolean flag = false;
public static void main(String[] args) {
Thread writer = new Thread(() -> {
flag = true;
System.out.println("Flag is set to true");
});
Thread reader = new Thread(() -> {
while (!flag) {
// 等待 flag 变为 true
}
System.out.println("Flag is now true");
});
reader.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
writer.start();
}
}
多个线程同时访问和修改共享数据变量时,若没有正确的同步机制,就会出现数据不一致问题。synchronized
关键字和 Lock
接口能保证同一时间只有一个线程可访问共享数据变量,从而保证数据一致性。volatile
关键字可保证变量的可见性,即一个线程对变量的修改会立即刷新到主内存中,其他线程能立即看到。
volatile
关键字。可深入了解并发容器,如 ConcurrentHashMap
、CopyOnWriteArrayList
等,这些容器在多线程环境下可安全使用,无需额外的同步机制。
线程池是一种线程管理机制,它预先创建一定数量的线程,当有任务提交时,从线程池中获取一个空闲线程来执行任务。任务执行完毕后,线程不会销毁,而是返回线程池等待下一个任务。
线程池的核心是一个线程集合和一个任务队列。线程池初始化时会创建一定数量的线程,这些线程会不断从任务队列中获取任务并执行。当任务队列中有任务时,线程会立即执行;当任务队列中没有任务时,线程会进入等待状态。
可了解 ThreadPoolExecutor
类的使用,它是 Java 中线程池的核心实现类,通过配置不同参数可实现不同类型的线程池。示例代码如下:
java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
// 创建一个固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " is being executed by " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Task " + taskId + " is completed.");
});
}
// 关闭线程池
executor.shutdown();
}
}
Spring 是一个轻量级的 Java 开发框架,提供了 IoC(控制反转)和 AOP(面向切面编程)等功能,有助于开发者更高效地构建企业级应用。
IoC 通过依赖注入(DI)实现,Spring 容器负责创建和管理对象,并将对象间的依赖关系注入到对象中。AOP 通过代理模式实现,Spring 会在目标对象的方法执行前后插入额外代码,实现对目标对象的增强。
可深入了解 Spring 的各种模块,如 Spring MVC、Spring Boot 等,以及它们的使用场景和优势。例如,Spring Boot 可帮助开发者快速搭建 Spring 应用,减少配置工作。
BeanFactory
在需要获取 Bean 时才加载和实例化 Bean;而 ApplicationContext
在容器启动时就会加载和实例化所有的单例 Bean。ApplicationContext
除具备 BeanFactory
的基本功能外,还提供更多企业级功能,如国际化支持、事件发布、资源加载等。例如,使用 ApplicationContext
可方便地实现多语言支持,通过发布和监听事件实现组件间的通信。BeanFactory
适用于资源有限、对内存和性能要求较高的环境;ApplicationContext
适用于大多数企业级应用开发,提供更丰富的功能和更好的开发体验。BeanFactory
是 Spring 框架的基础接口,定义了 Bean 的基本操作方法。ApplicationContext
是 BeanFactory
的子接口,继承其功能并进行了扩展。
ApplicationContext
提前加载单例 Bean。ApplicationContext
功能更丰富。可了解 ApplicationContext
的不同实现类,如 ClassPathXmlApplicationContext
、FileSystemXmlApplicationContext
等,以及它们的使用方法。
Spring Bean 的生命周期包含以下几个阶段:
InitializingBean
接口的 afterPropertiesSet()
方法或自定义的初始化方法。示例代码如下:java
import org.springframework.beans.factory.InitializingBean;
public class MyBean implements InitializingBean {
private String name;
public void setName(String name) {
this.name = name;
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Initializing MyBean with name: " + name);
}
}
DisposableBean
接口的 destroy()
方法或自定义的销毁方法。示例代码如下:java
import org.springframework.beans.factory.DisposableBean;
public class MyDisposableBean implements DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("Destroying MyDisposableBean");
}
}
Spring 容器负责管理 Bean 的生命周期,通过反射机制创建 Bean 的实例,并使用依赖注入的方式为 Bean 的属性赋值。在 Bean 的初始化和销毁阶段,Spring 会调用相应的方法,开发者可在这些方法中进行初始化和清理工作。
可深入了解 Spring 的 Bean 后置处理器,它能在 Bean 生命周期的各个阶段进行干预,实现更复杂的功能。
在 Spring 中,事务的实现方式主要有两种:
TransactionTemplate
或 PlatformTransactionManager
实现。示例代码如下:java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
@Service
public class ProgrammaticTransactionService {
@Autowired
private PlatformTransactionManager transactionManager;
public void doTransaction() {
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 执行数据库操作
System.out.println("Doing database operations...");
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
}
}
}
@Transactional
。示例代码如下:java
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class DeclarativeTransactionService {
@Transactional
public void doTransaction() {
// 执行数据库操作
System.out.println("Doing database operations...");
}
}
编程式事务管理通过手动调用事务管理器的方法控制事务流程。声明式事务管理通过 AOP 实现,Spring 会在目标方法执行前后插入事务管理代码,实现对事务的自动管理。
可了解不同的事务管理器,如 DataSourceTransactionManager
、JtaTransactionManager
等,以及它们的使用场景。
事务的传播级别定义了一个事务方法调用另一个事务方法时,事务的行为方式。Spring 提供了 7 种事务传播级别:
事务的传播级别通过事务管理器实现,事务管理器会根据传播级别的定义决定如何处理事务。
要点
可深入了解嵌套事务的实现原理和使用场景,以及在不同数据库中的支持情况。
事务嵌套失效通常由以下原因导致:
@Transactional
注解中指定需要回滚的异常类型。PROPAGATION_NOT_SUPPORTED
传播级别,会以非事务方式执行,即使在事务方法中调用也不会生效。需根据业务需求正确设置传播级别。Spring 的事务管理通过 AOP 代理实现,只有通过代理对象调用事务方法,事务才会生效。事务的回滚基于异常触发,若没有异常抛出,事务会正常提交。
可了解如何通过 AOP 代理解决同一个类中方法调用事务失效的问题,以及如何使用 @Transactional
注解的 rollbackFor
属性指定需要回滚的异常类型。
友情提示:本文已经整理成文档,可以到如下链接免积分下载阅读
https://download.csdn.net/download/ylfhpy/90516132