Java多线程四种常用创建方式 | 【奔跑吧!JAVA】

举报
lwq1228 发表于 2021/06/12 22:10:11 2021/06/12
【摘要】 Java多线程四种常用创建方式

方式一:继承Thread类的方式(JDK1.5之前)

1、创建步骤:

(1)定义子类继承Thread类。
(2)子类中重写Thread类中的run方法。
(3)创建Thread子类对象,即创建了线程对象。
(4)调用线程对象start方法:启动线程,调用run方法。

2、MyThread.java代码

public class MyThread extends Thread {
    public MyThread() {
        super();
    }
    /**
     * 重写Thread类的run()
     */
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

3、ThreadTest.java代码

public class ThreadTest {
    public static void main(String[] args) {
        // 创建Thread类的子类的对象
        MyThread t1 = new MyThread();
        // 启动线程;并调用当前线程的run()方法。
        t1.start();

        // 创建Thread类的子类的对象
        MyThread t2 = new MyThread();
        // 启动线程;并调用当前线程的run()方法。
        t2.start();
    }
}

4、注意点

(1)如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
(2)run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU调度决定。
(3)想要启动多线程,必须调用start方法。
(4)一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上的异常“IllegalThreadStateException”。

方式二:实现Runnable接口的方式(JDK1.5之前)

1、创建步骤:

(1)定义子类,实现Runnable接口。
(2)子类中重写Runnable接口中的run方法。
(3)通过Thread类含参构造器创建线程对象。
(4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
(5)调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。

2、MyThread.java代码

public class MyThread implements Runnable {
    /**
     * 实现类去实现Runnable中的抽象方法:run()
     */
    @Override
    public void run() {
        // 遍历100以内的偶数
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

3、ThreadTest.java代码

public class ThreadTest {
    public static void main(String[] args) {
        // 创建实现类的对象
        MyThread myThread = new MyThread();
        // 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
        Thread t1 = new Thread(myThread);
        t1.setName("线程1");
        // 通过Thread类的对象调用start():① 启动线程 ②调用当前线程的run()-->调用了Runnable类型的target的run()
        t1.start();
        
        // 再启动一个线程
        Thread t2 = new Thread(myThread);
        t2.setName("线程2");
        t2.start();
    }
}

4、方式一和方式二比较

开发中:优先选择:实现Runnable接口的方式
原因:(1) 实现的方式没类的单继承性的局限性
     (2) 实现的方式更适合来处理多个线程共享数据的情况。

联系:public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
      目前两种方式,要想启动线程,都是调用的Thread类中的start()

方式三:实现Callable接口的方式(JDK5.0新增)

1、创建步骤:

(1)创建一个实现Callable的实现类
(2)实现call方法,将此线程需要执行的操作声明在call()
(3)创建Callable接口实现类的对象
(4)将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
(5)将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
(6)获取Callable中call方法的返回值

2、MyThread.java代码

public class MyThread implements Callable<Integer> {
    /**
     * 实现call方法,将此线程需要执行的操作声明在call()中
     *
     */
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        // 计算100以内所有偶数的和
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {
                sum += i;
            }
        }
        // 返回计算结果
        return sum;
    }
}

3、ThreadTest.java代码

public class ThreadTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建Callable接口实现类的对象
        Callable<Integer> callable = new MyThread();
        // 将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        // 将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
        Thread t1 = new Thread(futureTask);
        t1.start();

        // 获取Callable中call方法的返回值
        // get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
        Integer sum = futureTask.get();
        System.out.println("总和为:" + sum);
    }
}

4、说明

为何说Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?
(1) call()可以返回值的。
(2) call()可以抛出异常,被外面的操作捕获,获取异常的信息。
(3) Callable是支持泛型的。

方式四:使用线程池

1、创建步骤:

(1)提供指定线程数量的线程池
(2)执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
(3)关闭连接池

2、实现Runnable接口方式

2.1、MyThread01.java代码

public class MyThread01 implements Runnable {
    /**
     * 实现类去实现Runnable中的抽象方法:run()
     */
    @Override
    public void run() {
        // 打印100以内的所有偶数
        for (int i = 0; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

2.2、MyThread02.java代码

public class MyThread02 implements Runnable {
    /**
     * 实现类去实现Runnable中的抽象方法:run()
     */
    @Override
    public void run() {
        // 打印100以内的所有奇数
        for (int i = 0; i <= 100; i++) {
            if (i % 2 != 0) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

2.3、ThreadTest.java代码

public class ThreadTest {
    public static void main(String[] args) {
        // 提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        // 执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
        service.execute(new MyThread01());
        service.execute(new MyThread02());
        // 关闭连接池
        service.shutdown();
    }
}

3、实现Callable接口方式

3.1、MyThread01.java代码

public class MyThread01 implements Callable<Integer> {
    /**
     * 实现call方法,将此线程需要执行的操作声明在call()中
     */
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        // 计算100以内所有偶数的和
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {
                sum += i;
            }
        }
        // 返回计算结果
        return sum;
    }
}

3.2、MyThread02.java代码

public class MyThread02 implements Callable<Integer> {
    /**
     * 实现call方法,将此线程需要执行的操作声明在call()中
     */
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        // 计算100以内所有奇数的和
        for (int i = 1; i <= 100; i++) {
            if (i % 2 != 0) {
                sum += i;
            }
        }
        // 返回计算结果
        return sum;
    }
}

3.3、ThreadTest.java代码

public class ThreadTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);

        // 创建Callable接口实现类的对象
        Callable<Integer> callable1 = new MyThread01();
        Callable<Integer> callable2 = new MyThread02();

        // 将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask<Integer> futureTask1 = new FutureTask<>(callable1);
        FutureTask<Integer> futureTask2 = new FutureTask<>(callable2);

        // 执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
        service.submit(futureTask1);
        service.submit(futureTask2);

        // 获取Callable中call方法的返回值
        Integer sum1 = futureTask1.get();
        System.out.println("偶数总和为:" + sum1);
        Integer sum2 = futureTask2.get();
        System.out.println("奇数总和为:" + sum2);

        // 关闭连接池
        service.shutdown();
    }
}

4、说明

使用线程池的好处:
(1)提高响应速度(减少了创建新线程的时间)
(2)降低资源消耗(重复利用线程池中线程,不需要每次都创建)
(3)便于线程管理

【奔跑吧!JAVA】有奖征文火热进行中:https://bbs.huaweicloud.com/blogs/265241

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。