java开启线程的四种方法

Posted Mr.Aholic

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java开启线程的四种方法相关的知识,希望对你有一定的参考价值。

文章目录

1、继承Thread类

/*
 * 创建步骤如下:
 * 1,定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务。因此把run方法称为线程执行体。
 * 2,创建Thread子类了的实例,即创建线程对象。本实例中是new一个ExtendThread,即可创建线程对象,也就是开启了一个线程
 * 3,调用线程对象的start()方法来启动该线程。
 *
 * 调用示例:
 * //循环10次即开启10个线程
 * for (int i = 0; i < 10; i++) 
 *     ExtendThread extendThread = new ExtendThread();
 *     extendThread.start();
 * 
 * */

1.1 代码实现

package com.zyz.mynative.demo03;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 15:51
 * @Description:继承Thread类,重写run方法(不推荐,因为java的单继承局限性)
 */
public class ExtendThread extends Thread 

    public static void main(String[] args) 
        ExtendThread thread = new ExtendThread();
        thread.start();
    

    /**
     * 重写Thread类的run(),这个方法称为线程执行体
     */
    @Override
    public void run() 
        doSomething();
    

    /**
     * 需要处理的任务
     */
    public void doSomething() 
        for (int i = 0; i < 10; i++) 
            System.out.println(Thread.currentThread().getName() + "执行" + i);
        
    


1.2 测试结果


2、实现Runnable接口

2.1 方式一:直接实现Runnable接口

避免单继承的局限性,方便共享资源,推荐使用

/*
 * 创建步骤如下:
 * 1,定义Runnable接口的实现类,并且实现run方法,这个方法同样是线程执行体
 * 2,创建Runnable实现类的实例,并以此实例对象作为Thread的target来创建Thread类,这个新创建的Thread对象才是真正的线程对象,即开启了新的线程
 * 3,调用线程对象的start()方法来开启该线程
 *
 * 调用示例:
 * //开启10个线程
 * for (int i = 0; i < 10; i++) 
 *     Thread thread = new Thread(new RunnableImpl());
 *     thread.start();
 * 
 * */

2.1.1 代码实现

package com.zyz.mynative.demo03;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 15:57
 * @Description:
 */
public class RunnableImpl implements Runnable 


    public static void main(String[] args) 
        RunnableImpl runnable = new RunnableImpl();
        Thread thread = new Thread(runnable);
        thread.start();

        /**
         * 简写
         * new Thread(runnable).start();
         */

    

    /**
     * 实现Runnable接口的run方法,这个方法称为线程执行体
     * */
    @Override
    public void run() 
        doSomething();
    

    /**
     * 需要处理的任务
     * */
    private void doSomething()
        for (int i = 0; i < 10; i++) 
            System.out.println(Thread.currentThread().getName() + "执行" + i);
        
    

2.1.2 测试结果

2.2 方式二:匿名内部类

/*
 * 创建步骤如下:
 * 匿名内部类本质上也是一个类实现了Runnable接口,重写了run方法,只不过这个类没有名字,直接作为参数传入Thread类
 *
 * 调用示例:
 * //开启10个线程
 * for (int i = 0; i < 10; i++) 
 *     Anonymous anonymous =new Anonymous();
 *     anonymous.myRun();
 * 
 *
 * */

2.2.1 代码实现

package com.zyz.mynative.demo03;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 15:57
 * @Description:
 */
public class RunnableImpl2 


    public static void main(String[] args) 
        RunnableImpl2 test = new RunnableImpl2();
        test.myRun();
    

    public void myRun()
        new Thread(new Runnable() 
            @Override
            public void run() 
                doSomething();
            
        ).start();
    

    /**
     * 需要处理的任务
     * */
    private void doSomething()
        for (int i = 0; i < 10; i++) 
            System.out.println(Thread.currentThread().getName() + "执行" + i);
        
    

2.2.2 测试结果


3、实现Callable接口

