Java多线程代码题

发布于2025-03-12
字数: 15616
JVM
学习笔记

面试的时候被问到一些多线程面试题,没有做出来,写一下记录一下。

双线程轮流打印1-100

使用两个线程,其中一个打印奇数,另外一个打印偶数

java 复制代码
public class TwoThreadPrintNumber {

    private final static Object lock = new Object();
    public static int num = 1;

    public static void main(String[] args) {
        TwoThreadPrintNumber twoThreadPrintNumber = new TwoThreadPrintNumber();
        Thread thread1 = new Thread(() -> {
            twoThreadPrintNumber.printNumber(true);
        },"偶数线程");
        Thread thread2 = new Thread(() -> {
            twoThreadPrintNumber.printNumber(false);
        },"奇数线程");
        thread1.start();
        thread2.start();
    }
    // true 打印偶数 否则打印奇数
    private void printNumber(boolean type) {
        while (num <= 100) {
            synchronized (lock) {
                while ((type && num % 2 == 1) || (!type && num % 2 == 0)) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (num <= 100) {
                    System.out.println(Thread.currentThread().getName() + " print " + num);
                    num++;
                    lock.notifyAll();
                }
            }
        }
    }
}

printNumber中为什么使用while,而不是使用if

为了避免线程的虚假唤醒,当线程进入等待状态后,有可能会在没有被其他线程调用notify或者notifyAll方法的情况下被唤醒,这种情况就被称为虚假唤醒

如果使用if语句,假设线程被虚假唤醒了,就会执行if语句后面的打印代码,而不是再次检查条件是否满足,最终导致打印结果不符合预期;而使用while语句,线程被唤醒后会再次检查while条件,从而避免虚假唤醒带来的问题。

同步代码块中为什么还需要再判断一次 if (num <= 100)

为了线程安全,当一个线程被唤醒后,虽然它已经重新获得锁了,但是等待期间可能其他线程已经将num值增加到超过100的值了,所以打印之前需要重新检查一下。

三个线程顺序打印1-100

java 复制代码
public class ThreeThreadPrint {

    private static int number = 1;

    public static final Object lock = new Object();

    public static void main(String[] args) {
        ThreeThreadPrint threeThreadPrint = new ThreeThreadPrint();
        Thread thread1 = new Thread(() -> {
            threeThreadPrint.printNumber(0);
        }, "3n");
        Thread thread2 = new Thread(() -> {
            threeThreadPrint.printNumber(1);
        }, "3n+1");
        Thread thread3 = new Thread(() -> {
            threeThreadPrint.printNumber(2);
        }, "3n+2");
        thread1.start();
        thread2.start();
        thread3.start();
    }

    private void printNumber(int type) {
        while (number <= 100) {
            synchronized (lock) {
                while (number % 3 != type) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (number <= 100) {
                    System.out.println(Thread.currentThread().getName() + " print " + number);
                    number++;
                    lock.notifyAll();
                }
            }
        }
    }
}

线程A、B、C,分别打印1、2、3,顺序执行10次

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

public class SequentialPrint {

    private static final Object lockObj = new Object();
    private static int count = 0;
    public static int offset =0;

    private static final ReentrantLock lock = new ReentrantLock();
    private static final Condition conditionA = lock.newCondition();
    private static final Condition conditionB = lock.newCondition();
    private static final Condition conditionC = lock.newCondition();
    public static int type = 1;
    public static final int MAX_LOOP = 10;



    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            printNumber(1, 1, conditionA, conditionB);
        }, "线程A");
        Thread threadB = new Thread(() -> {
            printNumber(2, 2, conditionB, conditionC);
        }, "线程B");
        Thread threadC = new Thread(() -> {
            printNumber(3, 0, conditionC, conditionA);
        }, "线程C");
        threadA.start();
        threadB.start();
        threadC.start();


        Thread threadA1 = new Thread(() -> {
            printNumber(1);
        }, "线程A");
        Thread threadB1 = new Thread(() -> {
            printNumber(2);
        }, "线程B");
        Thread threadC1 = new Thread(() -> {
            printNumber(3);
        }, "线程C");
        threadA1.start();
        threadB1.start();
        threadC1.start();
    }

    private static void printNumber(int number, int target, Condition curr, Condition nextThread) {
        for (int i = 0; i < MAX_LOOP; i++) {
            lock.lock();
            try {
                while (type % 3 != target) {
                    curr.await();
                }
                System.out.println(Thread.currentThread().getName() + " print " + number);
                type++;
                nextThread.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    private static void printNumber(int number) {
        for (int i = 0; i < MAX_LOOP; i++) {
            synchronized (lockObj) {
                while (offset % 3 != number - 1) {
                    try {
                        lockObj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (count < 30) {
                    System.out.println(Thread.currentThread().getName() + " print " + number);
                    offset++;
                    count++;
                    lockObj.notifyAll();
                }
            }
        }
    }
}

100个线程,每个线程累加100次

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

public class AtomicCount {

    public static AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(100);
        for (int i = 0; i < 100; i++) {
            executor.submit(() -> {
                for (int j = 0; j < 100; j++) {
                    count.incrementAndGet();
                }
            });
        }
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.HOURS);
        System.out.println("count = " + count.get());
    }
}

线程交叉打印12A34B56C

java 复制代码
public class CrossingPrint {

    public static final Object lock = new Object();
    // true 打印数字
    public static boolean flag = true;
    private static int cnt = 0;
    private static int num = 1;
    private static Character letter = 'A';

    public static void main(String[] args) {
        Thread numberPrintThread = new Thread(() -> {
            crossPrint(true);
        }, "NumberPrintThread");
        Thread letterPrintThread = new Thread(() -> {
            crossPrint(false);
        }, "LetterPrintThread");
        numberPrintThread.start();
        letterPrintThread.start();
    }

    private static void crossPrint(boolean currFlag) {
        while (cnt < 78) {
            synchronized (lock) {
                while (flag != currFlag) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (cnt < 78) {
                    if (currFlag) {
                        System.out.println(num++);
                        System.out.println(num++);
                        cnt += 2;
                    } else {
                        System.out.println(letter++);
                        cnt++;
                    }
                    flag = !flag;
                    lock.notifyAll();
                }
            }
        }
    }
}

模拟购票系统 500张票 有1-4编号的4个购票窗口,模拟购票流程,打印购票结果

java 复制代码
public class BuyTicket {

    private volatile static int ticketTotal = 500;

    private static final Object lock = new Object();

    public static void main(String[] args) {
        for (int i = 1; i <= 4; i++) {
            new Thread(BuyTicket::buyTicket, i + "").start();
        }
    }

    private static void buyTicket() {
        while (ticketTotal > 0) {
            synchronized (lock) {
                if (ticketTotal > 0) {
                    --ticketTotal;
                    System.out.println(Thread.currentThread().getName() + "购买1张票, 剩余票数为:" + ticketTotal);
                }
            }
            try {
                // 休眠增加窗口随机性
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }



}

生产者-消费者模式

synchronized+wait+notify实现

java 复制代码
public class ProductConsumer {

    public static final Object lock = new Object();

    public static int currCnt = 0;

    private static final int BUFFER_SIZE = 5;

    private static Object[] buffer = new Object[BUFFER_SIZE];

    private static int inIndex = 0;
    private static int outIndex = 0;


    public static void main(String[] args) {
        Product product = new Product();
        Consumer consumer = new Consumer();
        product.start();
        consumer.start();
    }


    static class Product extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                synchronized (lock) {
                    while (currCnt >= BUFFER_SIZE) {
                        System.out.println("队列已满 生产者等待");
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    buffer[inIndex] = i;
                    inIndex = (inIndex + 1) % BUFFER_SIZE;
                    currCnt++;
                    System.out.println("生产数据: " + i + " 缓冲区数量: " + currCnt);
                    lock.notify();
                }
            }
        }
    }

    static class Consumer extends Thread {
        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    while (currCnt <= 0) {
                        System.out.println("队列已空 消费者等待");
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Object data = buffer[outIndex];
                    currCnt--;
                    System.out.println("消费消息: " + data + " 缓冲区数量: " + currCnt);
                    outIndex = (outIndex + 1) % BUFFER_SIZE;
                    lock.notify();
                }
            }
        }
    }
}

