《Java面向对象程序设计》学习笔记——第 15 章 Java 多线程机制

​笔记汇总:《Java面向对象程序设计》学习笔记

​# 第 15 章 Java 多线程机制

Java 语言的一大特点就是内置了对多线程的支持。

多线程是指同时存在几个执行体,按几条不同的执行线索共同工作的情况。

我们感觉线程正在同步执行,但并不是真的同时执行多个线程,只是Java 快速地从一个线程切换到另一个线程。

Java 虚拟机( JVM )负责管理线程。

15.1 Java 中的线程

15.1.1 程序、进程与线程

程序

  • 程序是一段静态的代码。

进程

  • 进程是程序的一次动态执行过程。

  • os 可以管理多个进程,即让多个进程都机会使用系统的资源,比如 CPU 资源。

    (一个 OS 里可以多个进程)

线程

  • 一个进程在其执行过程中可以产生多个线程。
  • 有自己独特的运行机制。
  • 线程是比进程更小的执行单位,也有产生、存在和消亡的过程。

其他

  • 每个进程都有一段专用的内存区域,但,线程间可以共享相同的内存单元(包括代码与数据)
  • 操作系统分时管理各个进程,按时间片轮流执行每个进程。
  • Java 的多线程就是在操作系统每次分时给 Java 程序一个时间片的 CPU 时间内,在若干个独立的可控制的线程之间进行切换
  • 每个 Java 程序都有一个默认的主线程。

15.1.2 线程的状态与生命周期

Java 语言使用 Thread 类及其子类的对象来表示线程。

使用 Thread 类及其子类的对象来表示线程。

Thread 提供 getstate() 方法返回枚举类型 Thread.State 的下列枚举常量之一:

NEW , RUNNABLE , BLOCKED , WAITING , TIMED_WAITING , TERMINATED

新建状态( NEW )

  • 当一个 Thread 类或其子类的对象被声明并创建时,新生的线程对象处于NEW状态,称作新建状态。

  • 即尚未启动(没有调用 start() 方法)的线程处于此状态。

可运行状态 (RUNNABLE)

  • 处于 NEW 状态的线程,调用 Thread 类提供的 start() 方法,进入 RUNNABLE状态。

  • NEW 状态线程调用 start() ,让自己进入 RUNNABLE状态。

  • JVM 就会知道又有一个新一个线程排队等候切换了。

  • 当 JVM 将 CPU 使用权切换给 RUNNABLE 状态的线程时,如果线程是 Thread 的子类创建的,该类中的 run() 方法就立刻执行。

    如果线程是 Thread 的子类创建的,该类中的 run () 方法就立刻执行。所以我们必须在子类中重写父类的 run() 方法。

中断状态 (BLOCKED 、 WAITING 、 TIMED_WAITING)阻塞、等待、有限时间等待

  • BLOCKED 、 WAITING 、 TIMED_WAITING 状态都属于中断状态。
  • 当中断的线程重新进入 RUNNABLE 状态后,一旦 JVM 将 CPU 使用权切换给该线程, run() 方法将从中断处继续执行。

死亡状态 (TERMINATED)终止

  • 线程完成了它的全部工作,即执行完 run() 方法,该线程进入 TERMINATED 状态。

  • 只有处于NEW状态的线程可以调用 start() 方法,处于其他状态的线程都不可以调用 start() 方法,否则将触发 ILLegalThreadStateException 异常。

在主线程中用 Thread 的子类创建了两个线程,这两个线程在命令行窗口分别输出 5 句"老虎"和"小猫";主线程在命令行窗口输出 6 句"主人"。

注意,程序在不同的计算机上运行或在同一台计算机上反复运行的结果不尽相同。

复制代码
public class Tiger extends Thread {
	public void run() {
		for (int i = 1; i <= 5; i++) {
			System.out.print("|老虎" + i);
			try {
				sleep(1000); // 状态:TIMED_WAITING
			} catch (Exception exp) {
			}
		}
	}
}


public class Cat extends Thread {
	public void run() {
		for (int i = 1; i <= 5; i++) {
			// System.out.print("|小猫"+i+"状态:"+getState()+"|");
			System.out.print("|小猫" + i);
		}
	}
}


public class Example15_1 { 
	public static void main(String args[]) { // 主线程
		Tiger tiger;
		Cat cat;
		tiger = new Tiger(); // 创建线程
		cat = new Cat(); // 创建线程
		System.out.println("tiger的状态:" + tiger.getState());
		System.out.println("cat状态:" + cat.getState());
		tiger.start(); // 启动线程
		cat.start(); // 启动线程
		for (int i = 1; i <= 6; i++) {
			System.out.printf("\n%s", "tiger状态:" + tiger.getState());
			System.out.printf("\n%s", "cat状态:" + cat.getState());
			System.out.printf("\n%s", "主人" + i);
		}
		System.out.printf("\n%s", "|tiger的状态:" + tiger.getState());
		System.out.printf("\n%s", "|cat状态:" + cat.getState());
	}
}

