实现多线程方法与线程的生命周期

实现多线程的三种方法

方法一

继承Thread类

首先创建一个类继承Thread类
只有继承Thread才具备争抢资源的能力

package com.it04.Thread;

public class TestThread extends Thread{
    
}

线程对象要争抢资源,线程需要一个任务才能争抢资源,这个任务要放在方法中,这个方法必须是重写Thread类中的run方法,将线程的任务/逻辑写在run方法中。

package com.it04.TestThread;

public class TestThread extends Thread{

    @Override
    public void run() {
        //打印1-10
        for (int i = 1; i <=10 ; i++) {
            System.out.println(i);
        }
    }
}

创建一个测试类
在测试类中创建一个主线程与子线程,让它们两互相争抢资源

package com.it04.TestThread;

//测试类
public class Test {
    //main方法,程序的入口
    public static void main(String[] args) {
        //创建其他线程,与主线程争抢资源
        //具体的线程对象:子线程
        TestThread thread = new TestThread();
        //调用run方法,执行子线程中的任务
        thread.run();

        //主线程也打印1-10
        for (int i = 1; i <=10 ; i++) {
            System.out.println("主线程---"+i);
        }
    }
}

此时并没有出现争抢资源的一个状态,是因为run方法不能直接调用,直接调用会被当做一个普通方法。
想要子线程真正起作用需要启动线程:
调用Thread类中的start()方法

package com.it04.TestThread;

//测试类
public class Test {
    //main方法,程序的入口
    public static void main(String[] args) {
        //创建其他线程,与主线程争抢资源
        //具体的线程对象:子线程
        TestThread thread = new TestThread();
        //调用run方法,执行子线程中的任务
//        thread.run();
        thread.start();

        //主线程也打印1-10
        for (int i = 1; i <=10 ; i++) {
            System.out.println("主线程---"+i);
        }
    }
}

设置读取线程名字
用setName,getName方法来进行设置读取

package com.it04.TestThread;

//测试类
public class Test {
    //main方法,程序的入口
    public static void main(String[] args) {
        //创建其他线程,与主线程争抢资源
        //具体的线程对象:子线程
        TestThread thread = new TestThread();
        //调用run方法,执行子线程中的任务
//        thread.run();
        thread.setName("子线程");
        thread.start();

        //主线程也打印1-10
        for (int i = 1; i <=10 ; i++) {
            System.out.println("主线程---"+i);
        }
    }
}
package com.it04.TestThread;

public class TestThread extends Thread{

    @Override
    public void run() {
        //打印1-10
        for (int i = 1; i <=10 ; i++) {
            System.out.println(this.getName()+i);
        }
    }
}

