Java从零到熟练(九):并发编程基础

并发编程让程序同时处理多个任务,是现代应用开发的必备技能。

目录

  • [1. 线程基础](#1. 线程基础)
  • [2. 线程同步](#2. 线程同步)
  • [3. 线程间通信](#3. 线程间通信)
  • [4. 并发集合](#4. 并发集合)
  • [5. 线程池](#5. 线程池)
  • [6. 原子类](#6. 原子类)
  • [7. 实战案例](#7. 实战案例)
  • [8. 总结](#8. 总结)
  • 参考资源

1. 线程基础

1.1 创建线程

方式一:实现Runnable接口(推荐)

java 复制代码
public class MyRunnable implements Runnable {
    private String name;
    
    public MyRunnable(String name) {
        this.name = name;
    }
    
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + ":第" + (i + 1) + "次执行");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyRunnable("线程A"));
        Thread t2 = new Thread(new MyRunnable("线程B"));
        
        t1.start();
        t2.start();
    }
}

方式二:Lambda表达式(Java 8+)

java 复制代码
public class LambdaThreadDemo {
    public static void main(String[] args) {
        Runnable task = () -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("当前线程:" + Thread.currentThread().getName());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        
        Thread t1 = new Thread(task, "线程A");
        Thread t2 = new Thread(task, "线程B");
        
        t1.start();
        t2.start();
    }
}

1.2 线程生命周期

复制代码
新建 → 就绪 → 运行 → 死亡
         ↓
        阻塞

2. 线程同步

2.1 synchronized关键字

java 复制代码
public class SafeCounter {
    private int count = 0;
    
    // 同步方法
    public synchronized void increment() {
        count++;
    }
    
    public synchronized int getCount() {
        return count;
    }
}

2.2 Lock接口

java 复制代码
import java.util.concurrent.locks.*;

public class LockCounter {
    private int count = 0;
    private final ReentrantLock lock = new ReentrantLock();
    
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();  // 必须在finally中释放锁
        }
    }
    
    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

2.3 synchronized vs Lock

特性 synchronized Lock
实现 JVM内置 Java API
释放锁 自动 手动unlock
可中断 不可 可中断
超时获取 不可 可超时
公平锁 不可 可选

3. 线程间通信

3.1 wait/notify

java 复制代码
public class ProducerConsumer {
    private final int[] buffer = new int[10];
    private int count = 0;
    private final Object lock = new Object();
    
    // 生产者
    public void produce(int item) throws InterruptedException {
        synchronized (lock) {
            while (count == buffer.length) {
                lock.wait();
            }
            buffer[count++] = item;
            System.out.println("生产:" + item + ",缓冲区大小:" + count);
            lock.notifyAll();
        }
    }
    
    // 消费者
    public int consume() throws InterruptedException {
        synchronized (lock) {
            while (count == 0) {
                lock.wait();
            }
            int item = buffer[--count];
            System.out.println("消费:" + item + ",缓冲区大小:" + count);
            lock.notifyAll();
            return item;
        }
    }
}

4. 并发集合

java 复制代码
import java.util.concurrent.*;

public class ConcurrentCollectionDemo {
    public static void main(String[] args) {
        // ConcurrentHashMap
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        map.put("A", 1);
        map.putIfAbsent("A", 2);
        System.out.println("ConcurrentHashMap:" + map);
        
        // ConcurrentLinkedQueue
        ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
        queue.offer("A");
        queue.offer("B");
        System.out.println("队列:" + queue);
    }
}

5. 线程池

java 复制代码
import java.util.concurrent.*;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);
        
        // 提交任务
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("任务" + taskId + "在" + 
                    Thread.currentThread().getName() + "上执行");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        
        // 关闭线程池
        executor.shutdown();
        try {
            executor.awaitTermination(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("所有任务完成");
    }
}

常用线程池

java 复制代码
// 固定大小线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(3);

// 缓存线程池(按需创建)
ExecutorService cachedPool = Executors.newCachedThreadPool();

// 单线程池
ExecutorService singlePool = Executors.newSingleThreadExecutor();

// 定时线程池
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(2);

6. 原子类

java 复制代码
import java.util.concurrent.atomic.*;

public class AtomicDemo {
    private static AtomicInteger counter = new AtomicInteger(0);
    
    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            for (int i = 0; i < 10000; i++) {
                counter.incrementAndGet();
            }
        };
        
        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        
        t1.start();
        t2.start();
        
        t1.join();
        t2.join();
        
        System.out.println("期望值:20000");
        System.out.println("实际值:" + counter.get());
        // 一定是20000
    }
}

7. 实战案例

7.1 线程安全的缓存

java 复制代码
import java.util.concurrent.*;
import java.util.function.Function;

public class ThreadSafeCache<K, V> {
    private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>();
    private final Function<K, V> loader;
    
    public ThreadSafeCache(Function<K, V> loader) {
        this.loader = loader;
    }
    
    public V get(K key) {
        return cache.computeIfAbsent(key, loader);
    }
    
    public void put(K key, V value) {
        cache.put(key, value);
    }
    
    public void invalidate(K key) {
        cache.remove(key);
    }
    
    public static void main(String[] args) {
        ThreadSafeCache<String, Integer> cache = 
            new ThreadSafeCache<>(key -> {
                System.out.println("加载数据:" + key);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return key.hashCode();
            });
        
        // 多线程访问
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 20; i++) {
            final int index = i;
            executor.submit(() -> {
                String key = "key" + (index % 5);
                Integer value = cache.get(key);
                System.out.println("获取:" + key + " = " + value);
            });
        }
        
        executor.shutdown();
    }
}

