首页 > 编程语言 > 详细

Java-多线程

时间:2020-11-14 22:43:16      阅读:37      评论:0      收藏:0      [点我收藏+]

Java多线程1:三种实现方法。

分别是继承Thread类、实现Runnable接口、实现Callable接口通过FutureTask包装器来创建Thread线程。使用ExecutorService、Callable、Future实现有返回结果的多线程用到了线程池,后面再说。。

其中前两种方式线程执行完后都没有返回值,后两种是带返回值的。

原文链接 :https://www.cnblogs.com/felixzh/p/6036074.html

                   https://www.cnblogs.com/zivwong/p/9559931.html

1、继承Thread类创建线程
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:

定义新的类,继承Thread,重写run方法:

技术分享图片
1 public class MyThread extends Thread {
2     @Override
3     public void run(){
4         super.run();
5         System.out.println("执行子线程...");
6     }
7 }
View Code

测试用例:

技术分享图片
1 public class Test {
2     public static void main(String[] args) {
3         MyThread myThread = new MyThread();
4         myThread.start();
5         System.out.println("主线程...");
6     }
7 }
View Code

执行结果:

技术分享图片

 

 

 当然,这里的结果不代表线程的执行顺序,线程是并发执行的,如果多运行几次,打印顺序可能会不一样。多线程的运行过程中,CPU是以不确定的方式去执行线程的,故运行结果与代码的执行顺序或者调用顺序无关,运行结果也可能不一样。

这里还有一个需要注意的点就是main方法中应该调用的是myThread的start方法,而不是run()方法。调用start()方法是告诉CPU此线程已经准备就绪可以执行,进而系统有时间就会来执行其run()方法。而直接调用run()方法,则不是异步执行,而是等同于调用函数般按顺序同步执行,这就失去了多线程的意义了。


 

2、实现Runnable接口创建线程
如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口,如下:

技术分享图片
1 public class MyThread extends OtherClass implements Runnable {  
2   public void run() {  
3    System.out.println("MyThread.run()");  
4   }  
5 }  
View Code

举例说明:

技术分享图片
1 public class MyRunnable implements Runnable {
2     @Override
3     public void run() {
4         System.out.println("执行子线程...");
5     }
6 }
View Code

测试用例:

技术分享图片
1 public class Test {
2     public static void main(String[] args) {
3 
4         Runnable runnable = new MyRunnable();
5         Thread thread = new Thread(runnable);
6         thread.start();
7         System.out.println("主线程运行结束!");
8     }
9 }
View Code

运行结果:

技术分享图片

这里main中可以看到真正创建新线程还是通过Thread创建:

 

Thread thread = new Thread(runnable);

这一步Thread类的作用就是把run()方法包装成线程执行体,然后依然通过start去告诉系统这个线程已经准备好了可以安排执行。


3、使用Callable和Future创建线程

上面的两种方式都有这两个问题:

  1. 无法获取子线程的返回值
  2. run方法不可以抛出异常

为了解决这两个问题,我们就需要用到Callable这个接口了。说到接口,上面的Runnable接口实现类实例是作为Thread类的构造函数的参数传入的,之后通过Thread的start执行run方法中的内容。但是Callable并不是Runnable的子接口,是个全新的接口,它的实例不能直接传入给Thread构造,所以需要另一个接口来转换一下。

Java5提供了Future接口来代表Callable接口里call()方法的返回值,并为Future接口提供了一个实现类FutureTask,该实现类的继承关系如图所示:

技术分享图片

 

 

 

可以看到,该实现类不仅实现了Future接口,还实现了Runnable接口,所以可以直接传给Thread构造函数。

而关于FutureTask的构造函数如下:

技术分享图片

 

 所以这里面其实就是要比上一个方法再多一个转换过程,最终一样是通过Thread的start来创建新线程。有了这个思路,代码就很容易理解了:

 

 

 

import java.util.concurrent.Callable;

public class MyCallable implements Callable {
    int i = 0;
    @Override
    public Object call() throws Exception {
        System.out.println(Thread.currentThread().getName()+"  i的值:"+ i);
        return i++; //call方法可以有返回值
    }
}

测试类:

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

public class Test {
    public static void main(String[] args) {
        Callable callable = new MyCallable();
        for (int i = 0; i < 10; i++) {
            FutureTask task = new FutureTask(callable);
            new Thread(task,"子线程"+ i).start();
            try {
                //获取子线程的返回值
                System.out.println("子线程返回值:"+task.get() + "\n");
            }  catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

部分执行结果:

技术分享图片

 

 

 

 

 

 

 

 

Java-多线程

原文:https://www.cnblogs.com/carrothhh/p/13974455.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!