JAVA中线程(二)创建方式

157 阅读1分钟

这是我参与2022首次更文挑战的第23天,活动详情查看:2022首次更文挑战

前言:更具昨天我们所了解的进程、线程。线程的并行、串行、并发,以及线程的几个状态。那么今天我们来了解一下在JAVA中线程的创建方式和基本用法。

创建方式:

在JAVA中线程的创建方式有三种:通过继承Thread类创建线程类、通过实现Runnable接口创建线程类、通过Callable和Future接口创建线程。

第一种:通过继承Thread类创建线程类

/**
 * 通过继承Thread类创建线程类
 */
public class ThreadTest extends Thread{

    public static void main(String[] args) {

        //创建第一个线程
        ThreadTest FirstThreadTest = new ThreadTest();
        //创建第二个线程
        ThreadTest SecondThreadTest = new ThreadTest();

        //启动第一个线程
        FirstThreadTest.start();

        //启动第二个线程
        SecondThreadTest.start();
    }

    /**
     * 线程start后执行run方法
     */
    public void run() {
        for(int i=0;i < 5;i++) {
            //当通过继承Thread类的方式实现多线程时,可以直接使用this获取当前执行的线程
            //System.out.println(Thread.currentThread().getName() + ":" + i);
            System.out.println(this.getName() + ":"  + i);
        }
    }
}

我们来看一下线程的执行结果:

image.png

image.png

image.png

上图为运行时产生的三种不同情况。可以通过昨天我们讲解的线程的运行方式,从上可以判断为并发形式,在多线程下是存在资源竞争(抢占式),抢占的是时间片。

  • 时间片:时间片即CPU分配给各个程序的时间,每个线程被分配一个时间段,称作它的时间片,即该进程允许运行的时间,使各个程序从表面上看是同时进行的。如果在时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。而不会造成CPU资源浪费。在宏观上:我们可以同时打开多个应用程序,每个程序并行不悖,同时运行。但在微观上:由于只有一个CPU,一次只能处理程序要求的一部分,如何处理公平,一种方法就是引入时间片,每个程序轮流执行。总结:也就是CPU只有一个,他处理事情并不是一起的,运行的原则是:某个程序抢占到了时间片,那就先执行他,这个时间片的抢占就是我们所说的资源竞争。

image.png

我们来看一下Thread他的底层,他其实是实现了Runnable接口,从名义上来说,他是Runnable的一个重加工类,其中@FunctionalInterface注解是JDK1.8后的表示为“该类有且只有一个方法”。

第二种:通过实现Runnable接口创建线程类

/**
 * 通过实现Runnable接口创建线程类
 */
public class MyRunnable implements Runnable {

    public static void main(String[] args) {
        MyRunnable thread_target = new MyRunnable();
        //创建第一个线程,线程名称为FirstThread
        Thread FirstThreadTest = new Thread(thread_target,"FirstThread");
        //创建第二个线程,线程名称为SecondThread
        Thread SecondThreadTest = new Thread(thread_target,"SecondThread");
        //启动第一个线程
        FirstThreadTest.start();
        //启动第二个线程
        SecondThreadTest.start();
    }

    /**
     * 重写Runnable接口中的run方法
     */
    @Override
    public void run() {
        for(int i=0;i < 5;i++) {
            System.out.println(Thread.currentThread().getName()+ ":"  + i);
        }
    }
}

image.png 我们可以看到Runnable接口中有一个抽象方法run,所以在我们实现Runnable接口后,我就需要对run方法,进行重写。

第三种:通过Callable和Future接口创建线程

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

public class MyCallableTest {
    public static void main(String[] args) {
        // 方式一我们创建MyCallable对象使他实现Callable接口
        /**
         * import java.util.concurrent.Callable;
         *
         * public class MyCallble implements Callable<Integer> {
         *     @Override
         *     public Integer call(){
         *         int i = 0;
         *         for ( ; i < 5 ; i++ )
         *         {
         *             System.out.println(Thread.currentThread().getName()
         *                     + " i的值:" + i);
         *         }
         *         return i;
         *     }
         * }
         */
//        Callable<Integer> myCallable = new MyCallble;
        FutureTask<Integer> futureTask = new FutureTask<Integer>((Callable<Integer>)()->{
            int i = 0;
            for ( ; i < 5 ; i++ )
            {
                System.out.println(Thread.currentThread().getName()
                        + " i的值:" + i);
            }
            // call()方法可以有返回值
            return i;
        });
        for (int i = 0; i < 5; i++) {
            new Thread(futureTask , "我就是线程名称:").start();
        }

        try {
            //取得新创建的新线程中的call()方法返回的结果
            int sum = futureTask.get();
            System.out.println("sum = " + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

image.png

从上我们可以看到FutureTask实现了接口RunnableFuture,然后RunnableFuture接口他又实现了Runnable和Future。

总结:今天我们说了JAVA创建线程的三种形式,其实三种形式中,万变不离其宗的就是Runnable接口的实现。明天我们来说一下JAVA线程中一些常用的API。