并发编程--什么是线程?

本文详细解析Java线程的三种创建方法(继承Thread、实现Runnable和Callable),探讨其优缺点,涵盖了线程状态转换及常用状态转换方法,包括线程从NEW到RUNNABLE再到WAITING的过程。同时介绍了线程在操作系统中的映射关系和关键术语如KLT模型。
摘要由CSDN通过智能技术生成

一、什么是线程?

1)介绍

线程是调度CPU资源的最小单位,线程模型分为KLT模型与ULT模型,JVM使用的KLT模
型,Java线程与OS线程保持1:1的映射关系,也就是说有一个java线程也会在操作系统里有一个对应的线程。
在这里插入图片描述

2)Java线程与OS线程保持1:1的映射关系

我们本地执行代码测试一下是否是1:1的关系。初始系统运行的线程是1771个。我们创建一个有300个线程的测试,看结果是什么

public static void main(String[] args) {
		for (int i = 0; i < 300; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					while (true) {
						try {
							Thread.sleep(100);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}).start();
		}
	}

在这里插入图片描述
在这里插入图片描述
我们可以发现从2018变到了2351,忽略线程略微的变动,能看到300的一个变动就可,因为系统还运行这其他进程的各种线程。

二、三种创建线程的方式

1)extends Thread

① demo实例

public class CreateThreadTest1 extends Thread{

	@Override
	public void run() {
		System.out.println("通过继承Thread,线程号:" + currentThread().getName());
	}
}
public static void main(String[] args) {

		Thread thread = new CreateThreadTest1();
		thread.start();
	}

② 过程描述

2)implements Runnable

① demo实例

public class CreateThreadTest implements Runnable{
	@Override
	public void run() {
		System.out.println("通过实现Runnable,线程号:" + Thread.currentThread().getName());
	}
}
public static void main(String[] args) {
		Thread thread = new Thread(new CreateThreadTest());
		thread.start();
	}

② 过程描述

3)implements Callable

① demo实例

public class CreateThreadTest2 implements Callable {
	@Override
	public Object call() throws Exception {
		System.out.println("通过实现Callable,有返回值;线程号:" + Thread.currentThread().getName());
		return 10;
	}
}
public static void main(String[] args) {
		FutureTask futureTask=new FutureTask(new CreateThreadTest2());
		Thread thread = new Thread(futureTask);
		thread.start();
		try {
			System.out.println(futureTask.get());
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
	}

② 过程描述

  • 1.实现Callable类 ,重写call()方法 ,该call()方法将作为线程执行体,并且有返回值。
  • 2.创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
  • 3.使用FutureTask对象作为Thread对象的target创建并启动新线程。
  • 4.调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

③ 两种实现方式

  • 1.用FutureTask封装
  • 2.线程池调用
public static void main(String[] args) {
		//随意创建一个线程池
		ExecutorService executorService = Executors.newSingleThreadExecutor();
		Future future = executorService.submit(new CreateThreadTest2());
		try {
			//获取值
			System.out.println(future.get());
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		//关闭线程池
		executorService.shutdown();

	}

三、三种创建线程的优缺点

1)采用继承Thread类方式:

(1)优点:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this,即可获得当前线程。
(2)缺点:因为线程类已经继承了Thread类,所以不能再继承其他的父类。

2)采用实现Runnable接口方式:

(1)优点:线程类只是实现了Runable接口,还可以继承其他的类。在这种方式下,可以多个线程共享同一个目标对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
(2)缺点:编程稍微复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。

3)Runnable和Callable的区别:

(1)Callable规定的方法是call(),Runnable规定的方法是run().
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
(3)call方法可以抛出异常,run方法不可以,因为run方法本身没有抛出异常,所以自定义的线程类在重写run的时候也无法抛出异常
(4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。

四、生命状态

1)状态

  • NEW,新建
  • RUNNABLE,运行
  • BLOCKED,阻塞
  • WAITING,等待
  • TIMED_WAITING,超时等待
  • TERMINATED,

2)状态切换图解

在这里插入图片描述

3)源码位置

在Thread类里,有一个内部类state,来标明状态
public enum State {
}

五、常用的一些状态转换方法调用

1)由NEW到RUNNABLE的过程

RUNNABLE可以分为两个阶段:就绪状态和运行状态

① 调用thread.run()到就绪状态

②调用thread.start()运行状态

③调用thread.yield()方法由运行状态到就绪状态

public static native void yield();
1、这是一个静态方法,一旦执行,它会使当前线程让出CPU。但要注意,让出CPU并不表示当前线程不执行,当前线程在让出CPU后,还会进行CPU资源的争夺,但是否能够再次别分配到就不一定了。

因此,对于Thread.yield()的调用就好像是说:“我已经完成了一些最重要的工作了,我可以休息一下,可以给其他线程一些工作机会”。

2、因为还会再次抢夺,所以就好比我机会让出去了,其他线程珍不珍惜可就不关我的事了。

3、yield只会让步优先级同级或者高的线程,这就是社会规则。

// 设置优先级:MIN_PRIORITY最低优先级1;NORM_PRIORITY普通优先级5;MAX_PRIORITY最高优先级10
threadA.setPriority(Thread.MIN_PRIORITY);
threadB.setPriority(Thread.MAX_PRIORITY);

4、只会让出CPU资源,但是若持有锁,则不会让出。所以说再AQS中常见,因为本身就是一种乐观锁。

2)由RUNNABLE到WATING状态

① LockSupport.park()

LockSupport类是Java6(JSR166-JUC)引入的一个类,提供了基本的线程同步原语。LockSupport实际上是调用了Unsafe类里的函数,归结到Unsafe里,只有两个函数:

  public native void unpark(Thread jthread);
  public native void park(boolean isAbsolute, long time);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值