创建线程
当我们理解了进程与线程之间的联系和区别之后,我们就可以用代码进行线程的创建了。因为java的JVM虚拟机已经把操作系统的原生API(C语言)给封装好了,所以我们只需要学习Java提供的对操作系统进行调用的API就好了。通过调用JVM封装的操作系统的API我们就可以进行多线程编程了。
我们学习多线程编程首先就是要学会如何创建一个线程。
jconsole工具的使用说明
首先为大家介绍以下jconsole工具,这个工具是jdk自带的,用来查看咱们运行的程序中有多少个线程。
大家可以根据以下目录以及自己电脑的实际安装情况找到这个工具。
C:\Program Files\Android\jdk\jdk-8.0.302.8-hotspot\jdk8u302-b08\bin
如果实在不知道自己的JDK安装在哪里了,我们也可以通过Win+R打开运行,输入jconsole直接打开jconsole工具
前提是要配好环境变量。
运行jconsole后会出现这个界面:
我们先编写一个简单的程序。
图中这段进程是jconsole自己的线程。
这段进程则是idea的进程,jconsole是查看java程序进程的,所以证明了jconsole和idea都是用java语言编写的。
而第三个Main进程则是我们写的这个简短的程序的进程,我们选中Main,点击链接,再选择线程。
我们可以看到,左下角线程框里面列出了Main进程里面的所有线程,其中main线程是我们的主线程,也就是我们程序执行时创建的线程,因为我们这段程序中没有创建新的线程,所以我们的线程框中只有main线程是我们自己创建的,其他的线程都是JVM自带的线程,我们现阶段不需要了解。
通过学习jconsole工具,我们可以通过使用这个工具进行程序中线程的查看,现在我们就可以创建新的线程了。
创建一个新线程。
创建一个新的线程一共有五种方法。
1、继承Thread类,重写run()方法
class MyThread extends Thread{
@Override
public void run() {
System.out.println("hello thread");;
}
}
public class Thread1 {
public static void main(String[] args) {
Thread thread = new MyThread();
thread.start();
System.out.println("hello main");
}
}
2、实现Runable接口
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("hello Thread");
}
}
public class Thread2 {
public static void main(String[] args) {
Thread thread = new Thread(new MyRunnable());
thread.start();
System.out.println("hello main");
}
}
3、使用匿名内部类继承Thread
public class Thread3 {
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("hello thread");
}
};
thread.start();
System.out.println("hello main");
}
}
4、使用匿名内部类实现Runable
public class Thread4 {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("hello Thread");
}
});
thread.start();
System.out.println("hello main");
}
}
5、使用Lambda表达式
public class Thread5 {
public static void main(String[] args) {
Thread thread = new Thread(()->{
System.out.println("hello Thread");
});
thread.start();
System.out.println("hello main");
}
}
代码解释
现在我们以继承Thread类,重写run方法为例解释一下多线程代码。
class MyThread extends Thread{
@Override
public void run() {
System.out.println("hello thread");;
}
}
public class Thread1 {
public static void main(String[] args) {
Thread thread = new MyThread();
thread.start();
System.out.println("hello main");
}
}
首先,Java操作多线程的时候最核心的类就是Thread类,Thread使用的时候有几个特性需要我们大家明白。
(1)、使用Thread类不需要import任何的包,因为它包含在java.lang下面的,与之相同的还有Integer,String,StringBuilder,StringBuffer之类的类,也都不需要import任何的包。
(2)、我们创建了一个MyThread的类继承Thread,重写其中的run方法,run方法中的代码就是我们即将开启的新线程中要执行的代码。
(3)、我们创建一个线程对象(thread)的时候并不是就意味着开始执行了另一个线程,当我们开始调用start方法的时候才是真正的启动了一个新的线程。调用start方法就是调用操作系统的API通过操作系统内核开始创建了一个新的PCB,并且要把执行的指令也交给PCB,当PCB被调用到CUP上执行的时候,也就执行到了线程中的run方法了。
(4)、run方法不是由主线程调用的,而是主线程调用start方法,由start方法启动一个新的线程,这个新的线程会去执行run中的方法。
(5)、run方法执行完毕之后就会即刻把我们先创建的线程销毁。
我们需要明确一点的是,thread.start方法是通过主线程启动了一个新的线程,所以我们在thead.start 之后其实是有两个线程进行运行的,而且这两个线程是同时执行,不分先后的。
线程的抢占式执行
抢占式执行就是两个程序并发执行的时候,这两个线程就会抢占CPU资源,这样的抢占方式可以看作是随机的,我们可以用这样的程序来验证两个线程抢占式执行的方式。
import static java.lang.Thread.sleep;
class MyThread extends Thread{
@Override
public void run() {
while(true){
try {
sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("hello thread");
}
}
}
public class Thread1 {
public static void main(String[] args) throws InterruptedException {
Thread thread = new MyThread();
thread.start();
while(true){
sleep(100);
System.out.println("hello main");
}
}
}
我们无限的循环两个线程的打印程序,如果是抢占式执行那么两端程序一定是某一段时间执行“hello main”或者某一段时间执行“hello Thread”。
结果可以看到,因为抢占式执行,两段程序不断地抢占CPU资源,造成了交替执行两端程序的结果。
现在我们看一下两端不断执行的线程在jconsole上的运行情况。
运行程序,链接好进程,再查看进程中的线程。
其中main是主线程,而Thread-0就是我们通过thread.start创建的线程。