线程的基本概念以及线程的实现(常见面试题)

一、线程的基本概念

1.1 进程和线程

  • 进程:正在运行的程序,需要内存和CPU资源进行运算执行,
    一个程序包含一个或多个进程
  • 线程:是进程的组成单元,一个进程包含一个或多个线程,每个线程负责单独执行一个任务

1.2 进程和线程的区别

  1. 一个进程包含一个或多个线程
  2. 每个进程都有自己独立的内存空间,线程没有自己独立的内存空间,线程共享所在进程的内存空间
  3. 进程是重量级的单元,需要系统资源比较多,线程是轻量级单元,需要资源比较少

1.3 多进程和多线程

  • 多进程是操作系统可以同时运行多个进程。一个CPU内核一个时间只能执行一个进程,CPU会在多个进程之间进行来回切换,因为速度特别快,用户感觉不到。
  • 多线程是一个进程里面有多个线程,CPU执行进程时会来回切换里面所有的线程,每个线程会分配到一定的CPU的执行时间(CPU时间片)

1.4 多线程的应用场景

  • JAVAEE企业级开发:大量的用户需要同时访问网站的服务器,如:双十一、秒杀等。如果服务器只有一个线程,多个用户需要排队和服务器通信,效率非常低;多线程就是一个用户连接服务器后,服务器就会开一个新线程负责用户的通信,用户之间就不会相互影响。
  • 游戏开发:同时进行网络通信、游戏角色控制、图形绘制等操作,必须每个用一个线程执行。

1.5 并行和并发

  • 并发:一个CPU在多个线程之间快速切换,达到同时执行多个任务的目的
  • 并行:多个CPU可以同时执行一个进程中的多个线程

二、线程的实现

2.1 继承Thread类

  1. 继承Thread类
  2. 重写run方法
  3. 创建线程对象,调用start方法
/**
 * 自定义线程类
 * @author xray
 *
 */
public class MyThread extends Thread{

	/**
	 * 执行线程任务的方法
	 */
	public void run(){
		//Thread.currentThread()是获得系统当前执行的线程
		System.out.println(Thread.currentThread().getName()+"线程执行了!!!");
	}
	
	public static void main(String[] args) {
		//主线程中执行
		System.out.println(Thread.currentThread().getName()+"线程执行了!!!");
		//创建线程对象
		MyThread thread1 = new MyThread();
		MyThread thread2 = new MyThread();
		//启动线程
		thread1.start();
		thread2.start();
	}
}

2.2 实现Runnable接口

  1. 实现Runnable接口
  2. 实现run方法
  3. 创建自定义线程对象,作为参数传入Thread对象
  4. 调用start方法
/**
 * 自定义线程类
 * @author xray
 *
 */
public class MyRunnable implements Runnable{

	/**
	 * 实现run方法
	 */
	@Override
	public void run() {
		System.out.println("当前执行的线程是:"+Thread.currentThread().getName());
	}
	
	
	public static void main(String[] args) {
		//创建Thread对象,传入Runnable对象
		Thread thread1 = new Thread(new MyRunnable());
		//调用start方法
		thread1.start();
		//使用匿名内部类实现Runnable
		Thread thread2 = new Thread(new Runnable(){
			@Override
			public void run() {
				System.out.println("匿名内部类,当前执行的线程是:"+Thread.currentThread().getName());
			}
		});
		thread2.start();
		//使用Lambda实现Runnable
		Thread thread3 = new Thread(()->{
			System.out.println("Lambda,当前执行的线程是:"+Thread.currentThread().getName());
			});
		thread3.start();
	}

}

2.3 实现Callable接口

前面两种方法都不能返回结果,Callable的方法可以返回值

  1. 实现Callable接口,实现call方法
  2. 创建FutureTask对象,传入Callable对象
  3. 创建Thread对象,传入FutureTask对象
  4. 调用Thread对象的start方法
  5. 调用FutureTask对象的get方法,获得返回值
/**
 * Callable的实现类
 * @author xray
 *
 */
public class MyCallable implements Callable<Long>{

	@Override
	public Long call() throws Exception {
		System.out.println("执行call的线程是:"+Thread.currentThread().getName());
		//模拟执行耗时运算
		Long sum = 0L;
		for(int i = 0;i < 100000000L;i++){
			sum += i;
		}
		//返回结果
		return sum;
	}
	
	
	public static void main(String[] args) {
		//创建FutureTask对象,传入Callable对象
		FutureTask<Long> task = new FutureTask<>(new MyCallable());
		//创建Thread,传入FutureTask对象
		Thread thread = new Thread(task);
		//启动线程
		thread.start();
		//获得结果
		try {
//			long result = task.get();
			long result = task.get(5,TimeUnit.SECONDS);
			System.out.println("获得计算结果:"+result);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		} catch (TimeoutException e) {
			e.printStackTrace();
		}
	}
}

Thread、Runable、Callable的区别:

Thread和Runnable的实质是继承关系,没有可比性。无论使用Runnable还是Thread,都会new Thread,然后执行run方法。用法上,如果有复杂的线程操作需求,那就选择继承Thread,如果只是简单的执行一个任务,那就现runnable。当需要拿到线程的返回值的时候就可以去使用Callable去实现。

三、线程的生命周期

  1. 新建
    线程new出来,没有调用start方法,CPU没有分配时间片
  2. 就绪
    线程调用了start方法,CPU准备分配时间片,但是没有真正分配
  3. 运行
    线程抢到了CPU,开始执行run方法
  4. 阻塞

    进入阻塞状态:
    • 调用sleep方法
    • 线程的锁调用wait方法
    • 调用suspend(挂起)方法
    • 流的IO操作

      从阻塞恢复:
    • sleep的时间完毕
    • 锁调用notify或notifyAll方法
    • 调用resume(恢复)方法
    • IO操作完毕

阻塞状态结束后,回到就绪状态

  1. 死亡,run方法执行完毕后线程进入死亡状态
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值