进程和线程的区别
进程是系统进行资源分配和调度的基本单位, 线程是进程的子任务,是CPU调度和分派的基本单位
进程拥有完整的资源平台,如堆和方法区;线程只共享程序计数器和堆这些必不可少的资源
线程也拥有就绪、运行、阻塞三种状态,同样可以进行状态之间的转换
线程相比进程能减少开销:
-
线程创建时间更快:进程在创建过程中,还需要涉及内存管理信息和文件管理信息等诸多资源管理信息。而线程则无需这些,只会共享他们,同理,线程的终止时间比进程的要快
-
在同一进程中的线程切换要比进程切换快:因为线程具有相同的地址空间(虚拟内存共享),同一个进程的线程都具有同一个页表,那么在切换的时候不需要切换页表。而对于进程之间的切换,切换的时候要把页表给切换掉,而页表的切换开销是比较大的
-
由于同一进程的各线程间共享内存和文件资源,那么在线程之间数据传递的时候,就不需要经过内核了,这就使得线程之间的数据交互效率更高了
线程创建方式
- 继承Thread类重写run方法,好处是方便传参,可以通过set方法和构造函数传递参数。但由于Java是单继承,该类就不能再继承其他类;此外,任务和代码并未分离,当多个线程想要完成同一项任务时,需要创建多个线程类;任务没有返回值
class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(name + ":" + Thread.currentThread().getId());
}
}
class Main {
public static void main(String[] args) {
System.out.println("Main:" + Thread.currentThread().getId());
Thread thread1 = new MyThread("1");
Thread thread2 = new MyThread("2");
thread1.start();
thread2.run();
}
}
- 实现Runnable接口run方法,实现了多个线程公用一个任务逻辑。不能添加成员变量,只能使用主线程里面被声明为final 的变量【这点可能是为了保证参数的线程安全,如果真的想用参数的话,只能用主线程里被final修饰的变量】。而且和Thread类一样,任务没有返回值
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getId());
}
}
class Main {
public static void main(String[] args) {
System.out.println("Main:" + Thread.currentThread().getId());
MyRunnable myThread = new MyRunnable();
new Thread(myThread).start();
new Thread(myThread).start();
}
}
- FutureTask方式
class CallerTask implements Callable<String> {
@Override
public String call() throws Exception {
return "hello";
}
}
class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
new Thread(futureTask).start();
String s = futureTask.get();
System.out.println(s);
}
}
run()和start()方法区别
继承Thread类重写run方法实现线程创建,代码如下:
class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(name + ":" + Thread.currentThread().getId());
}
}
class Main {
public static void main(String[] args) {
System.out.println("Main:" + Thread.currentThread().getId());
Thread thread1 = new MyThread("1");
Thread thread2 = new MyThread("2");
thread1.start();
thread2.run();
}
}
运行结果
Main:1
2:1
1:24
根据输出,可以分析出
1.run方法不会创建新线程,在主线程中调用run方法和调用普通方法没有区别
2.start方法启动一个新线程,但是从先输出的为thread2的run方法内容可以看出,start() 中的 run 代码可以不执行完就继续执行下面的代码,进行线程切换
3.start() 实现了多线程,run() 没有
还需要注意,一个线程被启动,你不能重复调用该thread对象的start方法,代码和执行结果如下:
class Main {
public static void main(String[] args) {
System.out.println("Main:" + Thread.currentThread().getId());
Thread thread1 = new MyThread("1");
thread1.start();
Thread thread2 = new MyThread("2");
thread2.run();
thread1.start();
}
}
Main:1
1:24
2:1
Exception in thread "main" java.lang.IllegalThreadStateException
at java.base/java.lang.Thread.start(Thread.java:791)
at com.taotao.Main.main(SetModel.java:91)
总结:
run方法不会创建新线程,在主线程中调用run方法和调用普通方法没有区别
start方法启动一个新线程。通过start()方法来启动的新线程,处于就绪(可运行)状态,并没有运行,一旦得到cpu时间片,就开始执行相应线程的run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,run方法运行结束,此线程随即终止。并且一个线程被启动,你不能重复调用该thread对象的start方法。
参考:
小林Coding
Java并发编程之美