8. 总结

本篇我们学习了:

线程基础 :创建线程、线程生命周期

线程同步 :synchronized、Lock接口

线程间通信 :wait/notify、Condition

并发集合 :ConcurrentHashMap、BlockingQueue

线程池 :ExecutorService、Callable/Future

原子类:AtomicInteger、LongAdder

核心要点:

  1. 优先使用Runnable和Callable创建线程
  2. 同步是为了保护共享资源,但会降低并发性
  3. 线程池是管理线程的最佳方式
  4. 死锁是并发编程的大敌,要注意锁的顺序

下一篇预告: 《Java从零到熟练(十):JVM基础与性能优化》

  • 了解JVM内存模型
  • 学习垃圾回收机制
  • 掌握性能调优技巧

参考资源

  1. Java并发编程教程
  2. Java并发编程实战
  3. 廖雪峰Java教程 - 多线程

下一篇: Java从零到熟练(十):JVM基础与性能优化

相关推荐
木头程序员1 小时前
SSM框架学习笔记
java·开发语言·mysql·spring·maven
李白你好1 小时前
页面资产梳理 · 技术指纹识别 · Spring 端点探测
java·后端·spring
一起逃去看海吧1 小时前
dify-03
java·linux·开发语言
我是一颗柠檬1 小时前
【Java后端技术亮点】热Key探测与本地缓存二级防护:Redis热点问题的终极解决方案
java·redis·后端·缓存·中间件
Refrain_zc2 小时前
Android 音视频通话核心 —— 音频编码(AAC)完整解析
java
xiaoshuaishuai82 小时前
C# AvaloniaUI 资源找不到报错
java·服务器·前端·windows·c#
Xin_ye100862 小时前
C# 零基础到精通教程 - 第十八章:部署与发布——让应用上线
开发语言·c#
我是唐青枫2 小时前
Java JdbcTemplate 实战指南:用 Spring 轻量完成数据库增删改查
java·数据库·spring
思麟呀3 小时前
C++11并发编程:call_once一次性执行+atomic原子类型+CAS无锁编程+自旋锁
linux·开发语言·jvm·c++·windows