从CRUD到并发高手(一):多线程基础知识全解析
前言
在开始深入学习Java并发编程之前,让我们先建立起对多线程的基础认知。本文将带您从最基础的概念开始,逐步理解Java多线程的核心内容。
1. 向大师致敬
1.1 Doug Lea
Doug Lea是Java并发包(java.util.concurrent)的主要设计者和实现者。他不仅创造了众多优秀的并发工具类,还在其著作《Java Concurrent Programming: Design Principles and Patterns》中系统地阐述了Java并发编程的设计原则。
1.2 java.util.concurrent包
这个包含了Java并发编程中最常用的工具类,包括:
- 线程池(ThreadPoolExecutor)
- 并发集合(ConcurrentHashMap等)
- 同步工具(CountDownLatch、CyclicBarrier等)
- 阻塞队列(BlockingQueue的各种实现)
2. 为什么多线程如此重要?
2.1 硬件方面
摩尔定律逐渐失效,CPU主频提升遇到瓶颈,芯片厂商转向多核设计。为了充分利用多核CPU的性能,多线程编程变得越来越重要。
// 获取CPU核心数
int processors = Runtime.getRuntime().availableProcessors();
System.out.println("CPU核心数: " + processors);
2.2 软件方面
- 提高系统资源利用率
- 提升程序响应速度
- 增强用户体验
2.3 弊端及问题
- 线程上下文切换开销
- 死锁风险
- 线程安全问题
- 增加程序复杂度
3. 从start()一个线程说起
3.1 Java线程的实现
public class Thread implements Runnable {
private native void start0();
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
}
3.2 OpenJDK源码深度解析
在OpenJDK中,线程的实现位于以下位置:
src/share/native/java/lang/Thread.c
src/share/vm/prims/jvm.cpp
src/share/vm/runtime/thread.cpp
3.2.1 线程创建的本地实现
在Thread.c中,Java_java_lang_Thread_start0方法是线程启动的本地实现:
JNIEXPORT void JNICALL
Java_java_lang_Thread_start0(JNIEnv *env, jobject thread)
{
// 获取JVM内部的线程对象
JVM_StartThread(env, thread);
}
在jvm.cpp中,JVM_StartThread进一步实现了线程的创建:
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
// 确保线程对象非空
if (jthread == NULL) {
THROW(vmSymbols::java_lang_NullPointerException());
}
// 将Java线程对象转换为本地线程
JavaThread *native_thread = NULL;
// 创建新的Java线程
native_thread = new JavaThread(&thread_entry, sz);
// 启动线程
Thread::start(native_thread);
JVM_END
3.2.2 线程实现的核心结构
在thread.cpp中,定义了线程的核心结构:
class Thread: public ThreadShadow {
private:
// 线程状态
volatile int _thread_state;
// 线程优先级
int _priority;
// 是否是守护线程
bool _is_daemon;
public:
// 线程启动方法
static void start(Thread* thread) {
// 初始化线程状态
thread->initialize_thread_state();
// 设置线程优先级
thread->set_priority(NormPriority);
// 启动线程
os::start_thread(thread);
}
};
3.3 synchronized的底层实现
3.3.1 字节码层面
当我们使用synchronized时,会在字节码层面看到monitorenter和monitorexit指令:
public class SynchronizedDemo {
public synchronized void method() {
// 同步方法
}
public void blockMethod() {
synchronized(this) {
// 同步块
}
}
}
反编译后的字节码(使用javap -c):
public synchronized void method();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=0, locals=1, args_size=1
0: return
public void blockMethod();
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter // 进入同步块
4: aload_1
5: monitorexit // 退出同步块
6: goto 14
7: astore_2
8: aload_1
9: monitorexit // 异常时也要退出同步块
10: aload_2
11: athrow
3.3.2 Monitor机制
synchronized的实现依赖于Monitor机制,在HotSpot虚拟机中,Monitor是由ObjectMonitor实现的:
// ObjectMonitor.hpp
class ObjectMonitor {
private:
volatile markOop _header; // 对象头
volatile intptr_t _count; // 记录个数
volatile intptr_t _waiters; // 等待线程数
volatile Thread* _owner; // 持有锁的线程
public:
void enter(TRAPS); // monitorenter
void exit(TRAPS); // monitorexit
void wait(TRAPS); // wait()
void notify(TRAPS); // notify()
void notifyAll(TRAPS); // notifyAll()
};
3.3.3 锁的升级过程
synchronized实现了锁的自动升级,从偏向锁->轻量级锁->重量级锁:
// biasedLocking.cpp
BiasedLocking::Condition BiasedLocking::revoke_and_rebias() {
// 偏向锁撤销
markOop mark = object->mark();
if (mark->has_bias_pattern()) {
// 检查偏向锁
JavaThread* biased_thread = mark->biased_locker();
if (biased_thread == thread) {
// 当前线程已经获得偏向锁
return BIAS_REVOKED;
}
// 需要撤销偏向锁
if (biased_thread != NULL) {
// 升级为轻量级锁
markOop header = mark->displaced_mark_helper();
object->set_mark(header);
}
}
return NOT_BIASED;
}
3.4 实战案例:自定义同步工具
public class CustomLock {
private volatile int state = 0;
private Thread owner = null;
public synchronized void lock() throws InterruptedException {
while (state != 0) {
wait(); // 使用monitor的wait机制
}
state = 1;
owner = Thread.currentThread();
}
public synchronized void unlock() {
if (Thread.currentThread() == owner) {
state = 0;
owner = null;
notify(); // 唤醒等待的线程
}
}
}
// 使用示例
public class CustomLockDemo {
private static final CustomLock lock = new CustomLock();
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
lock.lock();
count++;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
t1.start();
t1.join();
System.out.println("Count: " + count);
}
}
4. Java多线程核心概念
4.1 并发vs并行
- 并发(Concurrent):多个任务交替执行
- 并行(Parallel):多个任务同时执行
4.2 三个核心概念
- 进程:独立的资源分配单位
- 线程:CPU调度的基本单位
- 管程(Monitor):Java中的锁实现机制
5. 用户线程vs守护线程
5.1 两种线程的区别
public class ThreadTypeDemo {
public static void main(String[] args) {
Thread userThread = new Thread(() -> {
while (true) {
System.out.println("用户线程运行中...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread daemonThread = new Thread(() -> {
while (true) {
System.out.println("守护线程运行中...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
daemonThread.setDaemon(true); // 设置为守护线程
userThread.start();
daemonThread.start();
}
}
5.2 守护线程的特点
- 主要用于服务用户线程
- JVM会等待用户线程执行完毕
- 不会等待守护线程执行完毕
5.3 实际应用场景
- 垃圾回收器线程
- JIT编译线程
- 监控统计线程
总结
本文介绍了Java多线程的基础知识,包括:
- 多线程编程的重要性
- 线程的实现原理
- 核心概念解析
- 用户线程与守护线程的区别
参考资料
- 《Java并发编程的艺术》
- OpenJDK源码
- 尚硅谷JUC并发编程