线程

37 阅读2分钟

线程

线程创建三种方式

继承Thread类

// 1. 继承Thread类型,就是一个自定义的线程类
public class MyThread extends Thread {
​
    @Override
    // 重写run方法,写线程的执行任务
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程MyThread输出:" + i);
        }
    }
​
}
​
public class ThreadTest1 {
    // main方法是由一条默认的主线程负责执行。
    public static void main(String[] args) {
        // 3、创建MyThread线程类的对象代表一个线程
        Thread myThread = new MyThread();
​
        // 4、start()方法:启动线程(自动执行run方法的)
        myThread.start();
​
​
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程输出:" + i);
        }
​
    }
}
​

实现Runnable接口

定义子类写法
// 1. 定义一个任务类,实现Runnable接口
public class MyRunnable implements Runnable {
    @Override
    // 写子线程执行的任务
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程MyRunnable执行:" + i);
        }
    }
}
public class ThreadTest2 {
    public static void main(String[] args) {
        // 3、创建任务对象。
        MyRunnable myRunnable = new MyRunnable();
        // 4、把任务对象交给一个线程对象处理。
        Thread thread = new Thread(myRunnable);
​
        thread.start();
​
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程执行:" + i);
        }
​
    }
}
匿名内部类写法
public class ThreadTest2_2 {
    public static void main(String[] args) {
        // 掌握多线程创建方式二的匿名内部类写法。
​
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("子线程1执行了:" + i);
                }
            }
        }).start();
​
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("子线程2执行了:" + i);
            }
        });
        thread.start();
​
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程执行了:" + i);
        }
    }
}
​

实现Callable接口

// 1. 实现Callable接口,泛型指定为线程的返回值类型
public class MyCallable implements Callable<Integer> {
    private int start;
    private int end;
​
    public MyCallable(int start, int end) {
        this.start = start;
        this.end = end;
    }
​
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = start; i <= end; i++) {
            sum += i;
        }
        return sum;
    }
}
​
public class ThreadTest3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
​
        // 3、创建一个Callable的对象
        Callable<Integer> c1 = new MyCallable(1, 10);
​
        // 4、把Callable的对象封装成一个FutureTask对象(任务对象)
        // 未来任务对象的作用?
        // 1、是一个任务对象,实现了Runnable对象.
        // 2、可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕后的结果。
        FutureTask<Integer> task1 = new FutureTask<>(c1);
​
        // 5、把任务对象交给一个Thread对象
        new Thread(task1).start();
​
​
        Callable<Integer> c2 = new MyCallable(11, 20);
        FutureTask<Integer> task2 = new FutureTask<>(c2);
        new Thread(task2).start();
​
​
        // 6、获取线程执行完毕后返回的结果。
        // 注意:如果执行到这儿,假如上面的线程还没有执行完毕
        // 这里的代码会暂停,等待上面线程执行完毕后才会获取结果。
        Integer sum1 = task1.get();
        Integer sum2 = task2.get();
​
        int sum3 = 0;
        for (int i = 21; i <= 30; i++) {
            sum3 += i;
        }
​
        System.out.println("通过三个线程合力完成了这个" +
                "复杂计算:1~30的和" + (sum1 + sum2 + sum3));
​
    }
}

线程池

线程池创建

public class ThreadPoolTest2 {
    public static void main(String[] args) {
        // 核心线程数的设置
        // 1. IO密集型
        // CPU核数 X 2, 12X2=24
        System.out.println(Runtime.getRuntime().availableProcessors());
​
        // 2. CPU密集型
        // CPU核数 + 1, 12+1=13
​
        // 核心线程数的设置:和核心线程数相等、1.5倍、2倍
​
        ExecutorService executor = new ThreadPoolExecutor(3, // 核心线程数
                5, // 最大线程数
                8, // 额外线程存活时间
                TimeUnit.SECONDS, // 时间单位
                new ArrayBlockingQueue<>(4), // 任务等待队列
                Executors.defaultThreadFactory(), // 线程工厂
                new ThreadPoolExecutor.AbortPolicy());// 任务丢弃策略 (默认的报错的策略)
​
        MyRunnable1 runnable1 = new MyRunnable1();
        // 线程池自动创建新线程,自动处理这个任务
        executor.execute(runnable1);
        executor.execute(runnable1);
        executor.execute(runnable1);
​
        // 下面四个任务在等待队列中排队
        executor.execute(runnable1);
        executor.execute(runnable1);
        executor.execute(runnable1);
        executor.execute(runnable1);
​
        // 创建临时(额外)线程,额外最多创建两个线程,因为最大线程数是5个
        executor.execute(runnable1);
        executor.execute(runnable1);
​
        // 任务拒绝策略生效,AbortPolicy():是默认的拒绝策略,方式是报错
        // executor.execute(runnable1);
​
        // executor.shutdown(); // 线程池中所有任务执行完毕后,再关
        // executor.shutdownNow(); // 强制关闭线程池,不管任务是否执行完毕
    }
}

线程池工具类

public class ThreadPoolUtil {
    private static final ExecutorService LOGIN_THREAD_POOL = new ThreadPoolExecutor(3, // 核心线程数
            5, // 最大线程数
            8, // 额外线程存活时间
            TimeUnit.SECONDS, // 时间单位
            new ArrayBlockingQueue<>(20), // 任务等待队列
            Executors.defaultThreadFactory(), // 线程工厂
            new ThreadPoolExecutor.AbortPolicy());// 任务丢弃策略 (默认的报错的策略)
​
​
    public static void executeLoginJob(Runnable runnable) {
        LOGIN_THREAD_POOL.execute(runnable);
    }
}
public class ThreadDemo2 {
    public static final Logger LOGGER = LoggerFactory.getLogger(ThreadDemo2.class);
​
    public static void main(String[] args) throws InterruptedException {
        // 多个用户登录
        // 改进:使用线程池管理线程,避免线程频繁创建
​
        // 基于匿名内部类 --> lambda写法
        ThreadPoolUtil.executeLoginJob(() -> login());
        ThreadPoolUtil.executeLoginJob(() -> login());
        ThreadPoolUtil.executeLoginJob(() -> login());
​
    }
​
    public static void login() {
        long start = System.currentTimeMillis();
​
        storeLoginMessage();
        getAd();
        checkAddress();
​
        long end = System.currentTimeMillis();
        LOGGER.info("共耗时:{}毫秒", (end - start));
​
    }
​
​
    private static void storeLoginMessage() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("记录登录信息-->用时500毫秒");
    }
​
    private static void getAd() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("广告推送-->用时1000毫秒");
    }
​
    private static void checkAddress() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("检测登录地址-->用时1000毫秒");
    }
}