多线程的创建与使用是java工作学习中常见的内容,一直对多线程怀有神秘感,今天就把自己对多线程的理解分享给大家,供学习交流。
进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
线程:是程序中一个单一的顺序控制流程,是cpu调度的最小单位。
多线程:在单个程序中同时运行多个线程完成不同的工作,称为多线程。
线程的生命周期有五个阶段:新建(new Thread)、就绪(runnable)、运行(running)、堵塞(blocked)、死亡(dead)。
新建(new Thread):当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。
就绪(runnable):线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。
运行(running):线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
堵塞(blocked):由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。
死亡(dead):当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
在java中创建多线程一般有两种方式是,一种是继承Thread类,并重写run()方法;另一种是实现Runnable接口,并重写run()方法。
第一种:继承Thread类
package com.xyfer;
public class ThreadDemo extends Thread(){
@Override
public void run(){
for(int i=0;i<5;i++){
System.out.println("继承Thread类第"+i+"次打印");
}
}
}
测试类进行多线程测试,
package com.xyfer;
public class ThreadTest {
public static void main (String[] args) {
ThreadDemo test1 = new ThreadDemo();
ThreadDemo test2 = new ThreadDemo();
test1.start();
test1.start();
}
}
控制台打印:
继承Thread类第0次打印
继承Thread类第0次打印
继承Thread类第1次打印
继承Thread类第1次打印
继承Thread类第2次打印
继承Thread类第2次打印
继承Thread类第3次打印
继承Thread类第3次打印
继承Thread类第4次打印
继承Thread类第4次打印
再执行一次:
继承Thread类第0次打印
继承Thread类第0次打印
继承Thread类第1次打印
继承Thread类第1次打印
继承Thread类第2次打印
继承Thread类第3次打印
继承Thread类第2次打印
继承Thread类第4次打印
继承Thread类第3次打印
继承Thread类第4次打印
程序启动运行main时,java虚拟机启动一个进程,主线程在main()方法被调用的时候创建,main()方法运行在主线程中,随着main()方法中的两个对象的start()方法调用,另外两个线程也被启动,这样,整个程序就在多线程下运行了。
多次运行,从控制台打印结果可以看出,多线程并不是按顺序执行代码的,而是随机执行的,操作系统随机在多个线程之间切换。start()方法调用后并不是立即执行相应线程代码,而是把线程代码变成可执行状态,等待操作系统随机执行。
第二中:实现Runnable接口
package com.xyfer;
public class ThreadRunnable implements Runnable(){
@Override
public void run(){
for(int i=0;i<5;i++){
System.out.println("实现Runnable接口第"+i+"次打印");
}
}
}
测试类进行多线程测试:(通过Thread的构造方法传入Runnable接口的实现类创建Thread类实例调用start()方法启动线程)
package com.xyfer;
public class ThreadTest {
public static void main (String[] args) {
new Thread(new ThreadRunnable()).start();
new Thread(new ThreadRunnable()).start();
}
}
控制台输出结果:
实现Runnable接口第0次打印
实现Runnable接口第0次打印
实现Runnable接口第1次打印
实现Runnable接口第1次打印
实现Runnable接口第2次打印
实现Runnable接口第2次打印
实现Runnable接口第3次打印
实现Runnable接口第3次打印、
此时多线程仍然是随机执行线程,多执行几次程序就会发现控制台打印结果顺序不一样。
多线程中的join()方法
下面介绍一下多线程中的join()方法,很多情况下,主线程启动了子线程,但是通常子线程要做大量的耗时工作,因此会出现主线程先于子线程执行结束。但是有时候主线程是需要子线程的执行结果的,这时候就需要使用多线程的join()方法来控制主线程等待子线程执行完毕后再结束主线程。
以继承Thread类为例:
package com.xyfer;
public class ThreadDemo extends Thread(){
@Override
public void run(){
for(int i=0;i<5;i++){
System.out.println("继承Thread类第"+i+"次打印");
}
}
}
测试类:
package com.xyfer;
public class ThreadTest {
public static void main (String[] args) {
System.out.println("主线程开启。");
ThreadDemo test1 = new ThreadDemo();
ThreadDemo test2 = new ThreadDemo();
test1.start();
test1.start();
System.out.println("主线程结束。");
}
}
控制台打印结果:
主线程开启。
主线程结束。
继承Thread类第0次打印
继承Thread类第1次打印
继承Thread类第2次打印
继承Thread类第3次打印
继承Thread类第4次打印
继承Thread类第0次打印
继承Thread类第1次打印
继承Thread类第2次打印
继承Thread类第3次打印
继承Thread类第4次打印
从控制台打印结果来看,主线程先于子线程执行结束。使用join()方法后就能保证主线程在子线程之后执行结束。
package com.xyfer;
public class ThreadTest {
public static void main (String[] args) {
System.out.println("主线程开启。");
ThreadDemo test1 = new ThreadDemo();
ThreadDemo test2 = new ThreadDemo();
test1.start();
test1.start();
try {
test1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
test2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程结束。");
}
}
控制台打印结果:
主线程开启。
继承Thread类第0次打印
继承Thread类第1次打印
继承Thread类第2次打印
继承Thread类第3次打印
继承Thread类第4次打印
继承Thread类第0次打印
继承Thread类第1次打印
继承Thread类第2次打印
继承Thread类第3次打印
继承Thread类第4次打印
主线程结束。
从控制台打印结果可以看出,join()方法能保证主线程在子线程执行结束之后再执行结束。