多线程技术学习(二)

继承Thread方法

Java虚拟机允许应用程序同时运行多个执行线程,每个线程都有独立的栈空间,并共享一份堆内存。一个任务对象如果要能够并发执行,就需要继承Thread类。
在继承Thread类时,需要重写run()方法,在run()方法中构写的就是一条新的执行路径,也就是所需要并发执行的代码。需要注意的一点,当继承Thread的子类对象任务创建之后,并不是通过调用run()来触发运行,而是通过调用start()来启动任务。下面通过一段代码来观察。

import javax.swing.plaf.TableHeaderUI;
import java.io.*;
import java.util.concurrent.LinkedTransferQueue;

public class Demo {
    public static  void main(String[] args) throws IOException, InterruptedException {
        //创建一个名为mt的任务对象
        MyThread mt = new MyThread();
        //启动mt任务
        mt.start();
        
        //在main方法中打印10-15
        for(int j=10;j<15;j++){
            //打印   当前线程的名称:第j次打印
            System.out.println(Thread.currentThread().getName()+":第"+j+"次打印");
        }
    }
}
//创建一个名为MyThread的类,并继承Thread
class MyThread extends Thread {
    //重写run()方法,run()中的方法体就是一条新的执行路径
    @Override
    public void run() {
        for(int i=0;i<5;i++){
            //打印   当前线程名:第i次打印
            System.out.println(Thread.currentThread().getName()+":第"+i+"次打印");
        }
    }
}

然后我们来观察这段代码的输出结果

main:10次打印
Thread-0:0次打印
main:11次打印
Thread-0:1次打印
main:12次打印
Thread-0:2次打印
main:13次打印
Thread-0:3次打印
main:14次打印
Thread-0:4次打印

这里"main"是主线程的名称,"Thread-0"是我们创建的mt任务的线程名称。可以看见,这里main方法中的任务和mt中的任务是并发执行的。然后我们再重新运行这段代码几次,会发现有不一样的运行结果。

main:10次打印
Thread-0:0次打印
main:11次打印
main:12次打印
main:13次打印
Thread-0:1次打印
main:14次打印
Thread-0:2次打印
Thread-0:3次打印
Thread-0:4次打印

这是因为在java中,线程的调度机制是抢占式的。优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性)。所以,当"main"线程和Thread-0"线程都在运行时,哪个先抢占到CPU的使用权,哪个线程先执行。所以,当我们执行这段代码的时候,输入的结果不是唯一的。

实现Runable接口

除了继承Thread类外,还有一种常用的实现多线程的方法,那就是实现Runable接口。这个方法的实现方式与继承Thread类的方法类似,也是重写run()方法。然后调用Thread.start()启动线程。下面也是用一段代码来展示:

public class Demo {
    public static  void main(String[] args) throws IOException, InterruptedException {
        //创建一个名为mt的任务对象
        MyThread myThread = new MyThread();
        //将实现Runable类接口的对象以Thread来实现
        Thread mt = new Thread(myThread);
        //启动线程
        mt.start();

        //在main方法中打印10-15
        for(int j=10;j<15;j++){
            //打印   当前线程的名称:第j次打印
            System.out.println(Thread.currentThread().getName()+":第"+j+"次打印");
        }
    }
}
//创建一个名为MyThread的类,并实现Runable接口
class MyThread implements Runnable {
    //重写run()方法,run()中的方法体就是一条新的执行路径
    @Override
    public void run() {
        for(int i=0;i<5;i++){
            //打印   当前线程名:第i次打印
            System.out.println(Thread.currentThread().getName()+":第"+i+"次打印");
        }
    }
}
Thread-0:0次打印
main:10次打印
Thread-0:1次打印
main:11次打印
Thread-0:2次打印
main:12次打印
Thread-0:3次打印
main:13次打印
Thread-0:4次打印
main:14次打印

上面这两个实现多线程的方法很相似,因为继承父类Thread来实现多线程的方法,在父类Thread中已经实现Runable接口,子类只需要重写其中的run()方法即可。在实际代码编写中,第二种方法用的更多。相比继承父类Thread来实现多线程来说,直接实现Runable接口有更大的优势:

  1. 通过创建任务,然后给线程分配的方式来实现多线程,更适合多个线程同时执行 的情况。
  2. 可以避免单继承所带来的局限性(java中的类只能单继承,但是接口可以多实现)
  3. 任务与线程是分离的,提高了程序的健壮性。
  4. 在线程池中,线程池接收实现Runable类的任务对象,不接收Tread对象的线程。

Callable

除了上面两个创建线程的方式之外,还有一种实现Callable接口来实现多线程的方法。Callable使用步骤:

  1. 编写类实现Callable接口 , 实现call方法
class XXX implements Callable<T> {
	@Override
	public <T> call() throws Exception {
   		return T;
  	}
}
  1. 创建FutureTask对象 , 并传入第一步编写的Callable类对象
FutureTask<Integer> future = new FutureTask<>(callable);
  1. 通过Thread,启动线程
new Thread(future).start();

Runnable 与 Callable的相同点

  1. 都是接口
  2. 都可以编写多线程程序
  3. 都采用Thread.start()启动线程

Runnable 与 Callable的不同点

  1. Runnable没有返回值;Callable可以返回执行结果
  2. Callable接口的call()允许抛出异常;Runnable的run()不能抛出Callable获取返回值
  3. Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值