疯狂Java讲义_Chapter16多线程

Java多线程

1.线程概述

1.线程和进程

  • 在一个系统中,每个独立运行的程序是一个进程;每个程序中有多个顺序流,称为线程;

  • 进程的特征:

  1. 独立性:每个进程都有着自己独立的地址空间,互不影响;

  2. 动态性;

  3. 并发性:多个进程可以在单个处理器上并发执行,且互不影响;

  • 并发性:同一时刻只能有一条指令执行,但是多个进程指令被快速轮换执行,在宏观上看起来是一起执行的效果;
  • 并行性:同一时刻,多条指令在多个处理器上同时执行;
  • Windows和Linux都是抢占式的多任务操作策略;
  • 线程又被称为是轻量级进程,每个线程是独立运行的;一个进程至少包括一个线程;

2.多线程的优势

  • 线程之间共享内存、文件句柄和状态信息;这也是线程执行速度高于进程的原因;
  • 多线程的应用:
  1. javaWeb服务器响应多个用户需求;
  2. JVM虚拟机的垃圾回收机制;

2.线程的创建和启动

1.继承Thread类创建线程类

  • 所有的线程对象都必须是Thread类或其子类的实例;每个线程的任务就是执行一段程序流;
  • 创建并启动线程的步骤如下:
/*
1.定义Thread类的子类,重写Thread的run方法,run就是线程的执行体;
2.创建Thread子类的实例,即创建了线程对象;
3.调用线程对象的start()方法来启动线程;
 */

//继承Thread
public class FirstThread extends Thread
{
	private int i;
	// 重写run
    @Override
	public void run()
	{
		for ( ; i < 100; i++)
		{
			// 直接调用getName方法可以返回当前线程的名字
			System.out.println(getName() + " " + i);
		}
	}
	public static void main(String[] args)
	{
        //这是主线程
		for (var i = 0; i < 100; i++)
		{
			// 调用Thread的currentThread方法获得当前线程;
			System.out.println(Thread.currentThread().getName()
				+ " " + i);
			if (i == 20)
			{
				// 创建并启用第一个线程
				new FirstThread().start();
				// 创建并启用第二个线程
				new FirstThread().start();
			}
		}
	}
}
  • main方法体就是主线程的方法体;

2.实现Runable接口创建线程类

  • 步骤如下

/*
使用Runable接口创建线程
1.定义Runable接口的实现类,并重写run方法,run是线程的实现体;
2.创建实现类的实例,并以此实例作为Thread的target来创建Thread对象,该对象为线程对象;
3.调用线程对象的start方法启动线程;
 */



//实现Runable接口
public class SecondThread implements Runnable
{
	private int i;
	//重写run方法
	public void run()
	{
		for ( ; i < 100; i++)
		{
			//如果想获得线程名,只能通过Thread.currentThread()方法
			System.out.println(Thread.currentThread().getName()
				+ " " + i);
		}
	}

	public static void main(String[] args)
	{
		for (var i = 0; i < 100; i++)
		{
			System.out.println(Thread.currentThread().getName() //主线程
				+ " " + i);
			if (i == 20)
			{
				var st = new SecondThread();     //创建线程实例;
				//用实例初始化Thread实例,并启动线程;
				new Thread(st, "新线程1").start();
				new Thread(st, "新线程2").start();
			}
		}
	}
}

 

3.使用Callable和Future创建线程(739)(这个方法有点复杂)

  • Thread将run方法作为线程执行体,但是不能将其他方法作为线程执行体;(C#可以)

  • Callable接口是Runable的增强版,提供了一个较强的call方法代替了run方法;

4.创建线程的三种方式对比

  • 由于扩展性原因,一般推荐采用Runable接口实现线程的编写;

3.线程的生命周期

1.新建和就绪状态

  • 线程的生命周期中,包括新建New,就绪Ready,运行Running,阻塞Blocked、死亡Dead共5个状态;
  • new一个线程对象,就进入了线程的新建状态;
  • 线程对象调用start方法,就进入了就绪状态;处于这个状态的线程并没有开始运行,只是表示该线程可以运行了,何时开始运行取决于JVM线程调度器的调度;
  • run方法不能直接执行,启动线程只能是start方法;
  • 如果希望子线程点击start方法后立即执行,程序可以使用Thread.sleep(1)来让当前运行的线程睡眠1ms,此时CPU就会去执行另一个已经就绪的线程;

2.运行和阻塞状态

  • 线程调度平台会定期的让一些运行的线程进入阻塞状态,让其他线程有机会运行;
  • Windows和Linux都是抢占式调度方法;而手机系统采用协作式调度方法,即由当前运行线程自动调用sleep或者yield方法进入阻塞状态;
  • 当发生如下状态时,系统进入阻塞状态:
  1. 线程调用sleep方法主动放弃占用线程资源;
  2. 线程调用了一个阻塞式IO方法,在该方法返回之前,该线程被进程阻塞;
  3. 线程试图获得一个同步监视器;
  4. 线程在等待某个通知notify;
  5. 程序调用了线程的suspend方法将该线程挂起(此方法容易导致死锁)
  • 被阻塞的线程会在解除阻塞后重新进入就绪状态;

3.线程死亡

  • 线程会以如下三种方式结束,并进入死亡状态:
  1. run或者call方法执行完成,线程正常结束;
  2. 线程抛出一个未捕获的Exception或Error;
  3. 直接调用stop方法停止线程;
  • 主线程和子线程是平级关系,主线程死亡不会影响子线程;
  • 为了验证线程是否死亡,可以调用isAlive()方法来验证;
  • 已经死亡的线程不能被再次start;

 

4.控制线程
1.join线程

  • join是让一个线程等待另一个线程的方法,当某个程序执行流过程中调用其他线程的join方法时,该线程将被阻塞,直到被join方法加入的join线程执行完为止;

/*
join方法有如下三种重载形式
1.join():等待被join的线程执行完成;
2.join(long millis):等待被join的线程的执行时间最长为millis毫秒;如果在给定时间内被join的线程还没有执行结束,则不再等待;
3.join(long millis, int nanos):等待被join的线程的时间最长为millis+nanos毫微秒;
 */


public class JoinThread extends Thread
{
	// 该构造器用于设置线程名;Thread的有参构造器用于给线程命名
	public JoinThread(String name)
	{
		super(name);
	}
	//重写run方法
	public void run()
	{
		for (var i = 0; i < 100
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值