概念:
1.进程:
定义:在一个操作系统中,每个独立运行的程序都可以称为一个进程,也就是“正在运行的程序”。
在多任务操作系统中,表面上看是支持进程并发执行的,但实际上这些进程并不是同时运行的。
2.线程:
定义:每个运行的程序都是一个进程,在一个进程中还可以有多个单元同时运行,这些执行单元可以看做程序执行的一条条线索,这些线索被称为线程。
操作系统中的每一个进程中至少存在一个线程。当一个Java程序启动时,就会产生一个进程,该进程会默认创建一个线程,在这个线程上会运行main()方法中的代码。
线程的创建:
java中实现多线程有两种方式,一种是继承java.long包下的Thread类,覆写Thread类的run()方法,在run()方法中实现运行在线程上的代码;另一种是实现java.long.Runnable接口,同样是在
run()方法中实现运行在线程上的代码。
1.继承Thread类创建多线程
在学习多线程之前,先来看看我们所熟悉的单线程程序:
Example01:
public class Example01 {
public static void main(String[] args) {
MyThread myThread=new MyThread(); // 创建MyThread实例对象
myThread.run(); // 调用MyThread类的run()方法
while (true) { // 该循环是一个死循环,打印输出语句
System.out.println("Main方法在运行");
}
}
}
class MyThread {
public void run() {
while (true) { // 该循环是一个死循环,打印输出语句
System.out.println("MyThread类的run()方法在运行");
}
}
}
运行结果如下所示:
概念:
1.进程:
定义:在一个操作系统中,每个独立运行的程序都可以称为一个进程,也就是“正在运行的程序”。
在多任务操作系统中,表面上看是支持进程并发执行的,但实际上这些进程并不是同时运行的。
2.线程:
定义:每个运行的程序都是一个进程,在一个进程中还可以有多个单元同时运行,这些执行单元可以看做程序执行的一条条线索,这些线索被称为线程。
操作系统中的每一个进程中至少存在一个线程。当一个Java程序启动时,就会产生一个进程,该进程会默认创建一个线程,在这个线程上会运行main()方法中的代码。
线程的创建:
java中实现多线程有两种方式,一种是继承java.long包下的Thread类,覆写Thread类的run()方法,在run()方法中实现运行在线程上的代码;另一种是实现java.long.Runnable接口,同样是在
run()方法中实现运行在线程上的代码。
1.继承Thread类创建多线程
在学习多线程之前,先来看看我们所熟悉的单线程程序:
Example01:
public class Example01 {
public static void main(String[] args) {
MyThread myThread=new MyThread(); // 创建MyThread实例对象
myThread.run(); // 调用MyThread类的run()方法
while (true) { // 该循环是一个死循环,打印输出语句
System.out.println("Main方法在运行");
}
}
}
class MyThread {
public void run() {
while (true) { // 该循环是一个死循环,打印输出语句
System.out.println("MyThread类的run()方法在运行");
}
}
}
运行结果如下所示:
从上图可以看出,程序一直打印的是“MyThread类的run()方法在运行”,这是因为该程序是一个单线程程序,当调用MyThread类的run()方法时,遇到死循环,循环会一直进行。因此,MyThread类的打印语句将永远执行,而main()方法中的打印语句无法得到执行。
如果希望上述代码中两个while循环中的打印语句能够并发执行,就需要实现多线程。
Example02:
public class Example02 {
public static void main(String[] args) {
MyThread myThread = new MyThread(); // 创建线程MyThread的线程对象
myThread.start(); // 开启线程
while (true) { // 通过死循环语句打印输出
System.out.println("main()方法在运行");
}
}
}
class MyThread extends Thread {
public void run() {
while (true) { // 通过死循环语句打印输出
System.out.println("MyThread类的run()方法在运行");
}
}
}
运行结果如下:
从上述结果看,两个while循环中的打印语句轮流执行了,说明该例子实现了多线程。
分析:单线程的程序在运行时,会按照代码的调用顺序进行执行。而在多线程中,main()方法和MyThread类的run()方法却可以同时运行互不影响,这正是单线程与多线程的区别。
实现Runnable接口创建多线程
通过继承Thread类有一定局限性。因为Java中只支持单继承,一个类一旦继承了某个父类就无法再继承Thread类。为了克服这种弊端,Thread类提供了另外一种构造方法Thread(Runnable target),其中Runnable是一个接口,他只有一个run()方法。当通过
Thread(Runnable target)构造方法创建线程对象时,只需要为该方法传递一个实现了Runnable接口的实例对象,这样创建线程将调用实现了Runnable接口中的run()方法作为运行代码,而不需要调用Thread类的run()方法,接下来通过一个案例来演示如何通过实现Runnable接口的方式来创建多线程,如下所示:
Example03:
public class Example03 {
public static void main(String[] args) {
MyThread myThread = new MyThread(); // 创建MyThread的实例对象
Thread thread=new Thread(myThread); // 创建线程对象
thread.start(); // 开启线程,执行线程中的run()方法
while (true) {
System.out.println("main()方法在运行");
}
}
}
class MyThread implements Runnable {
public void run() { // 线程的代码段,当调用start()方法时,线程从此处开始执行
while (true) {
System.out.println("MyThread类的run()方法在运行");
}
}
}
运行结果如下所示:
MyThread类实现了Runnable接口,并重写了Runnable接口中的run()方法,通过Thread类的构造方法将MyThread类的实力对象作为参数传入。从运行结果看出该例实现了多线程。
后台线程
对java程序来说,只要还有一个前台进程在运行,这个进程就不会结束,如果一个进程中只有后台线程云心,这个进程就会结束。这里提到的前台线程和后台线程是一种相对的概念,新创建的线程默认都是前台线程,如果某个线程对象在启动之前调用了setDeamon(true)
语句,这个线程就会变成一个后台线程。
Example04
class DamonThread implements Runnable { // 创建DamonThread类,实现Runnable接口
public void run() { // 实现接口中的run()方法
while (true) {
System.out.println(Thread.currentThread().getName()
+ "---is running.");
}
}
}
public class Example04 {
public static void main(String[] args) {
System.out.println("main线程是后台线程吗?"+ Thread.currentThread().isDaemon());//通过Thread的
currentThread()方法得到当前线程对象DamonThread dt = new DamonThread(); // 创建一个DamonThread对象dt
Thread t = new Thread(dt,"后台线程"); // 创建线程t共享dt资源
System.out.println("t线程默认是后台线程吗? "+t.isDaemon()); // 判断是否为后台线程
t.setDaemon(true); // 将线程t设置为后台线程
t.start(); // 调用start()方法开启线程t
for(int i=0;i<10;i++){
System.out.println(i);
}
}
}
运行结果如下所示:
该示例演示了一个后台线程结束的过程。当开启线程t时,会执行死循环中的打印语句,但我们将线程t设置为后台线程后,当前台线程死亡后,JVM会通知后台线程。由于后台线程从接受指令到作出相应需要一段时间,因此打印了几次“后台线程---is running.”语句后,后台线程也结束了。由此说明进程中只有后台线程运行时,程序就会结束。
注意:将某个线程设置为后台线程,必须在该线程启动之前,也就是说setDeamon()方法必须在start()方法之前调用,否则就会引发IllegalThreadStateException异常。
从上图可以看出,程序一直打印的是“MyThread类的run()方法在运行”,这是因为该程序是一个单线程程序,当调用MyThread类的run()方法时,遇到死循环,循环会一直进行。因此,MyThread类的打印语句将永远执行,而main()方法中的打印语句无法得到执行。
如果希望上述代码中两个while循环中的打印语句能够并发执行,就需要实现多线程。
Example02:
public class Example02 {
public static void main(String[] args) {
MyThread myThread = new MyThread(); // 创建线程MyThread的线程对象
myThread.start(); // 开启线程
while (true) { // 通过死循环语句打印输出
System.out.println("main()方法在运行");
}
}
}
class MyThread extends Thread {
public void run() {
while (true) { // 通过死循环语句打印输出
System.out.println("MyThread类的run()方法在运行");
}
}
}
运行结果如下:
从上述结果看,两个while循环中的打印语句轮流执行了,说明该例子实现了多线程。
分析:单线程的程序在运行时,会按照代码的调用顺序进行执行。而在多线程中,main()方法和MyThread类的run()方法却可以同时运行互不影响,这正是单线程与多线程的区别。
实现Runnable接口创建多线程
通过继承Thread类有一定局限性。因为Java中只支持单继承,一个类一旦继承了某个父类就无法再继承Thread类。为了克服这种弊端,Thread类提供了另外一种构造方法Thread(Runnable target),其中Runnable是一个接口,他只有一个run()方法。当通过
Thread(Runnable target)构造方法创建线程对象时,只需要为该方法传递一个实现了Runnable接口的实例对象,这样创建线程将调用实现了Runnable接口中的run()方法作为运行代码,而不需要调用Thread类的run()方法,接下来通过一个案例来演示如何通过实现Runnable接口的方式来创建多线程,如下所示:
Example03:
public class Example03 {
public static void main(String[] args) {
MyThread myThread = new MyThread(); // 创建MyThread的实例对象
Thread thread=new Thread(myThread); // 创建线程对象
thread.start(); // 开启线程,执行线程中的run()方法
while (true) {
System.out.println("main()方法在运行");
}
}
}
class MyThread implements Runnable {
public void run() { // 线程的代码段,当调用start()方法时,线程从此处开始执行
while (true) {
System.out.println("MyThread类的run()方法在运行");
}
}
}
运行结果如下所示:
MyThread类实现了Runnable接口,并重写了Runnable接口中的run()方法,通过Thread类的构造方法将MyThread类的实力对象作为参数传入。从运行结果看出该例实现了多线程。
后台线程
对java程序来说,只要还有一个前台进程在运行,这个进程就不会结束,如果一个进程中只有后台线程云心,这个进程就会结束。这里提到的前台线程和后台线程是一种相对的概念,新创建的线程默认都是前台线程,如果某个线程对象在启动之前调用了setDeamon(true)
语句,这个线程就会变成一个后台线程。
Example04
class DamonThread implements Runnable { // 创建DamonThread类,实现Runnable接口
public void run() { // 实现接口中的run()方法
while (true) {
System.out.println(Thread.currentThread().getName()
+ "---is running.");
}
}
}
public class Example04 {
public static void main(String[] args) {
System.out.println("main线程是后台线程吗?"+ Thread.currentThread().isDaemon());//通过Thread的
currentThread()方法得到当前线程对象DamonThread dt = new DamonThread(); // 创建一个DamonThread对象dt
Thread t = new Thread(dt,"后台线程"); // 创建线程t共享dt资源
System.out.println("t线程默认是后台线程吗? "+t.isDaemon()); // 判断是否为后台线程
t.setDaemon(true); // 将线程t设置为后台线程
t.start(); // 调用start()方法开启线程t
for(int i=0;i<10;i++){
System.out.println(i);
}
}
}
运行结果如下所示:
该示例演示了一个后台线程结束的过程。当开启线程t时,会执行死循环中的打印语句,但我们将线程t设置为后台线程后,当前台线程死亡后,JVM会通知后台线程。由于后台线程从接受指令到作出相应需要一段时间,因此打印了几次“后台线程---is running.”语句后,后台线程也结束了。由此说明进程中只有后台线程运行时,程序就会结束。
注意:将某个线程设置为后台线程,必须在该线程启动之前,也就是说setDeamon()方法必须在start()方法之前调用,否则就会引发IllegalThreadStateException异常。