多线程是指在同一个进程中有多个顺序流同时执行。一个应用程序由一个或多个进程组成。
应用程序是Application,比如迅雷这款软件;进程是Process,迅雷至少包括了界面进程和下载进程;线程是Thread,迅雷的下载进程里面有多个线程一起工作,才能完成下载功能。
在并发环境中,默认条件下多线程里的方法的执行顺序是随机无序的,并不是按照比如线程对象的创建顺序、start()方法的先后执行顺序而先后顺序执行。比如Thread-0和Thread-1执行一个相同的方法,Thread-0这个线程虽然是先创建的,但其内部的方法不一定最先完成。
注意,这里的随机无序指的是线程里的方法。线程的创建是有序的,谁先执行start()方法谁就先被创建。即启动顺序不等于执行顺序。
因为执行一个线程时,线程并不会立即执行,而是等待CPU的资源调度,CPU能调度哪个线程,是与底层物理硬件相关的,外界无法控制。
程序员可以控制的是程序是否抢占CPU资源。
Java里面多线程要执行的代码要放在run()方法内部,run()方法的方法体也被称为线程体。
更为通俗的理解是将run()方法作为主线程外其它线程的入口方法,即run()方法之于其它线程的地位相当于主线程的main()方法。
简单的开启多线程方式有两种:继承Thread类、实现Runnable接口。
/**
* 实现Runnable接口
* @author Zhang
*
*/
public class MyRunThread implements Runnable{
//run()方法相当于主线程中的main方法,run()是该线程入口
@Override
public void run() {
System.out.println("实现接口线程名称>>"+Thread.currentThread().getName());
otherThreadMethod();
}
public static void otherThreadMethod() {
for(int i=1;i<=5;i++) {
System.out.println("实现Runnable>>"+Thread.currentThread().getName()+":数值>>"+i);
}
}
}
/**
* 继承Thread类
* @author Zhang
*
*/
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("继承Thread线程名称>>"+Thread.currentThread().getName());
otherMethod();
}
public void otherMethod() {
for(int i=1;i<=5;i++) {
System.out.println("继承Thread>>"+Thread.currentThread().getName()+":数值>>"+i);
}
}
}
对以上多线程类进行启动,启动的标准是看是否调用了start()方法,因为主线程是必须启动且先启动,其它线程的启动看start()方法的先后顺序,所以下面的线程启动顺序一定是主线程、继承Thread的线程、实现接口的两个线程,但是各个线程之间方法的执行是无序的。
public class Test {
//主方法本身所在的是主线程
public static void main(String[] args) {
System.out.println("主线程名称>>"+Thread.currentThread().getName());
//创建Runnable实现类对象
MyRunThread runThread=new MyRunThread();
//创建继承Thread类的多线程对象
Thread thread=new MyThread();
//Runnable实现类传入Thread
Thread runTthread1=new Thread(runThread);
Thread runThread2=new Thread(runThread);
//启动一个线程
thread.start();
//启动一个线程
runTthread1.start();
//启动一个线程
runThread2.start();
//执行主线程中方法
mainMethod();
}
public static void mainMethod() {
for(int i=1;i<5;i++) {
System.out.println("主线程>>"+Thread.currentThread().getName()+":数值>>"+i);
}
}
}
结果:
主线程名称>>main
主线程>>main:数值>>1
主线程>>main:数值>>2
实现接口线程名称>>Thread-2
实现Runnable>>Thread-2:数值>>1
实现Runnable>>Thread-2:数值>>2
实现Runnable>>Thread-2:数值>>3
实现Runnable>>Thread-2:数值>>4
实现Runnable>>Thread-2:数值>>5
主线程>>main:数值>>3
实现接口线程名称>>Thread-1
实现Runnable>>Thread-1:数值>>1
继承Thread线程名称>>Thread-0
实现Runnable>>Thread-1:数值>>2
主线程>>main:数值>>4
实现Runnable>>Thread-1:数值>>3
继承Thread>>Thread-0:数值>>1
实现Runnable>>Thread-1:数值>>4
继承Thread>>Thread-0:数值>>2
实现Runnable>>Thread-1:数值>>5
继承Thread>>Thread-0:数值>>3
继承Thread>>Thread-0:数值>>4
继承Thread>>Thread-0:数值>>5
具体而言,主线程内子线程start()后并不一定立即执行子线程内的run()方法。
public class MyRunThread implements Runnable{
//run()方法相当于主线程中的main方法,run()是该线程入口
@Override
public void run() {
//获取当前线程名称
System.out.println("线程类内,子线程名称:"+Thread.currentThread().getName());
otherThreadMethod();
}
public static void otherThreadMethod() {
for(int i=1;i<=5;i++) {
System.out.println(Thread.currentThread().getName()+":数值>>"+i);
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
//获取当前线程名称
System.out.println("主线程名称>>"+Thread.currentThread().getName());
//创建一个子线程
Runnable runThread=new MyRunThread();
Thread thread1=new Thread(runThread);
//启动1个子线程
thread1.start();
//获取子线程的默认名称
System.out.println("子线程名称>>"+thread1.getName());
//设置子线程名称
thread1.setName("子线程1");
//执行主线程中方法
mainMethod();
}
public static void mainMethod() {
for(int i=1;i<5;i++) {
System.out.println(Thread.currentThread().getName()+":数值>>"+i);
}
}
}
结果如下,可以发现run()方法执行的获取子线程名称是在主方法setName后的结果,所以证实了start()之后并不一定会立即执行run()方法,因为主线程的资源让出,子线程才有可能执行。
主线程名称>>main
子线程名称>>Thread-0
main:数值>>1
main:数值>>2
线程类内,子线程名称:子线程1
子线程1:数值>>1
子线程1:数值>>2
子线程1:数值>>3
子线程1:数值>>4
子线程1:数值>>5
main:数值>>3
main:数值>>4