计算机上正在运行的程序就是一个进程,而每个进程可以拥有多个线程,每个线程是进程的一个执行场景/执行单元。
进程与进程之间是不共享资源的,线程和线程之间会共享堆、方法区,但是每个线程会有其对应的栈空间。线程的栈空间存放线程执行的时需要用到的东西,例如在run一个程序时,jvm会run这个程序的main方法,main方法中我们可能会定义一个变量,此时这个变量是一个临时变量,而这个变量会被存放在栈中,还有栈中可能会调用方法,这个方法如果是递归方法,那么这些方法会依次压入栈中,当程序运行结束,这个栈的内存就会被释放掉。
以上是我学习线程概念时的逻辑,我的逻辑思维是否有什么问题呢?
如果有问题,是什么导致的盲区?
如果没有,我有什么盲区是我没有发现的?
答:为什么没有疑问java中线程是怎么起作用的?
线程又怎么使用?java包中是怎么封装线程的?
缺:什么是多线程呢?
当一个计算机是多核的,就代表这个计算机是可以真正多线程的。
当一个计算机是单核的,那么处理器可以依靠快速的运算能力切换线程运行,这个时候给我们的感觉就是这个机器是多线程的。
线程的实现
线程的实现可以有3种方式:
第一种:
创建一个类,extends Thread ,即继承线程类,然后重写run方法,run方法必须重写。然后在main方法中new一个Thread对象,调用start方法,这个start方法会创建一个独立的栈空间,用来运行这个新建的类的run方法,注意这个run方法的调用是分支线程创建出来之后由线程自己调用的,不用程序员关心,类似主线程的main方法,start方法在开辟出一个栈空间之后就完成了使命,此时会接着运行main方法体里的代码,同时又运行了另一个类中的run方法体,这就实现了多线程。
第二种:
创建一个类,implements Runnable,要实现run方法。逻辑同上。
然后new这个类,当new Thread时将这个类的对象作为参数传入。
再用Thread对象调用start。
第三种:
匿名内部类。
这块发现了一些问题,我们看一下匿名内部类的编写方式;
Thread t = new Thread(new Runnable(){
public void run(){
for (;i<100;i++ ) {
System.out.println("my thread " + i + " is running");
}
}
})
可以看到匿名内部类的编写是new了一个接口,那么我们知道接口是不可以new一个对象的,这里偶然看到一个人是这么理解的:因为是匿名内部类,所以这个new 是new的一个类,Runnable是这个匿名类实现的接口,那么可以完整的理解为new class implements Runnable(){}。这种理解让我眼前一亮。
在控制台上我们看到run方法和main方法的输出会交互输出,并且频率不一,这是因为控制台只有一个,而且跟时间片、执行权有关?
答:这里需要了解一下线程的生命周期。线程的生命周期中共有5个状态,分别是新建状态、就绪状态、运行状态、阻塞状态、死亡状态。新建状态调用start方法然后转为就绪状态,run方法执行的时候为运行状态,当运行过程中有其他时间发生,例如执行sleep方法、输入数据等这时候线程会进入阻塞状态,将所占有的资源释放,等到执行完这些命令之后进入就绪状态继续等待时间片的分配,得到新的时间片之后继续执行剩下的run方法,run方法执行完后变成死亡状态。
Timer类:
定时器类
用于定时去执行一个线程
通过对象调用schedule方法,传三个参数
TimerTask对象参数:创建TimerTask对象时需要编写run方法,这个run方法就是定时任务需要执行的内容。
Date对象参数:给定时任务一个第一次执行时间,格式为格林威治时间。
毫秒数:period,一段时间;传一个毫秒数,表示每隔多少毫秒就执行一次定时任务。
FutureTask:
线程的第三种实现方式
在创建该对象时需要实现一个call方法,带对象类型返回值。
要获得这个值需要将FutureTask对象作为参数传给Thread,然后通过FutureTask对象调用get方法来获得这个返回值。
FutureTask ft = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
return 1 + 2;
}
});
Thread t = new Thread(ft);
t.start();
try {
Object obj = ft.get();
System.out.println(obj);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
需要注意的是如果缺少t.start()方法启动线程的话,这个返回值不会输出。