Java创建线程的三种方式

一、继承Thread类+重写run()方法

启动:创建子类对象+对象.start();

缺点:Java只支持单继承,如果我们的类已经从一个类继承,则无法再继承Thread类。

 1 package Thread;
 2 
 3 /**
 4  * 模拟龟兔赛跑
 5  * 1、创建多线程(方式一):继承 Thread + 重写run方法(线程体)
 6  * 2、使用线程:创建子类对象  + 对象.start()方法  线程启动
 7  */
 8 
 9 public class Demo01 {
10 
11     public static void main(String[] args) {
12         //创建子类对象
13         Rabbit rab = new Rabbit();
14         Tortoise tor = new Tortoise();  
15         //调用start方法 ,启动线程。 内部由CPU管控
16         rab.start(); //不要调用run方法,由内部自己调用。
17         tor.start();        
18         for(int i=0;i<30;i++)
19         {
20             System.out.println("main-->"+i);                
21         }
22     }
23 }
24 
25 class Rabbit extends Thread{
26     //线程体  一切从run开始
27     @Override
28     public void run() {
29         //线程体
30         for(int i=0;i<30;i++)
31         {
32                 System.out.println("兔子跑了"+i+"步");               
33         }
34     }
35 
36 }
37 
38 class Tortoise extends Thread
39 {
40     @Override
41     public void run() {
42         //线程体
43         for(int i=0;i<30;i++)
44         {
45                 System.out.println("乌龟跑了"+i+"步");               
46         }
47     }
48 }

二、实现Runnable接口+重写run方法

启动:使用静态代理

1、创建真实角色

2、创建代理角色

3、调用start()方法 启动线程

优点:可以同时实现继承,Runnable接口方式更加通用一些

1、避免单继承的局限性

2、便于共享资源

通过实现Runnable接口实现多线程(用到了静态代理设计模式)

 1 package Thread;
 2 
 3 /**
 4  * 推荐使用Runnable创建线程
 5  * 1、避免单继承的局限性
 6  * 2、便于共享资源
 7  */
 8 public class Demo03 {
 9     public static void main(String[] args) {
10         //1)、创建真实角色
11         Programmer pro = new Programmer();
12         //2)、创建代理角色+真实角色引用
13         Thread proxy = new Thread(pro);             
14         //3)、调用start()方法 启动线程
15         proxy.start();
16 
17         for(int i=0;i<100;i++){
18             System.out.println("一边聊QQ");
19         }
20     }
21 }
22 
23 /**
24  * 使用Runnable 创建进程
25  * 1、类实现Runable接口+重写run()方法
26  * 2、启动多线程 使用静态代理
27  *      1)、创建真实角色
28  *      2)、创建代理角色
29  *      3)、调用start()方法 启动线程
30  */
31 class Programmer implements Runnable{
32     @Override
33     public void run() {
34         for(int i=0;i<100;i++){
35             System.out.println("一边敲代码");
36         }
37     }
38 }
39 
40 
41 
42 package Thread;
43 
44 /**
45  * 抢票系统
46  * 方便共享资源
47  */
48 public class Demo04 implements Runnable{
49     private int num = 50;
50     @Override
51     public void run() {
52         while(true){
53             if(num<=0)
54             {
55                 break;//跳出循环
56             }
57             System.out.println(Thread.currentThread().getName()+"抢到了倒数第"+num--+"张。");           
58         }
59     }
60 
61     public static void main(String[] args) {
62         //真实角色
63         Demo04 web = new Demo04();
64         //代理
65         Thread t1 = new Thread(web,"德玛西亚");
66         Thread t2 = new Thread(web,"卡特琳娜");
67         Thread t3 = new Thread(web,"德邦总管");
68 
69         //启动线程
70         t1.start();
71         t2.start();
72         t3.start();
73     }
74 }

三、使用Callable接口创建多线程

Callable和Future接口

Callable是类似于Runnable的接口,实现*Callable接口的类和实现Runnable的类都是可被其它线程执行的任务*。

优点:可以返回值,可以抛异常。

缺点:繁琐。

步骤:

1、实现Callable接口+重写call方法

2、借助执行调度服务ExecutorService获取Future对象

ExecutorService ser = Executors.newFixedThreadPool(2);
Future<Integer> result= ser.submit(tortoise);

3、获取值result
int num = result.get(); get方法返回值是一个泛型

4、停止服务ser.shutdownNow();

package Thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 使用Callable接口方式创建多线程 
 */
public class Demo05 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        //1、创建线程
        ExecutorService ser = Executors.newFixedThreadPool(2);//开两个线程

        Race tortoise = new Race("老乌龟",1000);
        Race rabbit = new Race("小兔子",500);

        //2、获取Future对象
        Future<Integer> result1 = ser.submit(tortoise);
        Future<Integer> result2 = ser.submit(rabbit);

        Thread.sleep(2000);//2秒
        tortoise.setFlag(false);//停止线程体循环 设置flag = false;
        rabbit.setFlag(false);
        //3、获取值
        int num1 = result1.get();
        int num2 = result2.get();

        System.out.println("乌龟跑了-->"+num1+"步");
        System.out.println("兔子跑了-->"+num2+"步");
        //4、停止服务
        ser.shutdownNow();
    }
}

class Race implements Callable<Integer>
{
    private String name;//名称
    private long time;//延时时间
    private boolean flag = true;
    private int step = 0;//

    public Race() {
    }   
    public Race(String name) {
        super();
        this.name = name;
    }
    public Race(String name, int time) {
        super();
        this.name = name;
        this.time = time;
    }

    //有返回值了
    @Override
    public Integer call() throws Exception {
        while(flag){
            Thread.sleep(time);//延时
            step++;
        }
        return step;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public long getTime() {
        return time;
    }
    public void setTime(long  time) {
        this.time = time;
    }
    public boolean isFlag() {
        return flag;
    }
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
    public int getStep() {
        return step;
    }
    public void setStep(int step) {
        this.step = step;
    }
}

运行结果:

乌龟跑了-->3步
兔子跑了-->5步

四、Callable和Runnable不同点:
(1)Callable规定的方法是call(),而Runnable规定的方法是run();

2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的;

(3)call()方法可抛出异常,而run()方法是不能抛出异常的;

(4)运行Callable任务可拿到一个Future对象,Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果;

原文地址:https://www.cnblogs.com/luckyjcx/p/12268944.html