java多线程实现的三种⽅式区别
前⾔:
java多线程其实在⼯作中接触的并不是很多,偶尔⽤⼀下,但是这个特性⼜是开发⼯程师⾛向⼤⽜必须要掌握的知识点,所以花⼏天时间整理了⼀下,⼀⽅便梳理知识点,另⼀⽅⾯也是为了以后更好地使⽤。
⼀. 线程和进程
线程可以理解是⼀个程序中可以独⽴执⾏的模块,⼀个程序在⼀个时间段内同时做好⼏件事(起码表⾯看起来是的)就是多线程最明显的表征;
进程是⼀次计算机的执⾏活动,可以是整个程序也可以是部分程序的动态执⾏;
从概念上看,进程是包含线程的,⼀个进程⾄少包含⼀个线程。
区别:
1. 系统资源管理区别
进程是在操作系统上运⾏的,有独⽴的地址空间;线程是运⾏在进程内部的,⼀个进程可以有多个线程;在⼀个进程内多线程可以交替切换,提⾼系统的并发度。
2. 通信⾏为区别
进程通过操作系统转发指令,是拥有独⽴资源的单位;线程不拥有系统资源,只能访问进程的资源;同⼀个进程的多个线程可以共享这个进程的所有资源;线程拥有⾃⼰的栈空间,拥有独⽴的执⾏序列。
3. 系统开销区别
创建进程时系统需要分配内存区域;在切换进程时需要保存当前进程的CPU环境并且要为被调度运⾏的进程设置CPU环境;线程创建不需要分配内存,使⽤的是所属的进程资源;切换线程时也只需要保存和设置少量寄存器的内容,不涉及存储器管理⽅⾯操作;线程消耗的资源远⼩于进程。
⼆、线程的⽣命周期
1. 线程状态
新建状态
可运⾏状态
运⾏状态
阻塞状态
结束状态
三、实现多线程的两个基础⽅法
1. 继承Thread重写run⽅法
public class MyThread extends Thread {
private int num;
private String threadName;
private long result;
public MyThread(int num, String threadName) {        this.threadName = threadName;
this.num = num;
}
public void run() {
for (int i = 0; i < num; i++) {
result += i;
}
}
public String getThreadName() {
return threadName;
}
public void setResult(long result) {
}
public long getResult() {
return result;
}
}
2. 实现Runnable 接⼝重写run⽅法
public class MyRunnable implements Runnable {
private int num;
private String threadName;
private long result;
public MyRunnable(int num, String threadName) {        this.threadName = threadName;
this.num = num;
}
public void run() {
for (int i = 0; i < num; i++) {
result += i;
}
}
public String getThreadName() {
return threadName;
}
public void setResult(long result) {
}
public long getResult() {
return result;
}
}
3. 测试两种⽅法
package thread;
public class Main {
public static void main(String[] args) {
threadTest();
//        runnableTest();
}
private static void threadTest() {
MyThread myThread_1 = new MyThread(10, "thread_1");
MyThread myThread_2 = new MyThread(10000, "thread_2");
myThread_1.setResult(10);
myThread_1.start();
myThread_2.start();
do {
System.out.println("--------------------------------------------------");
System.out.println("thread name: " + ThreadName() + ", status:  " + myThread_1.isAlive() + ",result: " + Result());            System.out.println("thread name: " + ThreadName() + ", status:  " + myThread_2.isAlive() + ",result: " + Result());        } while (myThread_1.isAlive() || myThread_2.isAlive());
}
private static void runnableTest() {
MyRunnable myRunnable_1 = new MyRunnable(10, "runnable_1");
MyRunnable myRunnable_2 = new MyRunnable(10000, "runnable_2");
Thread thread_1 = new Thread(myRunnable_1);
Thread thread_2 = new Thread(myRunnable_2);
thread_1.start();
thread_2.start();
do {
System.out.println("--------------------------------------------------");
System.out.println("thread name: " + ThreadName() + ", status:  " + thread_1.isAlive() + ",result: " + Result());            System.out.println("thread name: " + ThreadName() + ", status:  " + thread_2.isAlive() + ",result: " + Result());        } while (thread_1.isAlive() || thread_2.isAlive());
}
}
4. 两种⽅法的⽐较
如果⾮要说区别,其实就是实现接⼝和继承的区别,看开发者的使⽤习惯
另外⼀点,两种⽅法的run都是不能传参数的,只能通过类的⽅法设置参数使⽤
四、实现Callable接⼝重写call()⽅法实现多线程
上述两种基础⽅法的run都是void类型,想获取返回只能另加逻辑,⽽实现Callable接⼝重写call()⽅法的好处是允许call函数有返回,下⾯举例
private String threadName;
private long result;
public MyCall(int num, String threadName) {
this.threadName = threadName;
this.num = num;
}
public Long call() throws Exception {
for (int i = 0; i < num; i++) {
result += i;
}
return result;
}
}
// 下⾯是测试
import urrent.ExecutionException;
import urrent.FutureTask;
public class Main {
public static void main(String[] args) {
try {
callTest();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void callTest() throws ExecutionException, InterruptedException {        MyCall myCall_1 = new MyCall(10, "call_1");
MyCall myCall_2 = new MyCall(10000, "call_2");
FutureTask<Long> f1 = new FutureTask<Long>(myCall_1);
FutureTask<Long> f2 = new FutureTask<Long>(myCall_2);
Thread thread_1 = new Thread(f1);
Thread thread_2 = new Thread(f2);
thread_1.start();
thread_2.start();
System.out.());    // 获取返回
System.out.());    // 获取返回
}
}
五、线程池管理多线程
给⼀个简单的样例
private String threadName;
private long result;
public MyCall(int num, String threadName) {
this.threadName = threadName;
this.num = num;
}java线程池创建的四种
public Long call() throws Exception {
for (int i = 0; i < num; i++) {
result += i;
}
return result;
}
}
// 线程池⽅式测试
import urrent.ExecutionException;
import urrent.ExecutorService;
import urrent.Executors;
import urrent.Future;
public class ThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCall myCall_1 = new MyCall(10, "call_1");
MyCall myCall_2 = new MyCall(10000, "call_2");
ExecutorService service = wFixedThreadPool(5);
Future<Long> f1 = service.submit(myCall_1);
Future<Long> f2 = service.submit(myCall_2);
System.out.());    // 获取返回
System.out.());    // 获取返回
service.shutdown();
}
}
六、四种⽅式总结
1. 继承Thread和实现Runnable使⽤起来⽐较接近,唯⼀区别就是Runnable避免了单⼀继承的缺点
2. 有返回的情况下建议使⽤Callable,⽽且可以抛出异常⽅便定位问题
3.线程池其实不算是实现⽅式(有些⼈会把这个也算是实现⽅式),它更像是⼀种管理多线程的⽅式

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。