JAVA多线程手撕

线程安全的单例模式

双重检查锁定

java 复制代码
// 线程安全的单例模式:双重检查锁定
public class Singleton {
    // volatile防止指令重排序 instance = new Singleton()
    // 1.分配内存空间 2.初始化对象 3.引用赋值给instance
    // 如重排为1-3-2,则可能将尚未完成初始化的对象引用暴露给其他线程访问
    private static volatile Singleton instance;

    // 私有化构造方法,不允许外部实例化
    private Singleton() {

    }

    public static Singleton getInstance() {
        if (instance == null) { // 避免不必要的加锁同步,已存在实例
            // 一个线程获取锁进入,其他线程在此等待释放锁
            synchronized (Singleton.class) {
                if (instance == null) { // 确保实例唯一,已存在实例
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

线程循环打印

三个线程循环打印,线程一打印A,线程二打印B,线程三打印C,打印10遍

采用synchronized + wait/notify/notifyAll

java 复制代码
// 三个线程循环打印A B C
public class PrintABC {

    // volatile保证可见性:每次读从主内存拿最新值,每次写立刻刷回主内存,不加锁开销小
    // **但i++这种复合操作无法保护,两个线程可能同时读到同一个值,加1后写回
    // private volatile int flag = 0;

    // 标志位 0-A 1-B 2-C
    // synchronized块内部读写时,进入同步块清空本地缓存,退出时刷新主内存,已经保证可见性
    private int flag = 0;

    // 同一个监视器对象,保证互斥
    private final Object lock = new Object();

    public void print(int turn, char ch) { // 谁执行
        // 同步块
        synchronized (lock) {
            while (flag != turn) {
                try {
                    lock.wait(); // 不是当前线程执行时机 释放锁等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 执行
            System.out.println(ch);
            // 修改标志位
            flag = (flag + 1) % 3;
            // 释放锁,唤醒其他线程
            lock.notifyAll();
        }
    }

    public static void main(String[] args) {
        PrintABC printABC = new PrintABC();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                printABC.print(0, 'A');
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                printABC.print(1, 'B');
            }
        });
        Thread thread3 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                printABC.print(2, 'C');
            }
        });
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

采用ReetrantLock + Condition

java 复制代码
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class PrintABC1 {

    private int flag = 0;
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition conditionA = lock.newCondition();
    private final Condition conditionB = lock.newCondition();
    private final Condition conditionC = lock.newCondition();

    // 谁执行 唤醒谁
    public void print(int turn, char ch, Condition cs, Condition ce) {
        // 加锁
        lock.lock();
        try {
            while (flag != turn) {
                // 不满足cs执行时机 释放锁进入等待
                cs.await();
            }
            // 执行
            System.out.println(ch);
            // 修改标志位
            flag = (flag + 1) % 3;
            // 精确唤醒ce
            ce.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 手动释放锁
            lock.unlock();
        }
    }


    public static void main(String[] args) {
        PrintABC1 pt = new PrintABC1();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                pt.print(0, 'A', pt.conditionA, pt.conditionB);
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                pt.print(1, 'B', pt.conditionB, pt.conditionC);
            }
        });
        Thread thread3 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                pt.print(2, 'C', pt.conditionC, pt.conditionA);
            }
        });
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

保证线程顺序执行

保证线程T1,T2,T3按顺序执行

采用JOIN串行启动

java 复制代码
public class JoinDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> System.out.println("th1"));
        Thread t2 = new Thread(() -> System.out.println("th2"));
        Thread t3 = new Thread(() -> System.out.println("th3"));

        t1.start();
        // 让当前main线程阻塞,直到t1线程执行完毕才启动t2线程
        t1.join();

        t2.start();
        t2.join();

        t3.start();
        t3.join();

    }
}

采用CountDownLatch

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

public class CdLatch {

    public static void main(String[] args) {

        // 两个门闩
        CountDownLatch c1 = new CountDownLatch(1); // t1-t2
        CountDownLatch c2 = new CountDownLatch(1); // t2-t3

        Thread t1 = new Thread(() -> {
            System.out.println("thread1");
            c1.countDown(); // t1执行完后c1计数器减一
        });

        Thread t2 = new Thread(() -> {
            try {
                c1.await(); // t2需要c1计数器为零才能执行 否则等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("thread2");
            c2.countDown(); // t2执行完后c2计数器减一
        });

        Thread t3 = new Thread(() -> {
            try {
                c2.await(); // t3需要c2计数器为零才能执行 否则等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("thread3");
        });

        t1.start();
        t2.start();
        t3.start();
    }
}

采用CompletableFuture

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

public class cptFuture {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println("thread1");
        });