输出(不唯一,每次线程顺序都可能不一样)

复制代码
tiger的状态:NEW
cat状态:NEW

tiger状态:RUNNABLE|小猫1|老虎1|小猫2
cat状态:BLOCKED|小猫3
主人1|小猫4
tiger状态:TIMED_WAITING|小猫5
cat状态:RUNNABLE
主人2
tiger状态:TIMED_WAITING
cat状态:TERMINATED
主人3
tiger状态:TIMED_WAITING
cat状态:TERMINATED
主人4
tiger状态:TIMED_WAITING
cat状态:TERMINATED
主人5
tiger状态:TIMED_WAITING
cat状态:TERMINATED
主人6
|tiger的状态:TIMED_WAITING
|cat状态:TERMINATED|老虎2|老虎3|老虎4|老虎5

15.1.3 线程的调度与优先级

处于就绪状态的线程首先进人就绪队列排队等候 CPU 资源,同一时刻在就绪队列中的线程可能有多个。

Java 虚拟机中的线程调度器负责管理线程。

在采用时间片的系统中,每个线程都有机会获得 CPU 的使用权,以便使用 CPU 资源执行线程中的操作。

当线程使用 CPU 资源的时间到了后,即使线程没有完成自己的全部操作, Java 调度器也会中断当前线程的执行,把 CPU 的使用权切换给下一个排队等待的线程,当前线程将等待 CPU 资源的下一次轮回,然后从中断处继续执行。

Java 调度器的任务是使高优先级的线程能始终运行,一旦时间片有空闲,则使具有同等优先级的线程以轮流的方式顺序使用时间片。

在实际编程时,不提倡使用线程的优先级来保证算法的正确执行。

15.2 用 Thread 的子类创建线程

用 Thread 类或子类创建线程对象。

在编写 Thread 类的子类时,需要重写父类的 run() 方法,其目的是规定线程的具体操作,否则线程就什么也不做,因为父类的 run() 方法中没有任何操作语句。

例子

  • 除主线程外还有两个线程,这两个线程共享一个 StringBuffer 对象两个线程在运行期间将修改 StringBuffer 对象中的字符。

  • 为了使结果尽量不依赖于当前 CPU 资源的使用情况,我们应当让线程主动调用 sleep (int n ) 方法让出 CPU 的使用权进入中断状态,

  • sleep(int n) 方法是 Thread 类的静态方法,线程在占有 CPU 资源期间,通过调用 sleep(int n) 方法来使自己放弃 CPU 资源,休眠一段时间

    public class People extends Thread {
    StringBuffer str;
    People(String s,StringBuffer str) {
    setName(s); //调用从Thread类继承的setName方法为线程起名字
    this.str=str;
    }
    public void run() {
    for(int i=1;i<=3;i++) {
    str.append(getName()+","); //将当前线程的名字尾加到str 这里有可能被中断
    System.out.println("我是"+getName()+",字符串为:"+str);
    try { sleep(1000); // 中断状态 (TIMED_WAITING)
    }
    catch(InterruptedException e){}
    }
    }
    }

    public class Example15_2 {
    public static void main(String args[]) {
    People personOne,personTwo;
    StringBuffer str=new StringBuffer();
    personOne=new People("张三",str);
    personTwo =new People("李四",str);
    personOne.start();
    personTwo.start();
    }
    }

输出(不唯一,每次线程顺序都可能不一样)

复制代码
我是张三,字符串为:张三,李四,
我是李四,字符串为:张三,李四,
我是李四,字符串为:张三,李四,李四,张三,
我是张三,字符串为:张三,李四,李四,张三,
我是李四,字符串为:张三,李四,李四,张三,李四,张三,
我是张三,字符串为:张三,李四,李四,张三,李四,张三,

15.3 使用 Runnable 接口


还在做笔记中,请稍等~

相关推荐
这个名字先用着2 分钟前
NPN转PNP,PNP转NPN方法
科技·学习·制造
皇族崛起8 分钟前
【docker安装部署】- 一个可用的Docker 镜像配置 和 DNS配置
java·docker·容器
互亿无线明明14 分钟前
国际短信通知服务:如何为全球业务构建稳定的跨国消息触达体系?
java·c语言·python·php·objective-c·ruby·composer
深盾科技14 分钟前
Linux跨进程内存操作的3种方法及防护方案
java·linux·网络
Lynnxiaowen15 分钟前
今天我们学习kubernetes内容持久化存储
linux·运维·学习·容器·kubernetes
Jerry9527062819 分钟前
1.什么式可用性
java·分布式·后端·架构·高可用·秒杀
轻描淡写60619 分钟前
二进制存储数据
java·开发语言·算法
【上下求索】33 分钟前
学习笔记094——Ubuntu 如何部署 frp 客户端服务?
笔记·学习·ubuntu
君爱学习40 分钟前
JVM对象分配内存如何保证线程安全?
java
li星野1 小时前
打工人日报#20251210
笔记