线程的基本介绍

进程&线程

进程:进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。 线程: 线程是进程的一个执行路径,一个进程中至少有一个线程,也可以有多线程,多线程会共享进程的资源。

线程资源分配

操作系统分配资源的时候会把资源分配给进程,但cpu比较特殊,它是被分配到线程的,因为真正使用资源的是线程(如果进程中只有一个线程,可以理解为单线程),所以也可以说cpu的资源分配资源是线程。cpu使用时间片的方式让线程轮流占用,当前线程使用完成后会让出时间片,交给其他线程使用,等再次轮到自己的时候才继续执行。那么如何记录程序执行到哪里了呢。java中采用计数器的方式来记录让出cpu的执行地址,当可以再次执行的时候,java可以根据计数器指定地址继续执行。 java中启动main函数就相当于启动了一个jvm(java虚拟机)进程。 每个线程都有自己的栈资源,用来存储该线程的局部变量,这些局部变量是该线程私有的,除此之外栈还有来调用线程存储的栈贞。 堆是进程中的最大一块内存,它的资源是共享的。主要用来存放使用new操作创建的对象实例。 `方法区用来存放jvm加载的类,常量及静态变量等信息,也是共享的。

线程创建和运行

java中有三种创建线程的方式,1继承Thread、2实现Runnable、3使用FutureTask

继承Thread
Thread类在java.lang包中是一个线程类,该类中的方法都是关于线程的,感兴趣的可以看一下。我们先来了解如何创建线程:

public class MyThread  extends Thread{
    @Override
    public void run() {
        System.out.println("这个是我创建的一个线程");
    }


    public static void main(){
        MyThread  my=new MyThread();
        my.start();
    }
}

如上面MyThread继承自Thread类,并且重写了run方法,表示创建了一个线程。在main函数中创建一个MyThread的实例,然后调用start方法启动线程。这里需要提示一下,在线程中,如果调用run方法执行只是表示调用了一个方法,并不能表示调用了一个线程,而调用start方法之后,表示该线程已经启动了。
注意:在调用start方法之后并不一位置线程可以执行了,而是线程处于就绪状态,在等待cpu分配时间片,一旦线程获得时间片就可以马上执行,线程run执行完成之后就会处于中止状态。(这里可以看下线程的五种状态)
继承Thread的我们可以直接通过this调用该线程中的方法(如果已经看了Thread类的方法,就会知道线程有很多方法),但是之前学过面向对象我们会知道java只允许单继承,所以一旦继承了Thread类,那么就不能再去继承其他类了,所以线程又提供了一个接口供开发者实现。

实现Runnable接口

public class RunableTask implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable实现");
    }


    public  static  void main(String []args){
        RunableTask runableTask=new RunableTask();
        new Thread(runableTask).start();
        new Thread(runableTask).start();
        new Thread(runableTask).start();


    }
}

如上面的代码,实现了Runnable接口,可以继续继承其他累了。但是要注意,在Runable接口中没有start方法,所以不能直接启动线程,而是需要Thread来接收Runnable接口代码来实现线程的启动,如果在Runable中调用线程中的方法,需要使用Thread.currentThread()方法。
上面两种方式实现了线程,有一个小的缺点,就是没有返回值,如果需要返回值的话,可以使用FutrueTask。

实现FutrueTask接口
public class CallerTask  implements Callable<String> {


    @Override
    public String call() throws Exception {
        return "hello thread";
    }


    public static  void main(String []args){
        //创建异步任务
        FutureTask<String > futureTask=new FutureTask<String>(new CallerTask());
        //启动线程
        new Thread(futureTask).start();
        try {
            //等待任务执行返回结果
            String s = futureTask.get();
            System.out.println(s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

上面代码实现了Cllable接口,重写了call方法(接口的泛型就是call方法的返回值)。FutureTask通过构造方法接收Callable的子类,创建了异步线程。最后通过FutureTask的get方法执行了方法,获得返回值。

总结:

继承Thread的有优点是方便传参,可以在子类里面添加成员变量,通过set方法或构造方式进行传递,可以直接的获取线程中的方法.。但是java只支持单继承,不能继承其他类。 实现Runnable的接口,只能使用主线程里被声明为final的变量,不限制继承。 实现FutureTask接口,可以使用返回值。