线程、进程、并发、并行
1、线程与进程
- 线程也被称作轻量级进程,线程是进程的执行单元;
- 线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程;
2、并发与并行
- 并发:指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果;
- 并行:指在同一时刻,有多条指令在多个处理器上同时执行;
创建线程的两种方式
1、继承Thread类创建线程类
通过继承Thread类来创建并启动多线程的步骤如下:
- 定义Thread类的子类,并重写该类的run()方法。把run()方法称为线程执行体;
- 创建Thread子类的实例,即创建线程对象;
- 调用线程对象的start()方法来启动该线程;
public class Test7 extends Thread{
@Override
public void run() {
//当线程类继承Thread类时,可以直接调用getName()方法来返回当前线程的名。
System.out.println(getName());
}
public static void main(String[] args) {
//调用Thread的currentThread方法获取当前线程
System.out.println(Thread.currentThread().getName());
new Thread(new Test7()).start();
}
}
//控制台打印
main
Thread-0
复制代码
2、实现Runnable接口创建线程类
通过实现Runnable接口来创建并启动多线程的步骤如下:
- 定义Runnable接口的实现类,并重写该接口的run()方法;
- 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象;
- 调用线程对象的start()方法来启动线程;
public class Test7 implements Runnable
{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
//调用Thread的currentThread方法获取当前线程
System.out.println(Thread.currentThread().getName());
new Thread(new Test7()).start();
//设定线程名字
//new Thread(new Test7(),"新线程").start();
}
}
//控制台打印
main
Thread-0
复制代码
3、创建线程的两种方式对比
采用实现Runnable接口的方式:
- 还可以继承其他类;
- 这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况;
- 如需访问当前线程,则必须使用Thread.currentThread()方法;
采用继承Thread类的方式:
- 已经继承了Thread类,所以不能再继承别的类;
- 如需访问当前线程,直接使用this即可获得当前线程;
推荐使用接口的形式;
4、对同一线程多次调用start()方法会怎样?
public class Test implements Runnable {
@Override
public void run() {
System.out.println("Test");
}
public static void main(String[] args) {
Thread thread = new Thread(new Test());
thread.start();
thread.start();
}
}
复制代码
运行控制台会报错: 这是因为start()方法会在调用开始前检查当前线程的状态。线程创建时默认状态为0,当调用start()方法后,线程状态被修改,所以再次调用start()方法会报错;
/* Java thread status for tools,
* initialized to indicate thread 'not yet started'
*/
private volatile int threadStatus = 0;
if (threadStatus != 0)
throw new IllegalThreadStateException();
复制代码
5、容易混淆的创建线程的几种方式? 网上对于创建线程的几种方式各有不同,有说两种、四种、三种的?那么到底是几种呢?
/* What will be run. */
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
复制代码
我们查看Thread源代码可知,其实本质上就两种,一种是继承Thread,重写run方法的话,那么Thread原本的run方法则不再存在,我们调用的就是我们重写后的方法。
还有一种就是实现Runnable并重写run方法,此种方式的话,我们传入了Runnable对象,如上代码进行判断,target不为空,则执行了target的run方法;
查看网上其它的创建方式,其实本质上都是调用的我们最基本的两种创建方式,只是对他们进行了封装!
6、如果同时使用两种创建方式运行多线程会出现什么结果?
public class Test{
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我来自Runnable");
}
}){
@Override
public void run() {
System.out.println("我来自Thread");
}
}.start();
}
}
复制代码
控制台打印:我来自Thread;
这是为什么呢?这是因为我们重写了Thread的run()方法,所以即使我们传入了Runnable对象,但是下面的代码已经不存在了。最终是直接执行我们所重写的run()方法
@Override
public void run() {
if (target != null) {
target.run();
}
}
复制代码