/*
 * 创建步骤如下:
 * 1,定义实现Callable<V>接口的实现类,实现call方法,这个方法是线程执行体
 * 2,创建Callable<V>实现类的实例,借助FutureTask得到线程执行的返回值
 * 3,将FutureTask的实例,作为Thread的target来创建Thread类
 * 4,调用start方法,开启线程
 *
 * 调用示例:
 * Callable<String> tc = new CallableImpl();
 * FutureTask<String> task = new FutureTask<>(tc);
 * new Thread(task).start();
 * try 
 *     System.out.println(task.get());
 *  catch (InterruptedException | ExecutionException e) 
 *     e.printStackTrace();
 * 
 *
 * 说明:
 * 1.与使用Runnable相比, Callable功能更强大些
 * 2.实现的call()方法相比run()方法,可以返回值
 * 3.方法可以抛出异常
 * 4.支持泛型的返回值
 * 5.需要借助FutureTask类,比如获取返回结果
 * Future接口可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
 * FutureTask是Futrue接口的唯一的实现类
 * FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
 *
 * */

3.1 代码实现

package com.zyz.mynative.demo03;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 16:08
 * @Description:
 */
public class CallableImpl implements Callable<String> 

    public static void main(String[] args) 
        Callable<String> tc = new CallableImpl();
        FutureTask<String> task = new FutureTask<>(tc);
        new Thread(task).start();
        try 
            System.out.println(task.get());
         catch (InterruptedException | ExecutionException e) 
            e.printStackTrace();
        
    

    private int ticket = 5;

    @Override
    public String call() throws Exception 
        for (int i = 0; i < 10; i++) 
            System.out.println(doSomething());
        

        return "出票任务完成";
    

    public String doSomething() 
        String result = "";
        if (this.ticket > 0) 
            result = "出票成功,ticket=" + this.ticket--;
         else 
            result = "出票失败,ticket=" + this.ticket;
        
        return result;
    


3.2 测试结果


4、创建线程池

/*
 * 创建步骤如下:
 * 1,定义Runnable接口的实现类,或者定义(继承Runnable接口的类)的实现类,并且实现run方法,这个方法是线程执行体
 * 2,创建一个自定义线程个数的线程池
 * 3,实例化Runnable接口的实现类
 * 4,将3步的实例,作为线程池实例的execute方法的command参数,开启线程
 * 5,关闭线程池
 *
 * 调用示例:
 * ExecutorService pool = Executors.newFixedThreadPool(2);
 * ThreadPool threadPool = new ThreadPool("AA");
 * ThreadPool threadPoo2 = new ThreadPool("BB");
 * pool.execute(threadPool);
 * pool.execute(threadPoo2);
 * pool.shutdown();
 *
 * 说明:
 * 示例中创建的是2个线程的线程池
 * execute方法是开启线程方法,实参要求是实现Runnable的类。所以,继承Thread类的子类也可以以线程池的方式开启线程
 *
 * */

4.1 代码实例

package com.zyz.mynative.demo03;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/2/15 16:11
 * @Description:
 */
public class ThreadPool implements Runnable 



    public static void main(String[] args) 
      ExecutorService pool = Executors.newFixedThreadPool(2);
      ThreadPool threadPool = new ThreadPool("AA");
      ThreadPool threadPoo2 = new ThreadPool("BB");
      pool.execute(threadPool);
      pool.execute(threadPoo2);
      pool.shutdown();
    

    String name;
    public ThreadPool(String name) 
        this.name = name;
    

    @Override
    public void run() 
        doSomething();
    

    /**
     * 需要处理的任务
     * */
    private void doSomething() 
        for (int i = 0; i < 10; i++) 
            System.out.println(Thread.currentThread().getName() + "执行" + i + ",name=" + this.name);
        
    


4.2 测试结果

资料参考:创建线程池的实现方法

Java 多线程:创建线程的四种方式

创建线程的四种方式

方式一:继承自Thread 类

方法步骤

  • 1.创建一个继承于Thread类的子类
  • 2.重写Thread 类的 run()方法-> 将线程的执行操作声明到run()中
  • 3.创建Thread 类的子类对象
  • 4.通过此对象调用start() 方法

例如: 输出0~100 以内所有的偶数

public class ThreadInheritTest {
    public static void main(String[] args) {
        //3.new 一个继承自Thread类的对象
        MyInheritThread myThread = new MyInheritThread();

        //4.启动线程
        myThread.start();
    }
}

//1.创建一个继承自Thread 的子类
class MyInheritThread extends Thread {
    //2.重写父类中Thread 的run方法
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

注意:我们不能通过run方法启动线程,而是应该通过start方法启动线程。

方式二:实现Runnable 接口

方法步骤

