在进行讲解之前,我们有必要搞清楚什么是线程,什么是进程,以及二者有什么区别和联系
(1)线程是CPU独立运行和独立调度的基本单位;
(2)进程是资源分配的基本单位;
两者的联系:进程和线程都是操作系统所运行的程序运行的基本单元。
区别:
(1)进程具有独立的空间地址,一个进程崩溃后,在保护模式下不会对其它进程产生影响。
(2)线程只是一个进程的不同执行路径,线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间
上面的解释可能不是很好理解,下面我打一个比喻:
【比喻】我们启动一辆小汽车,在启动过程中需要电池供电、油箱供油、发动机燃烧汽油等等一些部件合作才能完成。
那么启动汽车就相当于一个进程,电池供电、油箱供油、发动机燃烧汽油等等这些就属于一个个的线程。这些单独的线程在同一时间并行的独立运行共同合作才能正常使汽车启动。
多线程就是为了解决效率问题,上述几个线程同时运行,目的就是最快的时间内启动汽车。
接下来我们就来用java代码来实现线程吧。
实现线程的方式有四种:
1. 继承Thread类 重写run();
2. 实现Runnable接口 实现run();
3. 内部类实现 类似于实现run接口(类似于2);
4. 实现Callable接口 实现call()。
这四种实现方式代码如下:
1. 继承Thread类 重写run(),代码如下:
package com.java.thread;
/**
* @Author:Mr.Liu
* @Description:实现多线程的四种方式
* @Date:Created in 10:34 2017/12/4
* @Modified By:
*/
//继承Thread
public class BlogThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("子线程ThreadID:"+Thread.currentThread().getId()+" : 第"+i+"次循环,ThreadName:"+Thread.currentThread().getName());
}
System.out.println("子线程运行结束");
}
public static void main(String[] args) {
/**
*@Author:Administrator
*@Description:
* * @param args
*@Date:Created in 10:36 2017/12/4
*/
BlogThread t01 = new BlogThread();
t01.setName("sonThread");
t01.start();//启动子线程
for (int i = 0; i < 10; i++) {
System.out.println("主线程ThreadID:"+Thread.currentThread().getId()+" : 第"+i+"次循环,ThreadName:"+Thread.currentThread().getName());
if(i == 3){
try {
System.out.println("主线程停止");
Thread.currentThread().stop();
} catch (Exception e) {
System.out.println("主线程停止异常");
}
}
}
}
}
运行上述代码的一种结果如下:
主线程ThreadID:1 : 第0次循环,ThreadName:main
主线程ThreadID:1 : 第1次循环,ThreadName:main
子线程ThreadID:10 : 第0次循环,ThreadName:sonThread
主线程ThreadID:1 : 第2次循环,ThreadName:main
子线程ThreadID:10 : 第1次循环,ThreadName:sonThread
主线程ThreadID:1 : 第3次循环,ThreadName:main
子线程ThreadID:10 : 第2次循环,ThreadName:sonThread
主线程停止
子线程ThreadID:10 : 第3次循环,ThreadName:sonThread
子线程ThreadID:10 : 第4次循环,ThreadName:sonThread
子线程ThreadID:10 : 第5次循环,ThreadName:sonThread
子线程ThreadID:10 : 第6次循环,ThreadName:sonThread
子线程ThreadID:10 : 第7次循环,ThreadName:sonThread
子线程ThreadID:10 : 第8次循环,ThreadName:sonThread
子线程ThreadID:10 : 第9次循环,ThreadName:sonThread
子线程运行结束
2. 实现Runnable接口 实现run(),代码如下:
package com.java.thread;
/**
* @Author:Mr.Liu
* @Description:
* @Date:Created in 10:54 2017/12/4
* @Modified By:
*/
//实现Runnable
public class BlogRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程ThreadID"+Thread.currentThread().getId()+" : 第"+i+"次循环,ThreadName:"+Thread.currentThread().getName());
}
System.out.println("子线程运行结束");
}
public static void main(String[] args) {
/**
*@Author:Administrator
*@Description:
* * @param args
*@Date:Created in 10:36 2017/12/4
*/
Thread t02 = new Thread(new BlogRunnable());
t02.setName("sonThread");
t02.start();//启动子线程
for (int i = 0; i < 10; i++) {
System.out.println("主线程ThreadID:"+Thread.currentThread().getId()+" ;第"+i+"次循环,ThreadName:"+Thread.currentThread().getName());
if(i == 3){
try {
System.out.println("主线程停止");
Thread.currentThread().stop();
} catch (Exception e) {
System.out.println("主线程停止异常");
}
}
}
}
}
运行后的一种结果如下:
主线程ThreadID:1 ;第0次循环,ThreadName:main
主线程ThreadID:1 ;第1次循环,ThreadName:main
主线程ThreadID:1 ;第2次循环,ThreadName:main
主线程ThreadID:1 ;第3次循环,ThreadName:main
主线程停止
子线程ThreadID10 : 第0次循环,ThreadName:sonThread
子线程ThreadID10 : 第1次循环,ThreadName:sonThread
子线程ThreadID10 : 第2次循环,ThreadName:sonThread
子线程ThreadID10 : 第3次循环,ThreadName:sonThread
子线程ThreadID10 : 第4次循环,ThreadName:sonThread
子线程ThreadID10 : 第5次循环,ThreadName:sonThread
子线程ThreadID10 : 第6次循环,ThreadName:sonThread
子线程ThreadID10 : 第7次循环,ThreadName:sonThread
子线程ThreadID10 : 第8次循环,ThreadName:sonThread
子线程ThreadID10 : 第9次循环,ThreadName:sonThread
子线程运行结束
3. 内部类实现 类似于实现run接口,代码如下:
package com.java.thread;
/**
* @Author:Mr.Liu
* @Description:
* @Date:Created in 10:57 2017/12/4
* @Modified By:
*/
public class BlogDemo03 {
public static void main(String[] args) {
System.out.println("本机CPU个数:"+Runtime.getRuntime().availableProcessors());
Thread t03 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("子线程ThreadID"+Thread.currentThread().getId()+" : 第"+i+"次循环,ThreadName:"+Thread.currentThread().getName());
}
System.out.println("子线程执行完毕");
}
});
t03.setName("匿名内部类");
t03.start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程ThreadID:"+Thread.currentThread().getId()+" ;第"+i+"次循环,ThreadName:"+Thread.currentThread().getName());
}
System.out.println("主线程已执行完毕");
}
}
运行后的一种结果如下:
本机CPU个数:12
主线程ThreadID:1 ;第0次循环,ThreadName:main
主线程ThreadID:1 ;第1次循环,ThreadName:main
子线程ThreadID10 : 第0次循环,ThreadName:匿名内部类
子线程ThreadID10 : 第1次循环,ThreadName:匿名内部类
子线程ThreadID10 : 第2次循环,ThreadName:匿名内部类
子线程ThreadID10 : 第3次循环,ThreadName:匿名内部类
子线程ThreadID10 : 第4次循环,ThreadName:匿名内部类
子线程ThreadID10 : 第5次循环,ThreadName:匿名内部类
子线程ThreadID10 : 第6次循环,ThreadName:匿名内部类
子线程ThreadID10 : 第7次循环,ThreadName:匿名内部类
子线程ThreadID10 : 第8次循环,ThreadName:匿名内部类
子线程ThreadID10 : 第9次循环,ThreadName:匿名内部类
子线程执行完毕
主线程ThreadID:1 ;第2次循环,ThreadName:main
主线程ThreadID:1 ;第3次循环,ThreadName:main
主线程ThreadID:1 ;第4次循环,ThreadName:main
主线程ThreadID:1 ;第5次循环,ThreadName:main
主线程ThreadID:1 ;第6次循环,ThreadName:main
主线程ThreadID:1 ;第7次循环,ThreadName:main
主线程ThreadID:1 ;第8次循环,ThreadName:main
主线程ThreadID:1 ;第9次循环,ThreadName:main
主线程已执行完毕
4. 实现Callable接口 实现call(),与Future联用获取线程执行后的返回值,代码如下:
package com.java.blog;
import java.util.concurrent.*;
/**
* @Author:Mr.Liu
* @Description:
* @Date:Created in 18:25 2017/12/4
* @Modified By:
*/
public class MyCallable implements Callable {
private String name;
public MyCallable(String name){
this.name = name;
}
@Override
public String call() throws Exception {
Thread.sleep(1000);
for (int i = 0; i < 5; i++) {
System.out.println(this.name+"子线程正在执行第"+(i+1)+"次");
}
return "MyCallable's name is "+ Thread.currentThread().getName();
}
public static void main(String[] args) throws Exception {
FutureTask<String> future01 = new FutureTask<String>(new MyCallable("乌龟"));
FutureTask<String> future02 = new FutureTask<String>(new MyCallable("兔子"));
new Thread(future01,"Thread01").start();
new Thread(future02,"Thread02").start();
System.out.println("future01:"+future01.get());
System.out.println("future02:"+future02.get());
System.out.println("主线程结束");
}
}
运行上述代码的一种结果如下:
兔子子线程正在执行第1次
兔子子线程正在执行第2次
兔子子线程正在执行第3次
乌龟子线程正在执行第1次
乌龟子线程正在执行第2次
乌龟子线程正在执行第3次
乌龟子线程正在执行第4次
乌龟子线程正在执行第5次
兔子子线程正在执行第4次
兔子子线程正在执行第5次
future01:MyCallable's name is Thread01
future02:MyCallable's name is Thread02
主线程结束
总结一下:
这四种创建线程的方式哪一种更好呢?当然是用Runnable与Callable接口的方式创建多线程。
用Runnable与Callable接口的方式创建多线程的特点:
1. 线程类只是实现了Runnable接口或Callable接口,还可以继承其它类。
2. 在这种方式下,多个线程可以共享一个target对象,所以非常适合多个线程来处理同一份资源情况。
如果需要访问当前线程,需要使用Thread.currentThread方法。Callable接口与Runnable接口相比,只是Callable接口可以返回值而已。
用Thread类的方式创建多线程的特点:
因为线程已经继承Thread类,所以不可以再继承其它类。
如果需要访问当前线程,直接使用this即可。