进程概念
进程就是运行中的程序,程序是由一段指令组成
线程概念
线程必须依赖于进程,进程存在,线程才存在
进程和线程的区别
进程是系统资源分配的基本单位,线程是CPU调度的基本单位,一个进程可以包含多个线程,同一个进程下面的资源共享很容易,但是进程之间的资源共享相对较难。
进程的几种状态
一共五种状态:新建 ,就绪 ,运行,阻塞,终止
其中三种基本状态:就绪,运行,阻塞
线程的几种状态
线程的状态: Thread类源码中定义了6种
public enum State {
NEW, //新建状态
RUNNABLE, //运行中状态
BLOCKED, //阻塞
WAITING,// 等待
TIMED_WAITING, //超时等待
TERMINATED;// 停止
}
注意:Java中,将就绪状态与运行状态统称为运行中
获取电脑逻辑处理器的数量:
Runtime.getRuntime().availableProcessors()
class MyTheard1 extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"extends Thread");
}
}
启动线程(启动线程,此时线程应该是处于就绪状态,当CPU将时间片给到这个线程的时候,这个线程才会处于运行状态),这里有两种写法
MyTheard1 myTheard1 = new MyTheard1();
myTheard1.start();
Thread thread1 = newMyTheard1();
thread1.start();
第二种写法其实就是多态的体现
这里有一个问题。我们调用的是start方法,但是我们用户重写的是run方法,如果我们直接调用run方法实际上是不会走创建线程这个流程的,run方法就是一个普通方法,可以直接调用,但是起不到开启线程调用的作用。
那么run方法是什么时候调用的呢?
这里看源码:
public synchronized void start() {
start0();
}
private native void start0();
这里将start方法里面的其他代码删除了,只保留了调用的start0,start0我们可以看到这是一个本地方法,底层是采用c++实现的,所以,我们的Java实际上是不能开启一个线程的,它实际上是通过底层的c++去调用操作系统的api来创建一个线程,当这个线程创建成功并且获取到了CPU的时间片的时候,就开始执行run方法了。
缺点:扩展性太差,Java中类只能单继承
class MyTheard2 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"implements Runnable");
}
}
启动线程:
MyTheard2 myTheard2 = new MyTheard2();
Thread thread2 = new Thread(myTheard2);
thread2.start();
这里启动线程的代码,实际上也是交由Thread来执行,我们分析一下,Thread的run和Runnable的run方法,看到底是执行的哪一个的。
看源码
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
这里就可以看出来,当我们的Runnable 不为空的时候,执行的是Runnable里面的run方法,这个target也就对应着源码里面的操作!
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
init 里面有一个地方 this.target = target;
整个逻辑就是,当我的Thread通过传入Runnable的构造函数创建的时候,执行的run方法就是Runnable的run方法,如果是直接new的话,就是继承Thread的那个类的run方法;
当然还有一种简单的写法
Thread thread = new Thread(()->{
System.out.println(Thread.currentThread().getName());
});
thread.start();
实际上这也是传入的一个Runnable实例,因为Runnable是一个函数式接口,可以用lambda表达式书写,这样代码写着很清晰。
class MyThread3 implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName()+"implements Callable");
return Thread.currentThread().getName()+"implements Callable";
}
}
Callable 接口创建线程稍微显得与上面的有区别,但是实际上也是一样的,只是这里我们实现的是call()方法并且这里有一个返回值。
先来看启动线程:
FutureTask futureTask = new FutureTask(new MyThread3());
Thread thread3 = new Thread(futureTask);
thread3.start();
首先,你会发现Thread类没有与Callable相关的构造函数,Thread其实是Runnable的一个实现类,这里的FutureTask其实也是Runnable的一个实现类,并且这个实现类有与Callable相关的构造函数,这样就能将Thread类也联系到一起了。他们之间的关系图如下:
另外,需要说明的是,通过继承Thread或者是实现Runnable接口实现的线程都没用返回值,但是通过Callable实现的线程会有一个返回值,返回值的类型可以根据实际情况设置。通过futureTask.get();可以获取这个值,值得注意的是,这个get方法会导致主线程阻塞,直到该线程执行完毕并返回值。