掘金 后端 ( ) • 2024-06-10 10:44

大学生勇闯多线程

如果将一个应用程序的运行称之为进程

那么多线程就是在进程的基础上更加细粒度的调用(cpu的核心数量)

使用多线程可以:

提高我们系统的响应速度(多线程允许程序同时执行多个任务)

提高资源利用率(减少cpu空闲时间)

多线程的创建方式

extends 继承

public class TestThread1  extends Thread{

    @Override
    public void run() {
        System.out.println("线程名称:"+ Thread.currentThread().getName() +"线程输出");
    }

    /**
     * 线程第一种创建方式 通过extends Thread 类实现
     */
    public static void main(String[] args) {
        new TestThread1().start();
    }
}

implements 接口

public class TestThread2 implements Runnable {
    @Override
    public void run() {
        System.out.println("线程执行体");
    }
    /**
     * 线程的第二种创建方式 但是并不推荐这种显式创建方式 而是通过线程池的方式去创建
     */
    public static void main(String[] args) {
        new Thread(new TestThread2()).start();

        for (int i = 0; i < 10; i++) {
            System.out.println("主线程正在执行");
        }
    }
}

因为这种方式只是实现了Runnable 所以没继承到start方法

在创建线程的时候 需要 new Thread(name).start()

使用executor 线程池

默认线程池的创建方式

// 创建一个固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(5);

自定义线程池

ThreadPoolExecutor executor = new ThreadPoolExecutor(
        2, //  线程池大小
        4,  // 线程池最大线程数
        60, // 线程池空闲时间
        TimeUnit.HOURS, // 时间单位
        new ArrayBlockingQueue<>(10), // 队列
        Executors.defaultThreadFactory(), // 线程工厂
        new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

抢票案例

多线程的运用场景

因为多线程是多个线程并发进行 所以存在数据竞争(如丢失更新、读取到脏数据)

常见的线程安全采用加锁的方式(synchronized关键字、ReentrantLock等)

在这里 我们使用 原子变量 AtomicInteger

/**
 * 票的数量
 */
private static final AtomicInteger AVAILABLE_TICKETS = new AtomicInteger(30000);

模拟多用户进行抢票

 // 创建一个固定大小的线程池
    ExecutorService executor = Executors.newFixedThreadPool(5);

    // 模拟多个用户抢票
    for (int i = 1; i <= 30000; i++) {
        executor.submit(new TicketBuyer("User " + i));
    }

    // 关闭线程池,不再接受新的任务,等待所有任务完成
    executor.shutdown();
    while (!executor.isTerminated()) {
        // 等待所有任务完成
    }
    System.out.println("All threads finished, remaining tickets: " + AVAILABLE_TICKETS.get());
}

线程的购票策略

@Override
public void run() {
    // 尝试购买票
    if (tryToBuyTicket()) {
        System.out.println(name + " bought a ticket."+Thread.currentThread().getName());
    } else {
        System.out.println(name + " failed to buy a ticket, sold out.");
    }
}

因为使用了原子变量 所以不需要加锁

private boolean tryToBuyTicket() {
        if (AVAILABLE_TICKETS.get() > 0) {
            AVAILABLE_TICKETS.decrementAndGet(); // 减少票数
            return true;
        }

    return false;
}

完整代码如下:


public class TicketBookingExample {

    /**
     * 票的数量
     */
    private static final AtomicInteger AVAILABLE_TICKETS = new AtomicInteger(30000);

    public static void main(String[] args) {
        // 创建一个固定大小的线程池
        ExecutorService executor = Executors.newFixedThreadPool(5);

        // 模拟多个用户抢票
        for (int i = 1; i <= 30000; i++) {
            executor.submit(new TicketBuyer("User " + i));
        }

        // 关闭线程池,不再接受新的任务,等待所有任务完成
        executor.shutdown();
        while (!executor.isTerminated()) {
            // 等待所有任务完成
        }
        System.out.println("All threads finished, remaining tickets: " + AVAILABLE_TICKETS.get());
    }

    static class TicketBuyer implements Runnable {
        private final String name;

        public TicketBuyer(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            // 尝试购买票
            if (tryToBuyTicket()) {
                System.out.println(name + " bought a ticket."+Thread.currentThread().getName());
            } else {
                System.out.println(name + " failed to buy a ticket, sold out.");
            }
        }

        private boolean tryToBuyTicket() {
                if (AVAILABLE_TICKETS.get() > 0) {
                    AVAILABLE_TICKETS.decrementAndGet(); // 减少票数
                    return true;
                }

            return false;
        }
    }
}