8 способов создания потоков в серии мертвых потоков Java

Java

(удобнее просматривать исходный код на горизонтальном экране мобильного телефона)

проблема

(1) Каковы способы создания темы?

(2) Какие у них есть сценарии применения?

Введение

«Создание потоков — это самая основная операция в многопоточном программировании», — резюмировал брат Тонг, — «Существует около 8 способов создания потоков, знаете ли вы?»

Наследовать класс Thread и переопределить метод run().

public class CreatingThread01 extends Thread {

    @Override
    public void run() {
        System.out.println(getName() + " is running");
    }

    public static void main(String[] args) {
        new CreatingThread01().start();
        new CreatingThread01().start();
        new CreatingThread01().start();
        new CreatingThread01().start();
    }
}

Наследовать класс Thread и переопределить метод run(). Недостаток этого метода в том, что класс может наследовать только один родительский класс. Если сам класс унаследовал другие классы, этот метод использовать нельзя.

Реализовать интерфейс Runnable

public class CreatingThread02 implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");
    }

    public static void main(String[] args) {
        new Thread(new CreatingThread02()).start();
        new Thread(new CreatingThread02()).start();
        new Thread(new CreatingThread02()).start();
        new Thread(new CreatingThread02()).start();
    }
}

Преимущество реализации интерфейса Runnable заключается в том, что класс может реализовать несколько интерфейсов, не затрагивая его систему наследования.

анонимный внутренний класс

public class CreatingThread03 {
    public static void main(String[] args) {
        // Thread匿名类,重写Thread的run()方法
        new Thread() {
            @Override
            public void run() {
                System.out.println(getName() + " is running");
            }
        }.start();

        // Runnable匿名类,实现其run()方法
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " is running");
            }
        }).start();
        
        // 同上,使用lambda表达式函数式编程
        new Thread(()->{
            System.out.println(Thread.currentThread().getName() + " is running");
        }).start();
    }
}

Способ использования анонимного класса: один — переписать метод run() Thread, другой — передать анонимный класс Runnable, а третий — использовать лямбда-метод.Теперь третий метод (java8+) обычно используется, что просто и быстро.

Реализовать интерфейс Callabe

public class CreatingThread04 implements Callable<Long> {
    @Override
    public Long call() throws Exception {
        Thread.sleep(2000);
        System.out.println(Thread.currentThread().getId() + " is running");
        return Thread.currentThread().getId();
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Long> task = new FutureTask<>(new CreatingThread04());
        new Thread(task).start();
        System.out.println("等待完成任务");
        Long result = task.get();
        System.out.println("任务结果:" + result);
    }
}

Реализуя интерфейс Callabe, можно получить результат выполнения потока, FutureTask фактически реализует интерфейс Runnable.

Таймер (java.util.Timer)

public class CreatingThread05 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        // 每隔1秒执行一次
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " is running");
            }
        }, 0 , 1000);
    }
}

С помощью таймера java.util.Timer можно быстро реализовать временные задачи, а TimerTask фактически реализует интерфейс Runnable.

Пул потоков

public class CreatingThread06 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 100; i++) {
            threadPool.execute(()-> System.out.println(Thread.currentThread().getName() + " is running"));
        }
    }
}

Используя пулы потоков, можно повторно использовать потоки и экономить системные ресурсы.

Параллельные вычисления (Java8+)

public class CreatingThread07 {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        // 串行,打印结果为12345
        list.stream().forEach(System.out::print);
        System.out.println();
        // 并行,打印结果随机,比如35214
        list.parallelStream().forEach(System.out::print);
    }
}

Использование метода параллельных вычислений позволяет повысить эффективность работы программы и многопоточное параллельное выполнение.

Spring асинхронные методы

Во-первых, класс запуска springboot плюс@EnableAsyncАннотация (@EnableAsync поддерживается Spring, вот удобный пример использования springboot).

@SpringBootApplication
@EnableAsync
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Во-вторых, метод плюс@Asyncаннотация.

@Service
public class CreatingThread08Service {

    @Async
    public void call() {
        System.out.println(Thread.currentThread().getName() + " is running");
    }
}

Затем тестовый пример точно такой же, как при использовании обычного метода службы.

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class CreatingThread08Test {

    @Autowired
    private CreatingThread08Service creatingThread08Service;

    @Test
    public void test() {
        creatingThread08Service.call();
        creatingThread08Service.call();
        creatingThread08Service.call();
        creatingThread08Service.call();
    }
}

Результаты приведены ниже:

task-3 is running
task-2 is running
task-1 is running
task-4 is running

Вы можете видеть, что поток, используемый каждый раз при выполнении метода, отличается.

Можно сказать, что способ использования асинхронных методов Spring достаточно удобен, и он подходит для некоторых методов, не связанных с front и back логикой и подходящих для асинхронных вызовов, таких как функция отправки коротких сообщений.

Суммировать

(1) Наследовать класс Thread и переопределить метод run();

(2) реализовать интерфейс Runnable;

(3) Анонимные внутренние классы;

(4) реализовать интерфейс Callabe;

(5) Таймер (java.util.Timer);

(6) пул потоков;

(7) параллельные вычисления (Java8+);

(8) Spring асинхронный метод;

пасхальные яйца

Выше описано так много способов создания потоков, что фактически есть два способа: один — наследовать класс Thread и переписать его метод run(), а второй — реализовать метод run() интерфейса Runnable. Какая связь?

Пожалуйста, посмотрите следующий пример, унаследуйте Thread и одновременно реализуйте интерфейс Runnable, что должно быть выведено?

public class CreatingThread09 {

    public static void main(String[] args) {
        new Thread(()-> {
            System.out.println("Runnable: " + Thread.currentThread().getName());
        }) {
            @Override
            public void run() {
                System.out.println("Thread: " + getName());
            }
        }.start();
    }
}

Сказав это, нам необходимо взглянуть на исходный код класса Thread:

public class Thread implements Runnable {
    // Thread维护了一个Runnable的实例
    private Runnable target;
    
    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
    
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    
    private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        // ...
        // 构造方法传进来的Runnable会赋值给target
        this.target = target;
        // ...
    }
    
    @Override
    public void run() {
        // Thread默认的run()方法,如果target不为空,会执行target的run()方法
        if (target != null) {
            target.run();
        }
    }
}

Увидев здесь, вдруг не просветлел? Поскольку приведенный выше пример наследует Thread и одновременно реализует интерфейс Runnable, согласно исходному коду, это фактически эквивалентно переписыванию метода run() Thread, а метод run() Thread на самом деле не имеет ничего общего с цель.

Итак, вывод приведенного выше примера:Thread: Thread-0, выводите содержимое только в методе run() переопределенного потока.

Добро пожаловать, чтобы обратить внимание на мою общедоступную учетную запись «Брат Тонг читает исходный код», проверить больше статей из серии исходного кода и поплавать в океане исходного кода с братом Тонгом.

qrcode