Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。

线程的创建一般有以下三种:

一、继承Thread类创建线程类

步骤1.定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,
因此经常把run( )方法称为线程执行体
步骤2.创建Thread子类的实例,即创建了线程对象。你每new出一个对象,就意味着你创建了一条子线程
步骤3.调用线程对象的start()方法就可以启动该线程

public class ThreadTest {

    public static void main(String[] args) {
        for (int j=1; j <= 10; j++) {
            //通过Thread类的currentThread方法可以得到当前的线程名
            System.out.println("主线程的名字:" + Thread.currentThread().getName() + "-" + j);
            if(j == 5) {
                //创建第一条子线程Thread-0
                new FirstThread().start();//并且启动子线程
                //创建第二条子线程Thread-1,并启动
                new FirstThread().start();
            }
        }
    }

}
//通过继承Thread类来创建线程类
class FirstThread extends Thread {
	//重写run()方法,run()方法的方法体是线程的执行体
    @Override
    public void run() {
        super.run();
        //线程的执行体,此线程实现的任务代码
        //当线程类继承Thread时,直接使用this通过getName()即可获取到当前的线程名
        for (int i=0; i <= 10; i++ ) {
            System.out.println("子线程的名字:" + this.getName() + "-" + i);
        }
    }
}

运行的结果如下

第一次运行:

Java子线程如何运行子程序方法 java开启子线程_操作系统


第二次运行:

Java子线程如何运行子程序方法 java开启子线程_java_02


第三次运行:

Java子线程如何运行子程序方法 java开启子线程_操作系统_03


可以看到每次运行的结果都是不一样的,主线程main开始运行,依次打印main1-main5,当运行到5的时候,创建了两个新的子线程,这个时候三个线程在并发的执行,操作系统把cpu的时间片交给JVM这个进程,然后JVM拿到总的时间片再并发的分给三个线程去使用。Java程序中,这些线程对于cpu的时间片实施的是“抢”的制度,不能够保证哪个进程。这里是主线程先执行,执行到j=5,才创建两个子线程。当这两个子线程被创建出来以后,因为是并发执行,接下来执行的顺序就不能说清了,是由操作系统去调度的。我们能看到的是谁抢到谁执行,没抢到剩下的那两个就等,堵塞状态。

二、实现Runnable接口创建线程类

步骤1.定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体也称之为线程方法执行体。
步骤2.创建Runnable实现类的实例,并将此实例作为形参传入new Thread()的构造函数中,就可创建Thread线程对象

public class ThreadTest2 {
    public static void main(String[] args) {
        for (int j=1; j <= 10; j++) {
            //Thread.currentThread().getName()代码的意思是拿到当前线程的名字
            System.out.println("主线程的名字:" + Thread.currentThread().getName() + "-" + j);
            if(j == 1) {
                //创建第一条子线程Thread-0,并且启动子线程
                new Thread(new SecondThread(), "子线程1").start();
                //创建第二条子线程Thread-1,并启动
                new Thread(new SecondThread(), "子线程2").start();
            }
        }
    }
}
class SecondThread implements Runnable {

    @Override
    public void run() {
        //线程的执行体,此线程实现的任务代码
        for (int i=0; i <= 10; i++ ) {
            System.out.println("子线程的名字:" + Thread.currentThread().getName() + "-" + i);
        }
    }
}

运行的结果还是一样,是抢的机制,原理和上面一样,这里就不再解释了,放上几张运行结果再自己感受一下。

Java子线程如何运行子程序方法 java开启子线程_Java子线程如何运行子程序方法_04


Java子线程如何运行子程序方法 java开启子线程_Java子线程如何运行子程序方法_05


Java子线程如何运行子程序方法 java开启子线程_操作系统_06


在实际的运用中,还有一种一体式的写法,这种写法用的更多一些。

public class ThreadTest3 {

    public static void main(String[] args) {
        for (int j=1; j <= 10; j++) {
            //Thread.currentThread().getName()代码的意思是拿到当前线程的名字
            System.out.println("主线程的名字:" + Thread.currentThread().getName() + "-" + j);
            if(j == 1) {
                //创建第一条子线程Thread-0,并且启动子线程
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //线程的执行体,此线程实现的任务代码
                        for (int i=0; i <= 10; i++ ) {
                            System.out.println("子线程的名字:" + Thread.currentThread().getName() + "-" + i);
                        }
                    }
                }, "子线程1").start();
                //创建第二条子线程Thread-1,并启动
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //线程的执行体,此线程实现的任务代码
                        for (int i=0; i <= 10; i++ ) {
                            System.out.println("子线程的名字:" + Thread.currentThread().getName() + "-" + i);
                        }
                    }
                }, "子线程2").start();
            }
        }
    }
}

三、使用Callable和Future创建线程

步骤1.创建Callable接口的实现类,重写call()方法,该方法就是线程方法执行体,call()方法有返回值,再创建Callable实现类的实例。
步骤2.使用FutureTask类的实例,来包装Callable对象,即把callable的实例以形参的方式传入new FutureTask()的构造函数中
步骤3.使用FutureTask对象作为Thread对象的target创建启动线程。
步骤4.通过FutureTask实例对象调用get()方法得到子线程的返回值。

public class ThreadTest4 {
    public static void main(String[] args) {
        //创建Callable对象
        ThirdThread thirdThread = new ThirdThread();
        //创建FutureTask对象,并把callable以形参的方式传入FutureTask的构造方法内
        FutureTask<Integer> futureTask = new FutureTask<>(thirdThread);
        for (int j=1; j <= 10; j++) {
            // 通过Thread类的currentThread方法可以得到当前的线程名
            System.out.println("主线程的名字:" + Thread.currentThread().getName() + "-" + j);
            if(j == 1) {
                //创建线程并启动
                new Thread(futureTask, "子线程1").start();
            }
        }
        //获取子线程的返回值
        try {
            System.out.println("子线程返回的值是:" + futureTask.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class ThirdThread implements Callable {
    //call()方法称之为线程方法执行体,且该方法有返回值,可通过FutureTask实例对象调用get()方法得到子线程的返回值
    @Override
    public Object call() throws Exception {
        int i = 1;
        //线程的执行体,此线程实现的任务代码
        for (; i <= 10; i++ ) {
            System.out.println("子线程的名字:" + Thread.currentThread().getName() + "-" + i);
        }
        return i;
    }
}

运行结果:

Java子线程如何运行子程序方法 java开启子线程_Java子线程如何运行子程序方法_07


Java子线程如何运行子程序方法 java开启子线程_Java子线程如何运行子程序方法_08


Java子线程如何运行子程序方法 java开启子线程_操作系统_09

创建线程的三种方式对比

使用实现Runnable 、Callable接口的方式创建多线程

优点:

1.线程类只是实现了Runnable接口或Callable接口,同时还可以继承其他类。

2.多个线程可以共享一个target对象,非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码、数据分开,形成清晰的模型,较好的体现了面向对象的思想

Java子线程如何运行子程序方法 java开启子线程_操作系统_10


如图,只创建了一个SecondThread对象,但是通过着一个对象可以创建多个不同的线程

缺点:

1.编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。

使用继承Thread类的方式创建多线程

优点:
1.编写简单,如果要访问当前线程,无需使用Thread.currentThread()方法,可以直接使用this的方式获取当前线程
缺点:
1.因为线程类已经继承了Thread类,所以不能再继承其他的父类。

一般情况下,项目中推荐使用Runnable接口或Callable接口创建多线程。