  • 1.创建一个实现Runnable接口的类。
  • 2.重写Runnable 类的 run()方法-> 将线程的执行操作声明到run()中
  • 3.创建实现当前Runnable 接口类的对象。
  • 4.将上述对象作为参数传入到Thread 类的构造器中。
  • 5.通过Thread类的对象调用start() 方法启动线程。

例如:输出1~100 以内所有的偶数

public class ThreadRunnableTest {
    public static void main(String[] args) {
        MyRunnableThread myRunnableThread = new MyRunnableThread();
        Thread t1 = new Thread(myRunnableThread);
        t1.start();
    }
}


class MyRunnableThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

方式三:实现Callable 接口

实现Callable 接口,该接口是JDK 5.0 中新增的。

步骤

  • 1.创建一个实现Callable 的实现类
  • 2.实现call()方法,将此线程需要执行的操作声明到call()方法中,注意该方法是有返回值的。
  • 3.创建Callable接口实现类的对象
  • 4.将此Callable实现类的对象(numThread)传递FutureTask构造其中,创建FutureTask对象。
  • 5.将FutureTask创建的对象,传递到Thread构造器中,在start该线程。
  • 6.如果关心线程中call()方法的返回值,则可以用个futureTask.get() 来抓取返回值。

例如:如下代码用于计算0~100以内,所有偶数的和

package com.jerry.thread10;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadCallableTest {
    public static void main(String[] args) {
        //3.创建Callable接口实现类的对象
        NumThread numThread = new NumThread();

        //4.将此Callable实现类的对象(numThread)传递FutureTask构造其中,创建FutureTask对象。
        FutureTask<Integer> futureTask = new FutureTask<Integer>(numThread);

        //5.将FutureTask创建的对象,传递到Thread构造器中,在start该线程。
        new Thread(futureTask).start();

        //6.如果关心线程中call方法的返回值,则可以用个futureTask.get() 来抓取返回值。
        try {
            //拿到其返回值
            Integer sum = futureTask.get();
            System.out.println("总和为: " + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

//1.创建一个实现Callable 的实现类
class NumThread implements Callable<Integer> {

    //2.实现call方法,将此线程需要执行的操作声明到call方法中,注意该方法是有返回值的。
    @Override
    public Integer call() throws Exception {
        int sum = 0;

        for (int i = 0; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
                sum += i;
            }
        }

        return sum;
    }
}

注意: 如何理解Callable 接口更强大?

  1. call() 方法是有返回值得。
  2. call() 可以抛出异常,被外面的操作捕获,获取异常信息。
  3. Callable 中是支持泛型的,这个泛型就是用futureTask.get()的返回值。

方式四:使用线程池(ThreadPool)

1、使用线程池的好处

  • 1.提高了响应速度(减少了创建线程的时间)
  • 2.降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
  • 3.便于管理,如可设置如下属性管理线程池
      1. corePoolSize 线程池大小
      1. maximumPoolSize 最大线程数
      1. keepAliveTime 线程没有任务时,最多保持多长时间后会终止

2、步骤

这里我们只演示了通过Runnable 实现接口方式构建run方法

  • 1.创建一个固定大小的线程池
  • 2.设置线程池属性
  • 3.使用execute执行当前线程,excute适合用于Runnable方式,当线程方法是Callable时要用submit
  • 4.使用shutdown方法关闭当前线程,线程池不用了,则关闭当前线程。

例如:如下代码创建了固定可容纳10个线程的线程池,当中有两个线程,分别输出奇数和偶数

package com.jerry.thread10;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class ThreadPoolTest {

    public static void main(String[] args) {
        //1.创建一个固定大小的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        //2.设置线程池属性
        ThreadPoolExecutor executor1 = (ThreadPoolExecutor) service;
        executor1.setCorePoolSize(10);

        //3.执行当前线程,适合用于Runnable,Callable 要用submit
        service.execute(new NumThread1());//执行线程1,用于输出偶数
        service.execute(new NumThread2());//执行线程2,用于输出奇数
        //4.关闭当前线程,线程池不用了,则关闭当前线程
        service.shutdown();
    }
}

class NumThread1 implements Runnable {
    //输出偶数
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

class NumThread2 implements Runnable {
    //输出奇数
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            if (i % 2 != 0) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

以上是关于java开启线程的四种方法的主要内容,如果未能解决你的问题,请参考以下文章

Java线程的四种创建方式

Java中常用的四种线程池

Java多线程的四种实现方式

Java 多线程:创建线程的四种方式

java线程实现的四种方式

浅析java中的四种线程池