Java创建线程的几种方式

后端 潘老师 6个月前 (11-07) 136 ℃ (0) 扫码查看

之前我们学习了线程的相关概念,那么你知道Java创建线程的方式有哪些吗?接下来给大家介绍下Java创建线程的四种方式

1、Java创建线程的4种方式

  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口
  • 线程池创建

1.1 方式1:继承Thread类

步骤:

  1. 创建一个继承于Thread类的子类
  2. 重写Thread类的run()方法 -> 此线程执行的操作声明在方法体中
  3. 创建当前Thread子类的对象
  4. 通过实例对象调用start()方法,启动线程 -> Java虚拟机会调用run()方法
public class MyThread extends Thread {
   // 共享数据要放在run()方法外边才能被共享且声明为static,否则就是每个线程都会调用run()方法,都会单独拥有一个run()方法里的独享数据,而非共享数据
    //eg: static int trick = 100;
    //定义指定线程名称的构造方法
    public MyThread(String name) {
        //调用父类的String参数的构造方法,指定线程的名称
        super(name);
    }
    // 重写run
    @Override
    public void run() {
        for (int i = 10; i > 0; i--) {
            System.out.println(Thread.currentThread().getName() + "线程正在运行任务,输出:"+i);
        }
    }
}

运行,当调用start()方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时会执行该线程的run()方法;

注意main()方法是主线程。

public class Test01 {
    public static void main(String[] args) {
   
        //创建自定义线程对象1
        MyThread mt1 = new MyThread("子线程1");
        //开启子线程1
        mt1.start();
        
        //创建自定义线程对象2,分别创建对象开启线程,不可以数据共享,若要共享需要创建static变量
        MyThread mt2 = new MyThread("子线程2");
        //开启子线程2
        mt2.start();
    }
}

我们还可以通过创建Thread类的匿名子类的匿名对象来运行线程:

// 创建Thread类的匿名子类的匿名对象,并启动线程
new Thread(){
    public void run(){
        // 执行的操作
    }
}.strat(); // 启动线程

1.2 方式2:实现 Runnable 接口

相比继承 Thread 类,Runnable避免了单继承的局限性;run方法只关于运行逻辑,解耦合;Thread 代表线程,Runnable 可运行的任务(线程要执行的代码)。

Runnable把线程和任务分开了,更容易与线程池等高级 API 配合,让任务类脱离了 Thread 继承体系,使用更加灵活。

步骤:

  1. 创建一个实现Runnable接口的类
  2. 实现接口中的run()方法 —-> 线程执行的操作声明在此方法中
  3. 创建实例对象
  4. 将此对象作为参数传到Thread类的构造器中,创建Thread类的实例
  5. 通过Thread的实例对象调用strat()方法,启动线程 —-> Java虚拟机会调用run()方法

要知道,虽然实现Runnable,但最终还是通过Thread类来实现的。

创建线程类,实现Runnable接口及run()方法;

public class MyRunnable implements Runnable{
    @Override
    public void run() {
   
        for (int i = 10; i > 0; i--) {
            System.out.println(Thread.currentThread().getName() + "线程正在运行任务,输出:"+i);
        }
    }
}

创建Runnable实现类的实例,并把实例作为Thread类的Target创建对象,并调用对象start()方法来启动线程;

public class Test002 {
    public static void main(String[] args) {
       
        MyRunnable myRunnable = new MyRunnable();
        // 创建启动第1个线程
        Thread t1 = new Thread(myRunnable);
        t1.start();
       // 创建启动第2个线程
       Thread t2 = new Thread(myRunnable);
        t2.start();
    }
}

我们还可以创建Runnable接口的匿名子类的匿名对象来运行线程:

new Thread(new Runnable(){
    public void run(){
        // 执行的操作
    }
}).start(); // 开启线程

1.3 方式3:实现 Callable 接口

与实现Runnable接口类似,但是Callable是支持返回值的。

创建线程类,实现Callable接口,实现call方法,与Runnable相比,Callable可以有返回值,返回值通过FutureTask进行封装。

public class MyCallable implements Callable<String> {
   
    @Override
    public String call() throws Exception {
   
        for (int i = 10; i > 0; i--) {
   
            System.out.println(Thread.currentThread().getName() + "线程正在运行任务,输出:"+i);
        }
        return "MyCallable";
    }
}

运行Callable线程:

public class Test003 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
     
        MyCallable myCallable = new MyCallable();

        FutureTask<String> futureTask1 = new FutureTask<>(myCallable);
        Thread thread1 = new Thread(futureTask1);

        thread1.start();
        System.out.println(futureTask1.get());
    }
}

1.4 方式4:通过线程池方式

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

那么有没有一种办法使得线程可以复用,即执行完一个任务,并不被销毁,而是可以继续执行其他的任务?

思路: 提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。

public class ThreadTest {
    static ExecutorService service = Executors.newFixedThreadPool(2);//实例创建的为使用的固定线程的线程池
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main方法开始");
        service.execute(new MyRunnable());//传入一个Runable对象
        service.shutdown();//关闭线程池
        System.out.println("main方法结束");
    }
}

具体关于线程池的使用方法可以参考文章:《Java创建线程池的几种方式具体实现

2、继承Thread类 和 实现Runnable接口对比

2.1. 共同点:

  • 都是通过Thread类中定义的start()来启动线程。
  • 都是通过Thread类或其子类的实例对象来创建线程。

2.2. 不同点:

  • Thread是继承
  • Runnable是实现

2.3. Runnable好处:

  • 通过实现的方式,避免了类的单继承的局限性
  • 自动共享数据,更适合处理有共享数据的业务逻辑
  • 实现了逻辑代码(在run()方法中)和数据(在创建线程的方法中)的分离

3、总结

以上就是Java创建线程的几种方式的具体实现,我们通过相关的示例进行了演示和讲解,希望对你有帮助!


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/back/10661.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】