        Thread t2 = new Thread(() -> {
            System.out.println("thread2");
        });

        Thread t3 = new Thread(() -> {
            System.out.println("thread3");
        });
        // 函数式风格
        CompletableFuture<Void> future = CompletableFuture
                .runAsync(t1)
                .thenRun(t2)
                .thenRun(t3); // t1->t2->t3
    }
}

采用单个线程的线程池

java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Executor {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.submit(() -> System.out.println("th1"));
        executor.submit(() -> System.out.println("th2"));
        executor.submit(() -> System.out.println("th3"));

        executor.shutdown();
    }
}

生产者消费者

直接使用阻塞队列BlockingQueue

java 复制代码
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class pdCuQueue {

    private static final int MAX_QUEUE_SIZE = 2;
    // 队满插入阻塞,队空取数据阻塞
    private static final LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<>(MAX_QUEUE_SIZE);
    private static final AtomicInteger Product = new AtomicInteger(1);

    public static void main(String[] args) {
        for (int i = 1; i <= 2; i++) {
            new Thread(new Producer(i)).start();
        }
        for (int i = 1; i <= 5; i++) {
            new Thread(new Consumer(i)).start();
        }
    }


    private static class Producer implements Runnable {

        private int id;

        public Producer(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            while (Product.get() <= 20) { // 生产暂停
                int num = Product.getAndIncrement();
                try {
                    Thread.sleep(1000);
                    System.out.println("Producer " + id + " Produces " + num);
                    queue.put(num);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    e.printStackTrace();
                    break;
                }
            }

        }
    }

    private static class Consumer implements Runnable {

        private int id;

        public Consumer(int id) {
            this.id = id;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    Integer num = queue.poll(5, TimeUnit.SECONDS); // 超时 避免永久阻塞
                    if (num == null && queue.isEmpty()) {
                        break;
                    }
                    System.out.println("Consumer " + id + " consume " + num);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    e.printStackTrace();
                    break;
                }
            }

        }
    }

}

采用 synchronized + wait/notify/notifyAll + Queue

java 复制代码
import java.util.ArrayDeque;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

public class selfQueue {

    public final static int MAX_QUEUE_SIZE = 2;
    public final static AtomicInteger ATOMIC_INTEGER = new AtomicInteger(0);
    public static volatile boolean productionDone = false;

    private static class Consumer implements Runnable {

        private ArrayDeque<Integer> queue;
        private int ID;

