Java线程的学习_线程的创建和启动

线程的创建和启动

Java使用Thread类代表线程,所有线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流(一段顺序执行的的代码)。Java使用线程执行体来代表这段流程。

创建线程有三个方法:
- 方法1.继承Thread类创建线程类
- 方法2.实现Runnable接口创建线程类
- 方法3.使用Callable和Future创建线程


方法1

package com.thread.create_and_start;

/**
 * 继承Thread类创建多线程
 * 创建并启动多线程步骤:
 * 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务。因此把run()方法称为线程执行体
 * 创建Thread子类的实例,即创建了线程对象
 * 调用线程对象的start()方法来启动该线程
 * @author tengyu
 *
 */
public class FirstThread extends Thread{

    private int i;
    //重写run()方法,run()方法的方法体就是线程执行体
    public void run(){

        for(; i < 10; i++){
            //当线程类继承Thread类时,直接使用this即可获取当前线程
            //Thread对象的getName()返回当前线程的名字
            //因此可以直接调用getName()方法返回当前线程的名字
            System.out.println(getName()  + " " + i);
        }
    }

    public static void main(String[] args){

        for(int i = 0; i < 10; i++){
            //调用Thread的currentThread()方法获取当前线程
            System.out.println(Thread.currentThread().getName() + " " + i);
            if(i == 2){
                //创建并启动第一个线程
                new FirstThread().start();
                //创建并启动第二个线程
                new FirstThread().start();
            }
        }
    }
}

得到结果:

main 0
main 1
main 2
Thread-0 0
Thread-0 1
main 3
Thread-0 2
Thread-1 0
main 4
main 5
main 6
Thread-1 1
Thread-0 3
Thread-1 2
main 7
Thread-1 3
Thread-0 4
Thread-0 5
Thread-0 6
Thread-0 7
Thread-0 8
Thread-0 9
Thread-1 4
Thread-1 5
main 8
main 9
Thread-1 6
Thread-1 7
Thread-1 8
Thread-1 9

当循环变量i=2时,创建并启动了两个新线程。并且两个线程的循环变量不连续表明它们没有共享数据。
上面程序用到的方法:
Thread.currentThread():currentThread()是Thread类的静态方法,该 方法总是返回当前正在执行的线程对象。
getName():该方法是Thread类的实例方法,该方法返回调用该方法的线程名字。

方法2

package com.thread.create_and_start;

/**
 * Runnable接口创建线程类
 * 步骤:
 * 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体
 * 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象
 * 调用线程对象的start()方法来启动线程
 * @author tengyu
 *
 */
//通过实现Runnable接口来创建线程类
public class SecondThread implements Runnable{

    private int i;
    public static void main(String[] args) {
        // TODO 自动生成的方法存根
        for(int i = 0; i < 10; i++){
            System.out.println(Thread.currentThread().getName() + " " + i);
            if( i == 2){
                SecondThread st = new SecondThread();
                //通过new Thread(target , name)方法创建新线程
                new Thread(st , "新线程1").start();
                new Thread(st , "新线程2").start();
            }
        }
    }

    @Override
    //接口需要实现的方法,线程执行体
    public void run() {
        // TODO 自动生成的方法存根
        for( ; i < 10; i++){
            //当线程类实现Runnable接口时
            //如果想获取当前线程,只能用Thread.currentThread().getName()
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }

}

运行程序:

main 0
main 1
main 2
新线程1 0
新线程1 1
新线程1 2
新线程1 3
新线程1 4
新线程1 5
新线程1 6
main 3
新线程2 7
新线程1 7
新线程2 8
main 4
新线程1 9
main 5
main 6
main 7
main 8
main 9

线程1,2的i变量是连续的,说明采用该方法创建的多个线程可以共享线程类的实例变量。

方法3

package com.thread.create_and_start;

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

/**
 * 使用Callable和Future创建线程
 * 创建并启动有返回值的线程的步骤
 * 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,且该call()方法有返回值,再创建
 *  Callable实现类的实例
 * 使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值
 * 使用FutureTask对象作为Thread对象的target创建并启动线程
 * 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
 * @author tengyu
 *
 */
public class ThirdThread {

    public static void main(String[] args){
        //创建Callable对象
        ThirdThread rt = new ThirdThread();
        //先使用Lambda表达式创建Callable<Integer>对象
        //使用FutureTask来包装Callable对象
        FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{
            int i = 0;
            for(; i < 10; i++){
                System.out.println(Thread.currentThread().getName() + " 的循环变量i的值:"
                        + i);
            }
            //call()方法可以有返回值
            return i;
        });
        for(int i = 0; i < 10; i++){
            System.out.println(Thread.currentThread().getName() + " 的循环变量i的值:"
                    + i);
            if(i == 2){
                //实质是以Callable对象来创建并启动线程
                new Thread(task, "有返回值的线程").start();
            }
        }
        try{
            //获取线程返回值
            System.out.println("线程返回值:" + task.get());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

运行结果:

main 的循环变量i的值:0
main 的循环变量i的值:1
main 的循环变量i的值:2
main 的循环变量i的值:3
main 的循环变量i的值:4
main 的循环变量i的值:5
main 的循环变量i的值:6
main 的循环变量i的值:7
main 的循环变量i的值:8
main 的循环变量i的值:9
有返回值的线程 的循环变量i的值:0
有返回值的线程 的循环变量i的值:1
有返回值的线程 的循环变量i的值:2
有返回值的线程 的循环变量i的值:3
有返回值的线程 的循环变量i的值:4
有返回值的线程 的循环变量i的值:5
有返回值的线程 的循环变量i的值:6
有返回值的线程 的循环变量i的值:7
有返回值的线程 的循环变量i的值:8
有返回值的线程 的循环变量i的值:9
线程返回值:10

Callable接口可以看做是Runnable接口的加强版,Callable接口提供了一个call()方法可以作为线程执行体,但是call()方法比run()方法更强大。强大之处在于call()方法可以有返回值,并且可以抛出异常。
call()方法并不能直接调用,所以使用Future接口来代表返回值。

三种方法的优缺点

通过继承Thread类或实现Runnable、Callable接口都可以实现多线程,不过实现Runnable接口与实现Callable接口的方式基本相同,只是Callable接口里定义的方法有返回值,可以声明抛出异常而已。因此把Runnable接口和Callable接口归为一种方式。
采用实现Runnable、Callable接口的方式创建多线的优缺点:
-线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
-在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
-劣势是编程稍稍复杂,如果需要访问当前线程,则必须使用Thread.currentThread()方法。
采用继承Thread类的方式创建多线程的优缺点:
-劣势,不能继承其它父类,因为已经继承了Thread类。
-优势,编写简单,访问当前线程直接使用this即可。

总体来讲,一般采用Runnable接口和Callable接口的方式来创建多线程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章