Bootstrap

Java线程创建的四种方式

目录

1.继承Thread类

2. 实现Runnable接口

3.实现Callable接口

4.通过线程池创建线程

(1)使用线程池的好处

(2)线程池的四种创建方式

1.继承Thread类

流程:

  • 创建一个子类继承Thread类
  • 在这个子类中重写Thread的run方法
  • 创建子类的实例对象,通过调用该实例对象的 start() 方法 ,启动线程

模拟创建一个线程打印0-10的数。代码如下:

        ①创建子类MyThread继承Thread类

public class MyThread extends Thread{
    @Override
    public void run() {
        for(int i=0;i<=10;i++){
            System.out.println(i);
        }
    }
}

        ②创建一个测试类Test,创建子类的实例对象,通过调用start() 方法 ,启动线程

public class Test {
    public static void main(String[] args) {
        MyThread myThread=new MyThread();
        myThread.run();
    }
}

        ③运行结果

2. 实现Runnable接口

流程:

  •   创建一个子类实现 Runnable 接口。
  •   子类中重写 Runnable 接口中的 run 方法。
  •  通过 Thread 类含参构造器创建线程对象。
  • 将Runnable 接口的子类对象作为实际参数传递给 Thread 类的构造器中。
  • 调用Thread 类的 start 方法:开启线程,调用 Runnable 子类接口的 run 方法。

模拟创建一个线程打印0-10的数。代码如下:

        ①创建一个子类实现Runnable接口 ,重写Runnable接口中的run方法。

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for(int i=0;i<=10;i++){
            System.out.println(i);
        }
    }
}

        ②创建一个测试类Test,创建子类的实例对象,作为实际参数传递给Thread类的构造器中通过调用start() 方法 ,启动线程

public class Test {
    public static void main(String[] args) {
        MyRunnable myRunnable=new MyRunnable();
        Thread thread=new Thread(myRunnable);
        thread.run();
    }
}

        ③运行结果:

实现Runnable接口创建线程的好处:

  • Java中只能进行单继承,使用实现Runnable接口创建线程可以避免单继承的局限性
  • 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。

3.实现Callable接口

流程:

  • 创建一个子类实现Callable接口,需要注意的是我们需要确定方法的返回值类型
  • 重写接口的call方法
  • 由于Thread类的构造函数并不能传实现Callable接口的对象,所以我们需要使用FutureTask类,这个类实现了RunnableFuture接口,并且该类的有参构造器参数是实现Callable接口的对象
  • 将实现Callable接口的对象以传参的方式传进FutureTask的有参构造器,再将FutureTask的对象传进Thread的有参构造器
  • 使用FutureTask对象的ge()方法获取Callable的返回值

代码:

        ①创建子类实现Callable接口,确定返回值类型

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("Callable创建线程!");
        return 10;
    }
}

        ②创建测试类Test,打印call方法的返回值

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask=new FutureTask(new MyCallable());
        Thread thread=new Thread(futureTask,"task");
        thread.run();
        int res= (int) futureTask.get();//通过get方法获得call方法的返回值;
        System.out.println(res);
    }
}

        ③运行结果

        使用Callable接口创建线程可以满足前两种创建线程缺少的一项功能,就是当线程终止时(即run()完成时),我们无法使线程返回结果,通过FutureTask对象的get方法我们就可以拿到线程的返回值。

4.通过线程池创建线程

(1)使用线程池的好处

  • 可以降低资源的消耗 ,避免对单个线程创建销毁造成的消耗
  • 可以提高反应速度,任务可以不需要等到线程创建就能执行
  • 可以提高线程的可管理性,使用线程池可以对线程进行统一分配调优和监控

(2)线程池的四种创建方式

        Java通过Executors提供了四种线程池

①newCachedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

代码:

public class ThreadPoolTest01 {
    public static void main(String[] args) {
        //创建线程池,可重复利用
        ExecutorService executorService= Executors.newCachedThreadPool();
        //创建5个线程
        for(int i=0;i<10;i++){
            int num=i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+":线程"+num+"已经执行");
                }
            });
        }
    }
}

运行结果:

​​​​​​​

newFixedThreadPool:需要指定线程池的容量大小,可控制线程的最大并发数,超出的线程需要在队列中等待。

public class ThreadPoolTest02 {
    public static void main(String[] args) {
        //创建一个长度为5的线程池
        ExecutorService executorService= Executors.newFixedThreadPool(5);
        for(int i=0;i<10;i++){
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
}

运行结果:由于一共10个任务,但是设定了线程池的容量是5,所以一次只能执行5个任务,剩下的任务需要等待前五个任务执行结束之后运行。所以运行结果为先打印出5个线程的名字,两秒之后,打印出后五个线程的名字。

 ③newScheduledThreadPool:支持定时及周期性任务执行

代码:延迟三秒后执行

public class ThreadPoolTest03 {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(5);
        for(int i=0;i<10;i++){
            scheduledExecutorService.schedule(new Runnable() {
                @Override
                public void run() {

                    System.out.println(Thread.currentThread().getName());
                }
            },3,TimeUnit.SECONDS);
        }
    }
}

运行结果:运行三秒后

 ④newSingleThreadExecutor:单程化线程池,该线程池中只有一个线程,按照某种指定顺序去执行。

代码:

public class ThreadPoolTest04 {
    public static void main(String[] args) {
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            singleThreadExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }
    }
}

运行结果:由于只有一个线程,一共10个任务,所以这个线程会一次执行完这个任务,先睡眠两秒后打印出当前线程的名字,共执行10次,直到所有任务执行结束。

;