从CRUD到并发高手(一):多线程基础知识全解析

156 阅读5分钟

从CRUD到并发高手(一):多线程基础知识全解析

前言

在开始深入学习Java并发编程之前,让我们先建立起对多线程的基础认知。本文将带您从最基础的概念开始,逐步理解Java多线程的核心内容。

1. 向大师致敬

1.1 Doug Lea

image.png

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):多个任务同时执行

image.png

4.2 三个核心概念

  1. 进程:独立的资源分配单位
  2. 线程:CPU调度的基本单位
  3. 管程(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多线程的基础知识,包括:

  1. 多线程编程的重要性
  2. 线程的实现原理
  3. 核心概念解析
  4. 用户线程与守护线程的区别

参考资料

  1. 《Java并发编程的艺术》
  2. OpenJDK源码
  3. 尚硅谷JUC并发编程