Java多线程创建与运行

进程是系统进行资源分配和调度的基本单位。而线程是程序运行的最小单位。
一个进程里面会有多个线程共享进程的资源,而这又会导致一系列的数据安全和一致性问题,就需要我们认真去学习并发体系的相关知识去保证临界区资源的安全性。

那么java里面是怎么实现多线程的呢?

java实现了这样一个类,名叫Thread,是java的线程对象

进入到Thread源码中可以看到源码提供了两种创建多线程的方式

方式一:继承Thread类

class PrimeThread extends Thread {
    
    
    long minPrime;
    PrimeThread(long minPrime) {
    
    
        this.minPrime = minPrime;
    }

    public void run() {
    
    
        // compute primes larger than minPrime
         . . .
    }
}


PrimeThread p = new PrimeThread(143);
p.start();

方式二:实现Runnable接口

 class PrimeRun implements Runnable {
    
    
     long minPrime;
     PrimeRun(long minPrime) {
    
    
         this.minPrime = minPrime;
     }

     public void run() {
    
    
         // compute primes larger than minPrime
          . . .
     }
 }

将我们要运行的代码写到run()方法里面,这样新建的线程就会执行run()函数体里面的代码

使用继承Thread类实现的话,缺陷就在于java的单继承限制,而实现Runnable接口之后我们可以继承其他的类来实现更多的功能,扩展性更好。

通过Thread源码可以看到,有多种构造方法
在这里插入图片描述

其中一个,参数为实现了Runnable接口的实例

    public Thread(Runnable target) {
    
    
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

我们可以看到,我们只需要传递一个实现了Runnable接口的实例,就可以实现多线程。因此我随便找了一个实现了Runnable接口的类来实验验证。证实确实可以。

 TimerTask timerTask = new TimerTask() {
    
    
     @Override
     public void run() {
    
    
         for(int i=1;i<=20;i++){
    
    
             System.out.printf("I am TimerTask-1:%d\n",i);
         }
     }
 };
 
 Thread threadThree = new Thread(timerTask);
 threadThree.start();

我们进一步查看Thread的源码,发现有这样的字段

    /* What will be run. */
    private Runnable target;
    

在上面的那个构造函数中又会调用init()方法,而init()方法中又会有如下的代码,将我们传入的实例赋给Thread.target

this.target = target

而Thread 源码中重写了这个run()方法

    @Override
    public void run() {
    
    
        if (target != null) {
    
    
            target.run();
        }
    }

所以说Thread.run()方法实际上还是使用的我们传入的实现了Runnable接口的实例的run()方法

三、实现callable接口(有返回值)

    public static class CallbleTest implements Callable<Integer>{
    
    
        @Override
        public Integer call() throws Exception{
    
    
            return new Random().nextInt();
        }
    }
    //使用实现了Callable接口的类
    CallbleTest callbleTest = new CallbleTest();
    //下面这样是错误的,Thread类里面没有Callable接口的类的构造方法,只有Runnable接口的
    Thread threadFive = new Thread(callbleTest);
    threadFive.start();

但是这样是错误的,因为Thread的构造函数是这样子的,只能向Thread传递实现了Runnable接口的实例

    public Thread(Runnable target) {
    
    
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

我们可以使用Future来接收多线程的执行结果。

        //创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(10);
        //提交任务,由future 返回并发执行结果
        
        Future<Integer> future = executor.submit(new CallbleTest());
        System.out.println(future.isDone());
        System.out.println(future.get());
        System.out.println(future.isDone());

在《并发编程之美中》,作者还向我们推荐了FutureTask,可以在任务执行完毕后返回一个结果。
其实FutureTask 实现了RunnableFuture接口( 继承了Runnable 和Future接口),所以可以传入Thread的构造方法。

        //《并发编程之美》推荐
        FutureTask<Integer> futureTask = new FutureTask<>(new CallbleTest());
        new Thread(futureTask).start();
        try{
    
    
            int result  = futureTask.get();
            System.out.printf("利用futureTask执行实现了callable接口的线程,返回值:%d",result);
        }catch (ExecutionException e){
    
    
            e.printStackTrace();
        }

实验代码:

import java.util.Random;
import java.util.TimerTask;
import java.util.concurrent.*;


public class ThreadTest {
    
    

    public static class MyThread extends Thread{
    
    

        @Override
        public void run(){
    
    
            for(int i=1;i<=20;i++){
    
    
                System.out.printf("I am MyThread:%d\n",i);
            }
        }

    }

    public static class RunnableTest implements Runnable{
    
    

        @Override
        public void run() {
    
    
            for(int i=1;i<=20;i++){
    
    
                System.out.printf("I am RunnableTest:%d\n",i);
            }
        }
    }


    public static class CallbleTest implements Callable<Integer>{
    
    
        @Override
        public Integer call() throws Exception{
    
    
            return new Random().nextInt();
        }
    }




    public static void main(String[] args) throws InterruptedException, ExecutionException {
    
    

        MyThread thread = new MyThread();
        thread.start();

        RunnableTest runnableTest = new RunnableTest();
        Thread threadTwo = new Thread(runnableTest);
        threadTwo.start();

        TimerTask timerTask = new TimerTask() {
    
    
            @Override
            public void run() {
    
    
                for(int i=1;i<=20;i++){
    
    
                    System.out.printf("I am TimerTask-1:%d\n",i);
                }
            }
        };

        TimerTask timerTaskCopy = new TimerTask() {
    
    
            @Override
            public void run() {
    
    
                for(int i=1;i<=20;i++){
    
    
                    System.out.printf("I am TimerTask-2:%d\n",i);
                }
            }
        };


        Thread threadThree = new Thread(timerTask);
        threadThree.start();

        Thread threadFour = new Thread(timerTaskCopy);
        threadFour.start();

//        使用实现了Callable接口的类
        CallbleTest callbleTest = new CallbleTest();
//        下面这样是错误的,Thread类里面没有Callable接口的类的构造方法,只有Runnable接口的
//        Thread threadFive = new Thread(callbleTest);
//        threadFive.start();


        //创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(10);
        //提交任务,由future 返回并发执行结果

        Future<Integer> future = executor.submit(new CallbleTest());
        System.out.printf("线程执行是否完毕:%b\n",future.isDone());
        System.out.printf("线程执行结果:%d\n",future.get());
        System.out.printf("线程执行是否完毕:%b\n",future.isDone());


        //《并发编程之美》推荐
        FutureTask<Integer> futureTask = new FutureTask<>(new CallbleTest());
        new Thread(futureTask).start();
        try{
    
    
            int result  = futureTask.get();
            System.out.printf("利用futureTask执行实现了callable接口的线程,返回值:%d",result);
        }catch (ExecutionException e){
    
    
            e.printStackTrace();
        }


    }

}

实验结果如下,如果感觉不明显,可以增加i遍历的范围
在这里插入图片描述

文章内容来自对网络资料,书本的归纳整理和自己的实践,如果有理解错误或者不足的地方,还请不吝赐教。

猜你喜欢

转载自blog.csdn.net/qq_37774171/article/details/121247200