理解JAVA多线程技术——《JAVA多线程编程核心技术》读书笔记

1. JAVA多线程内存架构

要想深入理解JAVA多线程的机制,必须要从JVM的内存管理机制说起。

1.1 典型的JAVA多线程内存管理机制:

JVM将运行时内存按功能分为以下三个部分:堆(heap),栈(stack)和方法区(Method Area)。

  1. 方法区主要存放每个类编译后的字节码,它包含每个类的全部信息,包括类的属性,静态方法和一般方法等,还包含字符串等静态内容;
  2. 主要存放根据方法区中的类实例化(new)出的对象和数组(数组也可以被看作对象的一种形式),该对象拥有该类的初始化对象属性,以及该类的方法指针(指向方法区中类的对应方法),堆中的对象都是线程共享的,也即所有线程都能使用堆中的对象;
  3. 由多个线程组成,每个线程都保存由当前线程的运行上下文,缓存数据,方法内临时变量等;

多线程内存机制

1.2模拟执行过程:

  1. JVM加载当前含main静态方法的类到方法区,并在栈中创建main线程;
  2. main线程中加载了main静态方法,该方法在当前main线程中完成创建对象1的操作:
Class1 class1 = new Class1();
//从方法区中复制类1的属性到堆中创建的对象1d的内存区域,完成初始化工作;
//main线程中仅保存class1这样一个对象指针指向堆中的对象1所在内存空间;对象1中方法指针指向方法区类1的方法内容;
  1. main线程中创建了新线程,线程1,并按以上方法实例化出对象2;
  2. CPU根据多个线程的优先级随机接入要处理的线程。

由以上CPU轮询的过程可以看出多线程并不是真正意义上的多个线程并行执行(GPU是真正意义上的并行),而是CPU在多个线程任务之间切换,不至于卡死在一个线程任务上。

1.3多线程初探:

由于堆中的对象是公开透明的,也即任何一个线程都可以操作,此时如果两个线程调用了堆中同一个对象的同一个方法(去修改同一个属性的值),必定会引起冲突。

因而多线程问题也即是多个线程争同一对象或同一类中的属性和方法。

为了保证运行时不冲突(操作的原子性),JAVA选择给 方法加锁 的形式完成同步工作(也即排队进入该方法,避免冲突)。

  • :不同方法可以加相同的锁,也可以加不同的锁,但一种锁只有一个钥匙;
  • 钥匙:哪个线程在执行过程中先运行到锁处就先获得钥匙,其他线程没有钥匙不得进入该种锁限制的所有方法;
    锁和钥匙在使用过程中也必须满足:
  • N个线程只能有1个能获得当前对象的当前种类锁的钥匙;
  • N个线程可以同时分别获得同一对象的N种锁的钥匙;
  • 针对无锁的方法,任何线程都可以随意进出执行。

2. 多线程建立方法

JAVA编程实现多线程的常用方法有两种:

  • 继承Thread类,并覆盖run()方法
    在这里插入图片描述
  • 实现Runnable接口,并覆盖run()方法
    在这里插入图片描述

注意,这里需要说明:

  • Thread类本身已经实现了Runnable接口;
  • 上图B方法中MyThread类首先实现了Runnable接口,再讲其实例对象传入Thread类的构造器中;

总的来说,开启新线程的方法就是要先构造Thread类或其子类的实例对象,该对象代表着新线程

3. 线程的生命周期

JAVA中每个线程都有自己的生命周期,也即线程当前所处的状态;
在这里插入图片描述

3.1常见线程状态:

New状态:新创建线程;
Runnable状态:可执行状态,在该状态下的线程等待被CPU轮询执行;
Running状态:正在使用CPU资源;
Waiting状态:等待状态;
Blocked状态:线程阻塞状态,当前线程没有获得钥匙,只能阻塞等待到有钥匙才能执行;
Terminate状态:线程处理结束,被销毁

3.2常用线程控制方法:

static currentThread(); 返回当前线程对象
static sleep(); 使当前线程休眠,注意该方法为静态方法,不能对实例使用
interrupt(); 设置中断标志位,但不像break那样启动中断;
static interrupted(); 判断当前线程是否中断,静态方法,对实例使用无效;
该方法具有清除状态功能,第一次调用会消除中断标志,因而下次调用显示false
isInterrupted(); 判断当前实例线程是否中断,对线程对象判断
static yield(); 静态方法,暂时放弃CPU资源,让其他线程使用
isAlive(); 判断当前线程是否为存活,正在执行或者正准备执行为存活状态
getName(); 获得线程名字;
getId();获得线程唯一标识;
setPriority(); 设置线程优先级,最高为10,最低为1,
getPriority(); 获取当前实例线程的优先级,优先级具有继承性,规则性,随机性

4. 线程优先级

线程具有优先级,最高为10,最低为1。
线程的优先级具有三个特性:

  • 继承性:于线程1中创建新线程2,线程2具有和线程1一样的优先级,但可重新设定优先级;
  • 规则性: 优先级高的线程可获得更多的CPU资源;
  • 随机性: 优先级高的并不一定先执行,而是表示有更多的概率获得CPU资源。

5. synchronized关键字

5.1锁的分类

  • 一般方法:无锁,任何线程都可通知执行;
  • 对象锁方法:使用对象作为锁的标志

格式:

synchronized(this){
			//被加锁的代码块,其中锁this可以被替换成任何对象
		}
public synchronized void method(){
		//被加锁的方法内容
}
  • 类锁方法:使用类作为锁的标志
synchronized(xxx.class){}
synchronized(Class.forName(“xxx”)){}
public static synchronized void method(){}

5.2对象锁和类锁的区别:

  • 对象锁应用在多个线程争用同一对象内的被synchronized修饰的非静态方法,会有同步效果;
  • 类锁应用在多个线程争用同一个类的被synchronized修饰的静态方法,会有同步效果;
  • 对象锁争用的是堆内的同一对象的方法,类锁是争用方法区的同一类的静态方法。
    在这里插入图片描述

5.3锁的使用规则:

  • 一类锁只有一把钥匙,先执行的线程获得当前类锁的钥匙,执行完交出钥匙,其余阻塞线程再争取;
  • 当前线程争取得钥匙后,可以继续开启其他被当前锁控制的方法,直到执行完毕交出钥匙;
  • 在对象锁中,多个线程争一个对象的某类锁的钥匙才会同步;
  • 多个线程争一个对象的不同类锁的钥匙不会同步;
  • 多个线程争多个对象的同一类锁的钥匙不会同步,
  • 在类锁中,分属多个对象的线程争取当前类的静态方法会同步
发布了17 篇原创文章 · 获赞 3 · 访问量 5442

猜你喜欢

转载自blog.csdn.net/weixin_40106401/article/details/104526086