体验多线程抢占 CPU 资源的测试


主线程在调用时与多线程互不影响,代码如下所示:

  1. public class test0 {
  2. public static void main(String[] args) {
  3. System.out.println("hello world");
  4. MyRunnable myRunnable = new MyRunnable();
  5. Thread thread = new Thread(myRunnable);
  6. thread.start();
  7. System.out.println("hello world");
  8. }
  9. }
  10. class MyRunnable implements Runnable{
  11. @Override
  12. public void run(){
  13. System.out.println("hello myRunnable" + Thread.currentThread().getName());
  14. }
  15. }

按正常逻辑来讲,此时应该先输出 2 个 hello world,再输出 hello myRunnable,但由于我们调用的是多线程,所以输出结果如下所示:
体会 start 与 run 函数之间的区别 - 图1
此时线程是相对混乱的,由 CPU 自行进行分配,所以不要在意此时的执行顺序,但是从此处可以看出,java 主线程与多线程之间的区别。

start() 和 run() 之间的区别

start()

start() 函数 API 定义:
使该线程开始执行,Java 虚拟机调用该线程的 run 方法。结果是两个线程并发地运行;当前线程(从调用返回给 start 方法)和另一个线程(执行其 run 方法)。多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
start() 函数启动线程执行以下任务:

  • 它统计了一个新线程
  • 线程从 New State 移动到 Runnable 状态。
  • 当线程有机会执行时,它的目标 run() 方法将运行。
  • start() 方法不能多次重复调用,否则抛出 java.lang.IllegalStateException 异常;

小结:
start() 函数用来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕,可以直接继续执行下面的代码;通过调用 Thread 类的 start() 方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。 然后通过此 Thread 类调用方法 run() 来完成其运行操作的, 这里方法 run() 称为线程体,它包含了要执行的这个线程的内容。 Run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程。

run()

run() 函数 API 定义:
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。Thread 的子类应该重写该方法。
run() 函数启动:
线程类的 run() 方法是 Runnable 接口的一个抽象函数,由 java 虚拟机直接调用的,不会创建的新线程。所以可以被多次调用,因为它只是一个抽象函数。
小结:
run() 函数只是类的一个普通函数而已,如果直接调用 run 方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待 run 方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。

IllegalComponentStateException 异常

IllegalComponentStateException 异常概念

IllegalComponentStateException 异常是 RuntimeException 异常的父类,指无效状态异常,在不合理或不正确时间内唤醒一方法时出现的异常信息。换句话说,即 Java 环境或 Java 应用不满足请求操作。
多线程就是分时利用 CPU,宏观上让所有线程一起执行 ,也叫并发。在真正工作及学习使用中,想要异步的情况下需尽可能使用 start() 函数。但是 start() 方法不能多次重复调用,否则抛出 java.lang.IllegalStateException 异常;

重复调用 start() 报出 IllegalComponentStateException 异常

在 project 中创建 test2.java 文件

  1. public class test2{
  2. public static void main(String[] args){
  3. Thread MyThread = new MyThread();
  4. for(int i = 0; i < 2; i++){
  5. MyThread.start();
  6. }
  7. }
  8. }
  9. class MyThread extends Thread{
  10. @Override
  11. public void run(){
  12. System.out.println("hello myThread" + Thread.currentThread().getName());
  13. }
  14. }

public class test2{ public static void main(String[] args){ Thread MyThread = new MyThread(); for(int i = 0; i < 2; i++){ MyThread.start(); } } } class MyThread extends Thread{ @Override public void run(){ System.out.println(“hello myThread” + Thread.currentThread().getName()); } }
打开 terminal,输入命令编译并运行:
javac test2.java java test2
运行结果将会抛出 IllegalComponentStateException 异常,如下所示:
体会 start 与 run 函数之间的区别 - 图2

非重复调用 start() 不会报出异常

在 project 中创建 test3.java 文件

  1. public class test3{
  2. public static void main(String[] args){
  3. for(int i = 0; i < 2; i++){
  4. Thread MyThread = new MyThread();
  5. MyThread.start();
  6. }
  7. }
  8. }
  9. class MyThread extends Thread{
  10. @Override
  11. public void run(){
  12. System.out.println("hello myThread" + Thread.currentThread().getName());
  13. }
  14. }

public class test3{ public static void main(String[] args){ for(int i = 0; i < 2; i++){ Thread MyThread = new MyThread(); MyThread.start(); } } } class MyThread extends Thread{ @Override public void run(){ System.out.println(“hello myThread” + Thread.currentThread().getName()); } }
打开 terminal,输入命令编译并运行:
javac test3.java java test3
由于该实例每次都重新创建 MyThread 线程,而非在一个线程中频繁调用start()函数,所以运行结果将执行 2 次 Hello MyThread();
体会 start 与 run 函数之间的区别 - 图3

重复调用 .run() 不会报出异常

在 project 中创建 test4.java 文件

  1. public class test4{
  2. public static void main(String[] args){
  3. Thread MyThread = new MyThread();
  4. for(int i = 0; i < 2; i++){
  5. MyThread.run();
  6. }
  7. }
  8. }
  9. class MyThread extends Thread{
  10. @Override
  11. public void run(){
  12. System.out.println("hello myThread" + Thread.currentThread().getName());
  13. }
  14. }

public class test4{ public static void main(String[] args){ Thread MyThread = new MyThread(); for(int i = 0; i < 2; i++){ MyThread.run(); } } } class MyThread extends Thread{ @Override public void run(){ System.out.println(“hello myThread” + Thread.currentThread().getName()); } }
打开 terminal,输入命令编译并运行:
javac test4.java java test4
运行结果如下所示
体会 start 与 run 函数之间的区别 - 图4
此时便证明了,run() 函数只是一个普通的函数,而start() 函数才是真正意义上的创建了多线程。