给主线程设置名字
使用Thread.currentThread.setName与getName

 //主线程也打印1-10
        Thread.currentThread().setName("主线程----");
        for (int i = 1; i <=10 ; i++) {
            System.out.println(Thread.currentThread().getName()+i);

案例:购买火车票
每个窗口都是一个线程对象:
在这里插入图片描述
创建一个类继承Thread类,重写run方法
每个对象执行的代码放入run方法中

package com.it04.BuyTicketThread;

public class BuyTicketThread extends Thread{
    //创建一个有参构造器未窗口设置名字
    public BuyTicketThread(String name) {
        super(name);
    }
    //共10张票
    //用static让多个对象共享10张票
    static int ticketNum = 10;
    @Override
    public void run() {
        //每个窗口有100人抢票
        for (int i = 1; i <=100 ; i++) {
            //对票进行判断,票数大于0才抢票
            if (ticketNum > 0){
                System.out.println("在"+this.getName()+"买到了北京到上海的第"+ticketNum--+"张票");
            }
        }
    }
}

创建测试类

package com.it04.BuyTicketThread;

public class Test {
    public static void main(String[] args) {
        //多个窗口抢票:三个窗口三哥线程对象
        BuyTicketThread t1 = new BuyTicketThread("窗口1");
        t1.start();
        BuyTicketThread t2 = new BuyTicketThread("窗口2");
        t2.start();
        BuyTicketThread t3 = new BuyTicketThread("窗口3");
        t3.start();
    }
}

方法二

实现Runnable接口

实现接口里面的run方法

package com.it04.test;

public class TestThread implements Runnable{
    public void run() {
        //打印1-10数字
        for (int i = 1; i <=10 ; i++) {
            //要获取线程名字,只能使用Thread.currentThread().getName()
            System.out.println(Thread.currentThread().getName()+"----"+i);
        }
    }
}

创建测试类

package com.it04.test;

public class Test {
    public static void main(String[] args) {
        //创建子线程对象
        TestThread thread = new TestThread();
        //此时要将线程启动,但是TestThread并没有Thread方法
        //就需要创建一个Thread对象,此时通过构造器将它们关联起来
        //就可以使用start()方法了
        //因为Thread这个构造器里面可以传入一个Runnable接口的一个实现类
        //而thread刚好是实体类的一个对象
//      public Thread(Runnable target) {
//            init(null, target, "Thread-" + nextThreadNum(), 0);
//        }
        Thread thread1 = new Thread(thread,"子线程");
        thread1.start();


        //主线程打印1-10
        for (int i = 1; i <=10 ; i++) {
            System.out.println(Thread.currentThread().getName()+"-----"+i);
        }
    }
}

案例:购买火车票
创建一个类实现Runnable接口,实现run方法

package com.it04.test01;

public class ByTicketThread implements Runnable{

    int ticketNum=10;

    public void run() {
        for (int i = 1; i <=100 ; i++) {
                if (ticketNum > 0){
                    System.out.println("在"+Thread.currentThread().getName()+"买到了北京到上海的第"+ticketNum--+"张票");
                }
        }
    }
}

创建测试类

package com.it04.test01;
public class Test {
    public static void main(String[] args) {
        //定义一个线程对象
        ByTicketThread thread = new ByTicketThread();
        //窗口1
        Thread thread1 = new Thread(thread,"窗口1");
        thread1.start();
        //窗口2
        Thread thread2 = new Thread(thread,"窗口2");
        thread2.start();
        //窗口3
        Thread thread3 = new Thread(thread,"窗口3");
        thread3.start();
    }
}

在实际开发中,方式1继承Thread类,与方式2实现Runnable接口,用的多的应该是方式2

  1. 方式1 有java单继承的局限性,因为继承了Thread类,就不能在继承其他类了
  2. 方式2 共享资源的能力会比较强,不需要用static来修饰

方法三

实现Callable接口

对比第一种继承Thread和第二种实现Runnable接口创建线程的方式,,都需要一个run方法,但是这个run方法有它的不足

  1. 没有返回值,所以对象返回值的类型只能是void
  2. 不能抛出异常
    基于上面的不足,在JDK1.5之后就出现了第三种创建线程的方式:实现Callable接口
package com.it04.test02;

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

public class TestRandomNum implements Callable<Integer> {
    /*
    *
    1.实现Callable接口,可以不带泛型,如果不带泛型,那么call方式的返回值就是Object类型
    2.如果带泛型,那么call的返回值就是泛型对于的类型
    3.call方法可以有返回值,可以抛出异常

    V call() throws Exception;
    *
    * */
    public Integer call() throws Exception {
        //返回10以内的随机树
        return new Random().nextInt(10);
    }
}

class Test{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //定义一个线程对象
        TestRandomNum num = new TestRandomNum();
        //需要使用FutureTask才能转换为Runnable接口
        //因为FutureTask的父类RunnableFuture继承了Runnable接口
        FutureTask ft = new FutureTask(num);
        //由于Thread的构造器里面有一个可以传Runnable接口的实现类
        Thread thread = new Thread(ft);
        thread.start();
        //获取线程的返回值
        //使用ft里面的get方法来获取返回值
        Object o = ft.get();
        System.out.println(o);
    }
}

实现Callable接口
优点:1 有返回值 2能抛出异常
缺点: 线程创建比较麻烦

线程的生命周期

在这里插入图片描述


版权声明:本文为Falling_stars_原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。