线程与进程学习001

一个进程在运行时至少会有一个线程在运行,在这种情况下Java中也是存在的,这些线程在后台默默低执行,例如,调用public static void main()方法的线程就是这样的,而且它是由JVM创建。

package test;

public class test {
    
    
	public static void main(String[] args) {
    
    
		System.out.println(Thread.currentThread().getName());
	}

}

在控制台输出的main其实就是一个名称为main的线程在执行main()方法中的代码。另外,需要说明一下,在控制台中输出的main和main方法没有任何关系,她们仅仅是名字相同而已。
在这里插入图片描述
1.1继承Thread类
Java的JDK开发包已经自带了多线程技术的支持,通过它可以方便地进行多线程编程。实现多线程编程主要有两种方式:一种是继承Thread类,另外一种是实现Runnable接口。
在学习如何创建新的线程前,先来看看Thread类的声明结构:

public class Thread implements Runnable

从上面的源代码中可以发现,Thread类实现了Runnable接口,它们之间具有多态性,多态结构的实例代码如下:

Runnable run1 = new Thread();
Runnable run2 = new MyThread();
Thread t1 = new MyThread();

其实使用继承Thread类的方式创建新线程是,最大的局限是不支持多继承,因为java语言的特点是单继承,所以为了支持多继承,完全可以实现Runnable接口,即一边实现一边继承,但是这两种方式创建线程的功能是一样的,没有本质的区别。

本节主要介绍第一种方式。如下代码:

package com.mythread.www;

public class MyThread extends Thread {
    
    

	@Override
	public void run() {
    
    
		// TODO Auto-generated method stub
		super.run();
		System.out.println("MyThread");
	}

}

运行类代码如下:

package test;

import com.mythread.www.MyThread;;

public class Run {
    
    
	public static void main(String[] args) {
    
    
		final MyThread myThread = new MyThread();
		myThread.start();
		System.out.println("运行结束!");// 耗时小
	}

}

上面代码使用start()方法来启动一个线程,线程启动后会自动调用线程对象中的run()方法,run()方法里面的代码就是线程对象要执行的任务,是线程执行任务的入口。
程序运行结果如下图所示
在这里插入图片描述
从上图的程序运行结果来看,MyThread.java类中的run()方法的执行时间相对于输出“运行结束!”的执行时间晚一些,因为start()方法的执行比较耗时间,这也增加了先输出“运行结束!”字符串的概率。start()方法耗时的原因是执行了多个步骤,步骤如下:
(1)通过JVM告诉操作系统创建Thread。
(2)操作系统开辟内存并使用Windows SDK中的create Thread()函数创建Thread线程对象。
(3)操作系统对Thread对象进行调度,以确定执行时机。
(4)Thread在操作系统中被成功执行。
以上4步完整地执行后所消耗的时间一定大于输出“运行结束”字符串的时间,另外,main方法线程执行start()方法时候不必等待4步都执行完毕,而是立即继续执行start()方法后面的代码,这四步与输出“运行结束”的代码一同执行,但是由于“运行结束”耗时比较少,所以在大多数的情况下,先输出“运行结束”,后输出“My Thread”
在这里插入图片描述
输出上面的结果说明执行完整的start()方法的4步后,才执行输出“运行结束!”字符串的代码,这也说明线程执行的顺序具有随机性。然而由于输出这种结果的机会很小,使用手动的方式来重复执行“Run as—>java Application难以重现,这时候可以认为地制造这种输出结果,即在执行输出”运行结束!“代码之前先执行代码”Thread.sleep(200),让run()方法有充足的时间来先输出“MyThread”。后输出“运行结束!”实例代码如下:

package test;

import com.mythread.www.MyThread;;

public class Run2 {
    
    
	public static void main(String[] args) throws InterruptedException {
    
    
		final MyThread myThread = new MyThread();
		myThread.start();
		Thread.sleep(200);
		System.out.println("运行结束!");// 耗时小
	}
}

在使用线程技术时,代码的运行结果与代码的执行顺序或者调用顺序是无关的。另外,线程是一个子任务,CPU以不确定的方式,或者说是以随机的时间来调用线程中的run()方法,所以先输出“运行结束!”和先输出“MyThread”具有不确定性。
注意:
如果多次调用start()方法,则会出现Exception in Thread“main”java.lang.lllegal - ThreadStateException。

1.2.2使用常见命令分析线程的信息
可以在运行的进程中创建线程,如果想要查看这些线程的状态和信息,则可以采用3种常见命令,它们分别是jps+jstack.exe、jmc.exe、jvisualvm.exe,它们在jdk\bin文件夹中
创建测试使用的程序并运行,代码如下:

package test.run;

public class Run3 {
    
    
	public static void main(String[] args) {
    
    
		for (int i = 0; i < 5; i++) {
    
    
			new Thread() {
    
    
				public void run() {
    
    
					try {
    
    
						Thread.sleep(50000);
					} catch (InterruptedException e) {
    
    
						e.printStackTrace();
					}
				}
			}.start();
		}
	}
}

1.2.3线程随机性的展现
前面介绍过线程的调用时随机的,但是这一点并没有在代码中得以体现,都是理论的内容,所以本节将演示线程的随机性

package MyThread.java;

public class MyThread extends Thread{
    
    

	@Override
	public void run() {
    
    
		for (int i = 0; i < 10000; i++) {
    
    
			System.out.println("run="+Thread.currentThread().getName());
		}
	}
}

再创建运行类Test.java

package test;

import MyThread.java.*;;

public class Test {
    
    
	public static void main(String[] args) {
    
    
		final MyThread thread = new MyThread();
		thread.setName("mythread");
		thread.start();
		for (int i = 0; i < 10000; i++) {
    
    
			System.out.println("main=" + Thread.currentThread().getName());
		}
	}
}

Thread.java类中的start()方法通知“线程规划器”----次线程已经准备就绪,准备调用线程对象的run()方法。这个过程其实就是让系统安排一个时间来调用Thread中的run()方法,即让线程执行具体的任务,具有随机顺序执行的结果。
如果调用代码“Thread.run();而不是”thread.start();其实就不是异步执行了,那么此线程对象并不交给“线程规划器”来进行处理,而是由main主线程来调用run()方法,也就是等于run()方法中的代码完毕后在执行后面的代码。
以异步方式运行的效果如下图所示。
在这里插入图片描述
多线程随机输出的原因是CPU将时间片分给不同线程,线程获得时间片后就执行任务,所以这些线程在交替低执行并输出,导致输出结果呈现乱序的效果。时间片段即CPU分配给每个程序的时间。每个线程被分配一个时间片,在当前的时间片内CPU去执行线程中的任务。需要注意的是,CPU在不同的线程上进行切换是需要耗时的,所以并不是创建的线程越多,软件的运行效率就越高,相反,线程数过多反而会降低软件的执行效率。

猜你喜欢

转载自blog.csdn.net/m0_55284524/article/details/115033800