ReentrantLock + Condition实现

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

public class ProductConsumerWithReenLock {

    public static final ReentrantLock lock = new ReentrantLock();

    public static final Condition notFull = lock.newCondition();

    public static final Condition notEmpty = lock.newCondition();

    public static int currCnt = 0;

    private static final int BUFFER_SIZE = 5;

    private static Object[] buffer = new Object[BUFFER_SIZE];

    private static int inIndex = 0;
    private static int outIndex = 0;

    public static void main(String[] args) {
        Product product = new Product();
        Consumer consumer = new Consumer();
        product.start();
        consumer.start();
    }

    static class Product extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                lock.lock();
                try {
                    while (currCnt >= BUFFER_SIZE) {
                        System.out.println("队列已满 生产者等待");
                        notFull.await();
                    }
                    Thread.sleep(100);
                    buffer[inIndex] = i;
                    inIndex = (inIndex + 1) % BUFFER_SIZE;
                    currCnt++;
                    System.out.println("生产数据: " + i + " 缓冲区数量: " + currCnt);
                    notEmpty.signal();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }

    static class Consumer extends Thread {
        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (currCnt <= 0) {
                        System.out.println("队列已空 消费者等待");
                        notEmpty.await();
                    }
                    Object data = buffer[outIndex];
                    currCnt--;
                    System.out.println("消费消息: " + data + " 缓冲区数量: " + currCnt);
                    outIndex = (outIndex + 1) % BUFFER_SIZE;
                    notFull.signal();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

Java实现单例模式

懒汉式

java 复制代码
public class EagerSingleton {
    // 饿汉式
    private static final EagerSingleton INSTANCE = new EagerSingleton();

    private EagerSingleton() {
        // 防止发射创建实例
        if (INSTANCE != null) {
            throw new IllegalStateException("已初始化");
        }
    }

    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

懒汉式

线程不安全版

java 复制代码
public class UnsafeLazySingleton {

    private static UnsafeLazySingleton instance;
    
    private UnsafeLazySingleton() {}
    
    public static UnsafeLazySingleton getInstance() {
        if (instance == null) {
            // 多线程可能创建多实例
            instance = new UnsafeLazySingleton();
        }
        return instance;
    }
}

线程安全版

java 复制代码
public class SyncLazySingleton {

    private static SyncLazySingleton instance;

    private SyncLazySingleton() {}

    public static synchronized SyncLazySingleton getInstance() {
        if (instance == null) {
            instance = new SyncLazySingleton();
        }
        return instance;
    }
}

双重检测锁

java 复制代码
public class DoubleCheckLockSing {

    private static DoubleCheckLockSing instance;

    private DoubleCheckLockSing() {}

    public static DoubleCheckLockSing getInstance() {
        if (instance == null) {
            synchronized (DoubleCheckLockSing.class) {
                if (instance == null) {
                    instance = new DoubleCheckLockSing();
                }
            }
        }
        return instance;
    }
}

静态内部类

java 复制代码
public class HolderSingleton {
    private HolderSingleton() {}
    
    private static class Holder {
        static final HolderSingleton INSTANCE = new HolderSingleton();
    }
    
    public static HolderSingleton getInstance() {
        return Holder.INSTANCE;
    }
}