线程的创建方式

目录

一、线程与进程的区别

二、线程

三、线程的创建方式

1. 继承java.lang.Thread类创建

2.实现java.lang.Runnable接口创建

3.实现 java.util.concurrent.Callable接口创建

4.通过线程池创建


一、线程与进程的区别

        进程(Process)是程序的一次执行过程,是系统运行程序的基本单位。操作系统运行一个程序,即是一个进程从创建、运行到消亡的过程。

        线程(Thread)是进程划分的更小的运行单位,是进程内部的子任务。

        两者关系:一个进程可以包含一个或多个线程,但至少会有一个主线程。

        线程的创建方式_第1张图片

线程与进程的区别
比较方面 进程 线程

根本

区别

进程是操作系统资源分配的基本单位 线程是处理器任务调度和执行的基本单位

资源

开销

(1)每个进程都有独立的代码副本数据空间

(2)进程之间的切换,资源开销

(1)每个线程都有自己独立的运行栈程序计数器

(2)线程之间的切换,资源开销

包含

关系

包含 被包含(线程的执行是并行的)

内存

分配

进程之间的资源和内存空间相互独立 同一进程内所有线程共享本进程的内存空间和资源

影响

关系

一个进程崩溃后,在保护模式下不会对其他进程产生影响 一个线程崩溃,会导致整个进程退出(多进程比多线程健壮

执行

过程

每个独立的进程程序运行的入口和出口 线程不能独立执行,必须依赖在应用程序(进程)中,由应用程序提供多个执行控制

二、线程

        线程是CPU的最小执行单位,线程也有单线程和多线程之分。

       1. 单线程

        单线程是进程中只有一个线程,单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的线程必须处理好,后面的才会执行。

        2.多线程

        由一个以上的线程组成的程序称为多线程程序。Java中,所有的执行都是从主线程(main方法)开始执行,然后在主线程的某个位置创建并启动新的线程。

三、线程的创建方式

1. 继承java.lang.Thread类创建

  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结束执行!");
	}
}

 线程的创建方式_第2张图片

        主线程和子线程的结束是随机的,但可以通过join()方法来实现插队操作,让子线程每次都执行完在主线程结束。

//主线程调用sub中的join()方法
		sub.join();//子线程插队,插入到当前线程的main的执行序列前

 线程的创建方式_第3张图片

2.实现java.lang.Runnable接口创建

  抽象父类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();
	}
}

线程的创建方式_第4张图片

        创建的3个线程共同竞争CPU的资源分配,每次执行的概率是随机的。

3.实现 java.util.concurrent.Callable接口创建

 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接口。

 线程的创建方式_第5张图片

         而且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;
	}
	
}

结果:

线程的创建方式_第6张图片

实现Callable接口的子类,需要通过FutureTask实现类建立与线程Thread类之间关系。

4.通过线程池创建

        线程池是用来容纳线程的,通过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();
					}
					
				}
				
			});
		}
		
	}
}

结果:

线程的创建方式_第7张图片

 以上就是我对线程及线程的创建的理解,欢迎诸君一起探讨,不对请指正哦!

你可能感兴趣的:(jvm)