读书笔记【Java并发编程之美--Java三种线程创建方式】

1.1什么是线程

什么是进程

要理解什么是线程,首先有必要了解下什么是进程,因为线程是进程中的一个实体,线程本身是不会独立存在的。进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,线程则是进程的一个执行路径,一个进程中至少有一个线程,进程中的多个线程共享进程的资源。

操作系统在分配资源时是把资源分配给进程的,但是CPU资源比较特殊,它是被分配到线程的,因为你真正要占用CPU运行的是线程,所以也说线程是CPU分配的基本单位。
进程是系统进行资源分配和调度的基本单位;线程是CPU分配的基本单位。

进程和线程的关系

链接: link.

进程与线程的关系
程序计数器是一块内存区域,用来记录线程当前要执行的指令地址。那么为何要将程序计数器设计为线程私有的呢?前面说了线程是占用CPU执行的基本单位,而CPU一般是使用时间片轮转方式让线程轮询占用的,所以当前线程CPU时间片用完后,要让出CPU,等下次轮到自己的时候再执行。那么如何知道之前程序执行到哪里了呢?其实程序计数器就是为了记录该线程让出CPU时执行地址的,待再次分配到时间片时线程就可以从自己私有的计数器指定地址继续执行。另外需要注意的是,如果执行的是native方法,那么PC计数器记录的就是undefined地址,只有执行的是Java代码时PC计数器记录的才是下一条指令的地址。

另外每个线程都有自己的栈资源,用于存储该线程的局部变量,这些局部变量是该线程私有的,其他线程是访问不了的,除此之外栈还用来存放线程的调用栈帧。

堆是一个进程中最大的一块内存,堆是被进程中的所有线程共享的,是进程创建时分配的,堆里面主要存放使用new操作创建的对象实例。

方法区则用来存放JVM加载的类、常量及静态变量等信息,也是线程共享的。

1.2线程创建与运行

Java中有三种线程创建方式,分别为实现Runnable接口的run方法,继承Thread类并重写run的方法,使用FutureTask方式。

继承Thread 类的方式

public class ThreadTest{
	//继承Thread类并重写run方法
	public static class MyThread extends Thread{
		@override
		public void run(){
			System.out.println("I am a child thread");
		}
	}
	public static void main(String[]args){
		//创建线程
		MyThread thread = new MyThread();
		//启动线程
		thread.start();
	}
}

实现Runnable接口的run方法的方式

public class ThreadTest{
	//继承Thread类并重写run方法
	public static class RunnableTask implements Runable{
		@override
		public void run(){
			System.out.println("I am a child thread");
		}
	}
	public static void main(String[]args){
		//创建线程
		RunnableTask task = new RunnableTask ();
		//启动线程
		new Thread(task).start();
		new Thread(task).start();
	}
}

使用FutureTask的方式

public class ThreadTest{
	public static class CallerTask implements Callable<String>{
		@Override
		public void call() throws Exception{
			return "hello";
		}
	
	}
	
	public static void main(String[]args){
		//创建异步线程
			FutureTask<String> futureTask = new FutureTask<String>(new CallerTask());
			//启动线程
			new Thread(futureTask).start();
			try{
				//等待任务执行完毕,并返回结果
				String result = futureTask.get();
				System.out.println(result);
			}catch(ExecutionException e){
				e.printStackTrace();
			}catch(InterruptedException e){
				e.printStackTrace();
			}
	}
}

小结: 使用继承方式的好处是方便传参,你可以在子类里面添加成员变量,通过 set 方法设置参数或者通过构造函数进行传递,而如果使用 Runnable 方式,则只能使用主线程里面被声明为final的变量。不好的地方是 Java 不支持多继承,如果继承了 Thread 类,那么子类不能再继承其他类,而 Runnable 则没有这个限制。前两种方法都没办法拿到任务的返回结果,但是 FutureTask 方式可以。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值