        public Consumer(ArrayDeque<Integer> queue, int ID) {
            this.queue = queue;
            this.ID = ID;
        }

        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    while (queue.isEmpty()) {
                        if (productionDone) {
                            return; // 已经生产结束,不在等待消费
                        }
                        try {
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    int num = queue.poll();
                    System.out.println("Consumer " + ID + " consume " + num);
                    queue.notifyAll();
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }

    private static class Producer implements Runnable {

        private ArrayDeque<Integer> queue;
        private int ID;
        private Random random;

        public Producer(ArrayDeque<Integer> queue, int ID) {
            this.queue = queue;
            this.ID = ID;
            random = new Random();
        }

        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    if (ATOMIC_INTEGER.get() >= 20) break; // 进入前判断
                    while (queue.size() == MAX_QUEUE_SIZE) {
                        try {
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    if (ATOMIC_INTEGER.get() >= 20) break; // 唤醒后判断
                    int num = random.nextInt(1000) + 1;
                    queue.offer(num);
                    ATOMIC_INTEGER.getAndIncrement();
                    System.out.println("Producer " + ID + " produce " + num);
                    queue.notifyAll();
                }
                try {
                    // sleep会占用锁,放在同步块外面
                    Thread.sleep(random.nextInt(1000) + 1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            productionDone = true; // 生产完成
            synchronized (queue) {
                queue.notifyAll(); // 唤醒可能正在等待空队列的消费者
            }

        }
    }

    public static void main(String[] args) {
        ArrayDeque<Integer> queue = new ArrayDeque<>(MAX_QUEUE_SIZE);
        for (int i = 1; i <= 2; i++) {
            new Thread(new Producer(queue, i)).start();
        }

        for (int i = 3; i <= 6; i++) {
            new Thread(new Consumer(queue, i)).start();
        }
    }
}

死锁

java 复制代码
public class deadLock {

    private final static Object lockA = new Object();
    private final static Object lockB = new Object();
    // 互斥
    // 持有并等待
    // 不可剥夺
    // 循环等待
    public static void main(String[] args) {

        new Thread(() -> {
            synchronized (lockA) {
                try {
                    Thread.sleep(1000); // 让另一个线程先把B锁拿到
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lockB) { // 持有A还想持有B
                    System.out.println("A -> B");
                }

            }
        }).start();

        new Thread(() -> {
            synchronized (lockB) {
                try {
                    Thread.sleep(1000); // 让另一个线程先把A锁拿到
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lockA) { // 持有B还想持有A
                    System.out.println("B -> A");
                }

            }
        }).start();

    }
}

线程安全的计数累加

100个线程分别累加100次,最终结果为10000

采用原子类

java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class secureAdd {

    // 底层CAS竞争同一value,CAS失败就重试
    private static final AtomicInteger count = new AtomicInteger(0);
    // private static volatile int count = 0;

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(100);
        for (int i = 0; i < 100; i++) {
            executor.submit(() -> {
                for (int j = 0; j < 100; j++) {
                    count.incrementAndGet();
                    // count++;
                }
            });
        } // 100个线程分别累加100次
        executor.shutdown();
        try {
            // 等待所有任务执行完毕
            executor.awaitTermination(1, TimeUnit.HOURS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(count.get());
        // System.out.println(count);
    }
}

采用累加器

java 复制代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.LongAdder;

public class secureAdd1 {
    // 底层base+cell,先CAS尝试更新base,失败了则找到对应Cell更新,Cell之间互不干扰
    // 求和时将base和Cell值加起来
    // Cell数组触发扩容时容量翻倍但不超过核心CPU数
    private final static LongAdder counter = new LongAdder();

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(100);
        for (int i = 0; i < 100; i++) {
            executor.submit(() -> {
                for (int j = 0; j < 100; j++) {
                    counter.increment();
                }
            });
        }
        executor.shutdown();
        try {
            executor.awaitTermination(1, TimeUnit.HOURS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(counter.sum());
    }
}

模拟购票系统

总共100张票,10个窗口共同出售

采用synchronized + wait/notifyAll

java 复制代码
public class TkService {

    // 当前剩余票数 共享资源ticket
    private static int ticket = 100;

    // 同一个监视器对象
    private static final Object lock = new Object();

    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            new Thread(new ticketSeller(i)).start();
        }
    }

    private static class ticketSeller implements Runnable {

        // 窗口号
        private final int windowNumber;

        public ticketSeller(int windowNumber) {
            this.windowNumber = windowNumber;
        }

        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    if (ticket > 0) { // 还有余票
                        buyTicket();  // 卖出
                        lock.notifyAll(); // 唤醒其他线程
                        try {
                            lock.wait(); // 释放锁 等待 不再执行后续代码
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }

                    } else {
                        lock.notifyAll(); // 唤醒其他等待的线程 触发校验后都结束循环
                        break; // 没有余票结束循环
                    }

                }
            }

        }

        private void buyTicket() {
            System.out.println("窗口 " + windowNumber + " 购买了一张票 " + " 还剩下 " + --ticket + " 张票");
        }
    }
}

采用Semaphore

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

public class SpDemo {

    private static int ticket = 100;

    // Semaphore计数信号量,控制同时访问某个资源的线程数量
    // new Semaphore(1,true) 许可数为1(实现synchronized的效果) + 公平模式(FIFO队列)
    private static final Semaphore semaphore = new Semaphore(1);

    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            new Thread(new ticketSeller(i)).start();
        }
    }

    private static class ticketSeller implements Runnable {
        private int windowNumber;

        public ticketSeller(int windowNumber) {
            this.windowNumber = windowNumber;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    // 调用acquire,若计数器大于0,则减一,继续执行;否则阻塞等待
                    semaphore.acquire();
                    if (ticket > 0) {
                        buyTicket();
                    } else {
                        break;
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 完成任务后调用release,计数器加1,唤醒一个等待的线程
                    semaphore.release();
                }
            }
        }

        private void buyTicket() {
            System.out.println("窗口 " + windowNumber + " 卖出了一张票,还剩下 " + --ticket + " 张票");
        }
    }
}

