目录
生命周期
线程安全
生命周期
在Java中,线程的生命周期可以分为以下几个状态:
-
新建状态(New): 当线程对象被创建后,它就处于新建状态。此时线程对象已经在内存中了,但是还没有开始执行。
-
就绪状态(Runnable): 当调用线程的
start()
方法后,线程进入就绪状态。处于就绪状态的线程已经具备了运行的条件,等待系统调度执行。 -
运行状态(Running): 当线程获得了CPU资源后,它就进入了运行状态,开始执行
run()
方法中的代码。 -
阻塞状态(Blocked): 在某些情况下,线程可能会进入阻塞状态,暂时失去CPU执行权。常见的情况包括等待I/O操作的完成、等待获取锁或者同步块的许可等。
-
等待状态(Waiting): 线程处于等待状态时,它需要等待其他线程的通知或者某个条件的满足。可以通过
wait()
、join()
等方法使线程进入等待状态。 -
超时等待状态(Timed Waiting): 与等待状态类似,但是可以指定一个超时时间,在超时时间之后线程会自动恢复到就绪状态。可以通过
sleep()
、join()
等方法使线程进入超时等待状态。 -
终止状态(Terminated): 线程执行完
run()
方法中的代码,或者因异常而结束时,线程进入终止状态。一旦线程进入终止状态,它就不能再次运行了。
线程的状态转换如下:
- 新建状态 -> 就绪状态:调用
start()
方法; - 就绪状态 -> 运行状态:获取CPU资源;
- 运行状态 -> 阻塞状态:等待某个条件的满足;
- 阻塞状态 -> 就绪状态:条件满足,重新获得CPU资源;
- 运行状态 -> 等待状态或超时等待状态:调用
wait()
、join()
等方法; - 等待状态或超时等待状态 -> 就绪状态:其他线程通知或超时时间到;
- 运行状态 -> 终止状态:
run()
方法执行完毕或者抛出异常。
线程安全
线程安全是指在多线程环境中,对共享资源的访问不会导致数据的不一致或者程序的异常行为。在编写多线程程序时,需要特别注意确保线程安全,以避免出现数据竞争、死锁、活锁等问题。
以下是几种保证线程安全的常用方法:
- 使用同步方法或同步代码块: 可以使用
synchronized
关键字来保证在同一时刻只有一个线程可以执行某个方法或代码块,从而避免多个线程同时修改共享资源导致的数据不一致问题。
public synchronized void synchronizedMethod() {
// 同步方法体
}
public void synchronizedBlock() {
synchronized(this) {
// 同步代码块
}
}
- 使用锁: Java提供了
Lock
接口及其实现类(如ReentrantLock
),可以通过加锁和解锁来保证在同一时刻只有一个线程可以访问共享资源。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyThreadSafeClass {
private Lock lock = new ReentrantLock();
public void performOperation() {
lock.lock();
try {
// 访问共享资源的代码
} finally {
lock.unlock();
}
}
}
- 使用线程安全的数据结构: Java中提供了许多线程安全的数据结构,如
ConcurrentHashMap
、CopyOnWriteArrayList
等,它们内部实现了线程安全的机制,可以直接在多线程环境中使用。
import java.util.concurrent.ConcurrentHashMap;
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key", 1);
- 使用volatile关键字:
volatile
关键字用于修饰变量,保证变量的可见性,即当一个线程修改了volatile
变量的值后,其他线程可以立即看到最新的值,从而避免了由于线程间的缓存不一致而引起的问题。
private volatile int count;