目录
一、线程与进程的区别
二、线程
三、线程的创建方式
1. 继承java.lang.Thread类创建
2.实现java.lang.Runnable接口创建
3.实现 java.util.concurrent.Callable接口创建
4.通过线程池创建
进程(Process)是程序的一次执行过程,是系统运行程序的基本单位。操作系统运行一个程序,即是一个进程从创建、运行到消亡的过程。
线程(Thread)是进程划分的更小的运行单位,是进程内部的子任务。
两者关系:一个进程可以包含一个或多个线程,但至少会有一个主线程。
比较方面 | 进程 | 线程 |
根本 区别 |
进程是操作系统资源分配的基本单位 | 线程是处理器任务调度和执行的基本单位 |
资源 开销 |
(1)每个进程都有独立的代码副本和数据空间; (2)进程之间的切换,资源开销大 |
(1)每个线程都有自己独立的运行栈和程序计数器; (2)线程之间的切换,资源开销小 |
包含 关系 |
包含 | 被包含(线程的执行是并行的) |
内存 分配 |
进程之间的资源和内存空间相互独立 | 同一进程内的所有线程共享本进程的内存空间和资源 |
影响 关系 |
一个进程崩溃后,在保护模式下不会对其他进程产生影响 | 一个线程崩溃,会导致整个进程退出(多进程比多线程健壮) |
执行 过程 |
每个独立的进程有程序运行的入口和出口 | 线程不能独立执行,必须依赖在应用程序(进程)中,由应用程序提供多个执行控制 |
线程是CPU的最小执行单位,线程也有单线程和多线程之分。
1. 单线程
单线程是进程中只有一个线程,单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的线程必须处理好,后面的才会执行。
2.多线程
由一个以上的线程组成的程序称为多线程程序。Java中,所有的执行都是从主线程(main方法)开始执行,然后在主线程的某个位置创建并启动新的线程。
Thread的子类SubThread:
//Thread的子类SubThread
class SubThread extends Thread{
int random=(int)(Math.random()*1000);
@Override
public void run() {
System.out.println("【子线程】开始执行");
try {
Thread.sleep(random);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// TODO Auto-generated method stub
System.out.println("【子线程】结束执行,休眠;"+random+"毫秒");
}
}
主线程main方法:
public class ThreadDemo1{
public static void main(String[] args) throws InterruptedException {
//主线程开始
System.out.println("【主线程】开始执行!即将创建子线程!");
//创建并启动子线程
SubThread sub=new SubThread();
sub.start();
//主线程结束
System.out.println("【主线程】当子线程sub执行结束后,主线程main结束执行!");
}
}
主线程和子线程的结束是随机的,但可以通过join()方法来实现插队操作,让子线程每次都执行完在主线程结束。
//主线程调用sub中的join()方法
sub.join();//子线程插队,插入到当前线程的main的执行序列前
抽象父类Task:
//父类任务
abstract class Task{
abstract void execute();
}
子类EmailTask:
//子类任务
class EmailTask extends Task implements Runnable{
@Override
void execute() {
Thread currentThread=Thread.currentThread();
String name=currentThread.getName();
System.out.printf("邮件对象%s正在被执行,启动smtp服务器!\n",name);
}
@Override
public void run() {
execute();
}
}
测试类ThreadDemo2:
public class ThreadDemo2 {
public static void main(String[] args) {
//每个Runnable接口的实现类,封装了线程执行的逻辑
EmailTask emailTask=new EmailTask();
//线程的创建还是只能通过new关键字实现
Thread t1=new Thread(emailTask,"【线程戊】");
Thread t2=new Thread(emailTask,"【线程戌】");
Thread t3=new Thread(emailTask,"【线程卯】");
t1.start();
t2.start();
t3.start();
}
}
创建的3个线程共同竞争CPU的资源分配,每次执行的概率是随机的。
Callable的实现类SumCallTask:
class SumCallTask implements Callable{
//私有成员变量
private int begin,end;
public SumCallTask(int begin,int end) {
this.begin=begin;
this.end=end;
}
@Override
public Integer call() throws Exception {//实现不同数据范围的计算任务
int tol=0;
for(int i=begin;i<=end;i++) {//内部实现逻辑
tol+=i;
}
System.out.println(Thread.currentThread().getName());
return tol;
}
}
查阅Thread源码你会发现,Threa的有参构造方法中没有与Callable实现类有关的构造方法,而SumCallTask的功能是实现指定范围内的计算任务的,是需要有返回值的。
Callable接口的实现类并不能直接与Thread线程建立联系,此时我们需要了解一个类FutureTask
FutureTask实现了RunnableFuture接口,而RunnableFuture接口有继承自Runnable接口,等同于间接的FutureTask实现了Runnable接口。
而且FutureTask内部定义了get方法,用于获取当前任务的返回值。刚好,适用于需要得到返回值的功能。
测试类FutureTaskDemo:
public class FutureTaskDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//SumCallTask实现Callable接口,实现不同数据范围的计算任务
SumCallTask sct1=new SumCallTask(1, 300);
SumCallTask sct2=new SumCallTask(301, 600);
SumCallTask sct3=new SumCallTask(601, 1000);
//创建FutureTask(Runnable接口实现类)
//通过FutureTask类型的对象来建立与线程之间的关系
//Callable ---> FutureTask
FutureTask ftask1=new FutureTask(sct1);
FutureTask ftask2=new FutureTask(sct2);
FutureTask ftask3=new FutureTask(sct3);
//创建线程
Thread t1=new Thread(ftask1);
Thread t2=new Thread(ftask2);
Thread t3=new Thread(ftask3);
//启动线程
//执行ftask1.run(),调用callable.call()得到结果outcome
t1.start();
t2.start();
t3.start();
//线程执行结束,分别获取各自线程的返回结果结果
System.out.println("开始分别获取...........");
Integer res1=ftask1.get();
Integer res2=ftask2.get();
Integer res3=ftask3.get();
System.out.println("汇总各自计算结果:");
Integer result=res1+res2+res3;
System.out.println("最终结果:"+result);
}
}
class SumCallTask implements Callable{
//私有成员变量
private int begin,end;
public SumCallTask(int begin,int end) {//有参构造
this.begin=begin;
this.end=end;
}
@Override
public Integer call() throws Exception {
int tol=0;
for(int i=begin;i<=end;i++) {//内部实现逻辑
tol+=i;
}
System.out.println(Thread.currentThread().getName());
return tol;
}
}
结果:
实现Callable接口的子类,需要通过FutureTask实现类建立与线程Thread类之间关系。
线程池是用来容纳线程的,通过Executors类的newFixedThreadPool()方法可以创建固定大小的线程池。
//创建包含10个线程的线程池
ExecutorService executorservice=Executors.newFixedThreadPool(10);
在通过所创建的线程池对象的execute()方法向线程池提交一个执行任务(创建一个Runnable接口的匿名实现类),线程池会自动分配一个空闲线程执行该任务。
executorservice.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"被执行了1次");
try {
//休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
如果没有空闲线程,则该任务处于等待队列(工作队列)。
完整代码:
//线程的创建方式4:通过线程池创建
public class ThreadPoolsDemo {
public static void main(String[] args) {
//创建包含10个线程的线程池
ExecutorService executorservice=Executors.newFixedThreadPool(10);
while(true) {//不确定数量的线程请求
//向线程池提交一个执行任务(Runnable接口的实现类对象)
//线程池分配一个“空闲线程”执行该任务
//如果没有空闲线程,则该任务处于“等待队列(工作队列)”
executorservice.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"被执行了1次");
try {
//休眠1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
}
}
结果:
以上就是我对线程及线程的创建的理解,欢迎诸君一起探讨,不对请指正哦!