在了解多线程之前,先来了解一下进程与线程之间的关系。
进程和线程:
进程是指在系统中正在执行的一个程序,每个进程之间是独立的。
线程是进程的一个基本执行单元。一个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程)
主线程:
在Java程序中存在一个主线程(JVM线程),main方法自带的一个线程。
之所以在系统进行多个程序的时候(QQ,web网页等),看似是在同步执行,那是因为cpu在进程中进行多线程的切换,cpu切换的速度之快让我们 觉得是在同步执行,其实在进程的执行过程中是存在一定的先后顺序的。
线程的执行原理:
线程运行的结果每次都不同,因为多个线程都在获取cpu的执行权,cpu执行到了谁,谁就执行。
明确一点:在某一时刻,只能有一个程序在运行(多核cpu除外)——也可以说多线程的运行行为是在互相抢夺cpu的执行权,同时cpu在做着快速的切换动作,已达到看似同时运行的结果。因此多线程的特性是随机性。
实现多线程的方式有两种:继承Thread类、实现Runnable接口
继承Thread类
1、定义类继承Thread类
2、复写Thread中的run()方法
目的:将自定义代码写在run()方法中,让线程运行
3、调用start()方法,启动线程,调用run()方法
public class ThreadTest extends Thread{
@Override
public void run(){ //复写Thread中的run方法
for(int i=0;i<10;i++){
System.out.print(" demo run");
}
}
}
class ThreadDemo{
public static void main(String[] args) {
ThreadTest dt = new ThreadTest();
dt.start(); //调用start方法(启动线程,调用run方法)
for(int i=0;i<10;i++){
System.out.print("main run");
}
}
}
代码解说:在这一个程序中,存在两个线程:main主线程和ThreadTest自定义线程。
这两个线程同是在一个进程(程序)中执行,为确保程序的正常执行,因此在进程的内存空间中,开辟了两个线程空间,这两个线程空间在要执行的时候,都要获取cpu的执行权,所以,会互相的去抢夺cpu的执行权。会先抢到谁就执行。因此,这段程序的运行结果是随机的。
意思就是说:运行结果一会是main run,一会是demo run。存在无序性。
至于为什么要复写run方法,原因是:
Thread用于描述线程,该线程就只定义了一个功能,用于存储线程要运行的代码,该存储功能就是run()方法。也就是说Thread中的run()方法,用于存储线程要允许的代码。
(主线程的代码写在main方法中,而main方法是JVM定义的。所以JVM调用main方法的原因是因为主线程要使用,如果将自定义的代码写在main方法中,让JVM去执行,那就是一种单线程(主线程),不是多线程了。)
public class ThreadTest extends Thread{
@Override
public void run(){
for(int i=0;i<10;i++){
System.out.print(" demo run");
}
}
}
class ThreadDemo{
public static void main(String[] args) {
ThreadTest dt = new ThreadTest();
dt.start(); //①
dt.run(); //②
for(int i=0;i<10;i++){
System.out.print("main run");
}
}
}
代码解说:在上段程序中,如果①被注释掉,直接dt.run(),那这就不是一个多线程的执行,只是相当于一个简单的对象调用方法,而线程创建了,并没有运行。
所以,如果②被注释掉,直接dt.start(),那就是创建了一个线程对象dt,然后dt.start()将线程dt开启,并调用了run()方法,让线程去运行。
实现Runnable接口
1、定义类实现Runnable接口
2、复写Runnable接口中的run()方法
3、通过Runnable建立线程对象
4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数