线程池

java 复制代码
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadPoolDemo {
    
    // 原子类
    private static final AtomicInteger ID = new AtomicInteger(1);

    public static void main(String[] args) {
        // 线程池参数
        int corePoolSize = 2;
        int maxPoolSize = 5;
        long keepAliveTime = 1L;
        int queueCapacity = 10;
        ThreadFactory tf = (r) -> new Thread(r, "thread-" + ID.getAndIncrement());
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                // 核心线程不够用,创建新线程
                corePoolSize,
                // 队列满,创建非核心线程,最多创建到最大线程数
                maxPoolSize,
                // 非核心线程空闲超时,回收多余线程
                keepAliveTime,
                // 空线超时的时间单位
                TimeUnit.SECONDS,
                // 核心线程数满,新任务在工作队列排队
                // ArrayBlockingQueue LinkedBlockingQueue PriorityBlockingQueue DelayQueue SynchronousQueue
                new ArrayBlockingQueue<>(queueCapacity),
                // 线程工厂,自定义线程名
                tf,
                // 队列满且达到最大线程数,触发拒绝策略
                // AbortPolicy CallerRunsPolicy DiscardOldestPolicy DiscardPolicy
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
        for (int i = 0; i < 100; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行任务 " + taskId);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executor.shutdown();
    }
}

ThreadLocal实践

获取当前线程用户ID

java 复制代码
package com.juc.threadlocal;


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class thLocalDemo {

    public static final AtomicInteger atomicInteger = new AtomicInteger(0);
    // ThreadLocal线程隔离,每个线程都有自己独立的变量副本,不需要加锁也不会有竞争
    // 每个Thread对象内部有一个threadLocals字段,类型是ThreadLocalMap,调用get时候先拿到该Map,再用ThreadLocal对象自身作为key去查
    // ThreadLocalMap中Entry对key使用弱引用
    // Entry<Key,Value> Key->ThreadLocal弱引用,如果栈上对ThreadLocal的强引用没了,下次GC就可以回收

    // 声明static final,一个ThreadLocal实例对应一个线程变量即可
    // withInitial设置默认值,防止get拿到null
    private static final ThreadLocal<Integer> USER_ID = ThreadLocal.withInitial(() -> 0);

    public static void handleRequest(int userId) {
        try {
            // 需要隔离的数据 该线程内整个调用链都可以获取到
            USER_ID.set(userId);
            System.out.println("Current User ID is: " + USER_ID.get());
        } finally {
            // 业务逻辑执行完必须remove 防止内存泄露和脏数据
            USER_ID.remove();
        }
    }

    public static class Request implements Runnable {
        private final int userId;

        public Request(int userId) {
            this.userId = userId;
        }

        @Override
        public void run() {
            handleRequest(userId);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 100; i++) {
            executor.submit(new Request(atomicInteger.getAndIncrement()));
        }
        executor.shutdown();
        try {
            executor.awaitTermination(1, TimeUnit.HOURS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}
相关推荐
Jesse_EC1 小时前
我给消息推送加了 Publisher Confirm,然后亲手引入了一个竞态
java
用户8181870627461 小时前
第7章 死锁排查实录:jstack定位死锁的完整流程
java·后端
喵同志不止步于码农1 小时前
Java Caffeine 快速入门
java·开发语言·后端·并发·缓存一致性
Java成神之路-1 小时前
Java 并发灵魂拷问:notify/signal 唤醒顺序是规范还是实现?
java
Java牛马2 小时前
Caffeine 缓存及其应用相关总结
java·redis·缓存·caffeine·数据一致性·本地缓存
一只叫煤球的猫2 小时前
Spring AI 2.0 源码解析(三):ChatClient 的 Fluent API 如何构建请求?
java·后端·面试
软件开发JR2 小时前
基于Web的足球青训俱乐部管理后台系统的设计与开发
java·前端·spring boot·毕业设计
重生之我是Java开发战士3 小时前
【Java EE】Spring AOP :面向切面编程
java·spring·java-ee
凤山老林3 小时前
基于 Spring Batch 的海量数据迁移与批处理架构:分片、容错与断点续跑
java·spring boot·spring·架构·spring batch