2021-08-03笔记

3.Map集合

返回值方法描述
Vput(K key, V value)将⼀个键值对插⼊到集合中。 在 Map集合中,不允许出现重复的键。 如果添加的键重复了,会⽤新的值覆 盖掉原来的值。并返回被覆盖的原来 的值。
VputIfAbsent(K key, V value)将⼀个键值对插⼊到集合中。 向集 合中添加元素的时候,如果这个键已 经存在了,则不进⾏添加。 返回集 合中已经存在的这个键对应的值。
voidputAll(Map map将⼀个Map集合中所有的键值对添加 到当前集合中。
Vremove(Object key)通过键,删除⼀个键值对,并返回这 个被删除的键值对中的值。 如果这 个键不存在,则返回null。
booleanremove(Object key, Object value)通过键值对进⾏删除。 只有当键和 值⼀⼀匹配的时候, 才会进⾏删 除。
voidclear()清空集合。
Vreplace(K key, V value)修改指定的键对应的值, 并返回被 覆盖的值。
booleanreplace(K key, V oldValue, V newValue)只有当key和oldValue是匹配的情况 下,才会将值修改成newValue。
voidreplaceAll(BiFunction biFunction)对集合中的元素进⾏批量的替换 将集合中的每⼀个键值对,带⼊到 BiFunction的⽅法中, 使⽤接⼝⽅法 的返回值替换集合中原来的值。
Vget(K key)通过键, 获取值。 如果键不存在, 返回null。
VgetOrDefault(K key)通过键, 获取值。 如果键不存在, 返回默认的值。
intsize()获取集合中的元素数量(有多少个键 值对)
booleanisEmpty()判断集合是否为空
booleancontainsKey(K key)判断是否包含指定的值
booleancontainsValue(V value)判断是否包含指定的值
SetkeySet()获取由所有的键组成的集合(因为键是不允许重复的,因此这⾥返回的 是Set集合)
Collectionvalues(获取由所有的值组成的集合

4. HashMap

4.1 HashMap 基本实现

注意:HashMap可以实现排序:因为他的底层数据结构是由数组+链表+⼆叉树共同实现的.所以可以排序.同时这样做的⽬的是提⾼数据存储的效率.

4.2 HashMap 与 Hashtable 的区别

  1. HashMap是线程不安全的集合, Hashtable是线程安全的集合。

  2. HashMap允许出现null键值, Hashtable是不允许的。

  3. HashMap的⽗类是AbstractMap, Hashtable的⽗类是Dictionary。

  4. HashMap的Map接⼝的新的实现类, 底层算法效率优于Hashtable。

5. TreeMap

5.1原理实现

与TreeSet⼀样,进⾏排列,只是TreeMap是按照键的⼤⼩实现,对于值是不管的.我们可 以将TreeSet中的值理解成TreeMap中的键.

5.2注意点

  1. 什么类型的数据类型可以作为key?

  • 实现了Comparable接⼝的compareTo()⽅法

  • 实现了Comparator接⼝的compare()⽅法

  1. 经常作为key的有:

    String,包装类,⾃定义的实现了要求的类

  2. 不可以的代表:数组,ArrayList,LinkedList(如果给他们建⽴的⽐较器也可以⽐较,但是不建议使⽤)

  3. 元素可不可以作为key,跟元素内部的成员有没有关系

元素可不可以作为key,跟元素内部的成员有没有关系

6.其他的实现类

  • LinkedHashMap

    • 与HashMap类似的,底层多维护了⼀个链表, 记录每⼀个键的存储顺序。 也就是说, 在LinkedHashMap中, 键值对的添加顺序可以得到保障。 类似于LinkedHashSet与HashSet。

7.线程

7.1为什么要使用线程?

在程序中完成某⼀个功能的时候,我们会将他描述成任务,这个任务需要在线程中完成.

7.2串行和并发

如果在程序中,有多个任务需要被处理,此时的处理⽅式可以有串⾏并发

  • 串⾏(同步):所有的任务,按照⼀定的顺序,依次执⾏。如果前⾯的任务没有 执⾏结束,后⾯的任务等待。

  • 并发(异步):将多个任务同时执⾏,在⼀个时间段内,同时处理多个任务。

7.3并发的原理

所谓的并发, 并不是真正意义上的多个任务同时执⾏。 ⽽是CPU快速的在不同 的任务之间进⾏切换。 在某⼀个时间点处理任务A, 下⼀个时间点去处理任务B, 每 ⼀个任务都没有⽴即处理结束。 CPU快速的在不同的任务之间进⾏切换, 只是这个 切换的速度⾮常快, ⼈类是识别不了的, 因此会给⼈⼀种“多个任务在同时执⾏”的 假象。 因此, 所谓的并发, 其实就是CPU快速的在不同的任务之间进⾏切换的⼀种假象。

7.4进程和线程

  • 进程,是对⼀个程序在运⾏过程中,占⽤的各种资源的描述。

  • 线程,是进程中的⼀个最⼩的执⾏单元。 其实,在操作系统中, 最⼩的任务执 ⾏单元并不是线程,⽽是句柄。只不过句柄过⼩,操作起来⾮常的麻烦,因此线程就是我们可控的最⼩的任务执⾏单元。

每⼀个进程⾄少要处理⼀件任务, 因此, 每⼀个进程中⾄少要包含⼀个线程。 如果 ⼀个进程中所有的线程都结束了, 那么这个进程也就结束了。

多个线程的同时执⾏, 是需要这些线程去争抢CPU资源, ⽽CPU资源的分配是以时 间⽚为单位的。 即某⼀个线程抢到了0.01秒的CPU时间⽚, 在这个时间内, CPU处 理这个线程的任务。 ⾄于哪⼀个线程能够抢到CPU时间⽚, 则由操作系统进⾏资源 调度。

7.5进程和线程的异同

相同点: 进程和线程都是为了处理多个任务并发⽽存在的。

不同点: 进程之间是资源不共享的, ⼀个进程中不能访问另外⼀个进程中的数据。 ⽽线程之间是资源共享的, 多个线程可以共享同⼀个数据。 也正因为线程之间是资源 共享的, 所以会出现临界资源的问题。

7.6进程和线程的关系

⼀个进程, 在开辟的时候, 会⾃动的创建⼀个线程, 来处理这个进程中的任务。 这 个线程被称为是主线程。 在程序运⾏的过程中, 还可以开辟其他线程, 这些被开辟 出来的其他线程, 都是⼦线程。

也就是说, ⼀个进程中, 是可以包含多个线程。 ⼀个进程中的某⼀个线程崩溃了, 只要还有其他线程存在, 就不会影响整个进程的执⾏。 但是如果⼀个进程中, 所有 的线程都执⾏结束了, 那么这个进程也就终⽌了。

7.7总结

  • 程序:⼀个可执⾏的⽂件

  • 进程:⼀个正在运⾏的程序.也可以理解成在内存中开辟了⼀块空间

  • 线程:负责程序的运⾏,可以看做⼀条执⾏的通道或执⾏单元,所以我们通常将进程 的⼯作理解成线程的⼯作 进程中可不可以没有线程? 必须有线程,⾄少有⼀个.

  • 当有⼀个线程的时候我们称为单线程(唯⼀的线程就是主线程). 当有⼀个以上的线程同时存在的时候我们称为多线程

  • 多线程的作⽤:为了实现同⼀时间⼲多件事情(并发执⾏)

8.线程的生命周期

8.1线程的状态

线程的⽣命周期, 指的是⼀个线程对象, 从最开始的创建, 到最后的销毁, 中间所 经历的过程。 在这个过程中, 线程对象处于不同的状态。

  • New: 新⽣态,⼀个线程对象刚被实例化完成的时候,就处于这个状态。

  • Runnable: 就绪态,处于这个状态的线程,可以参与CPU时间⽚的争抢。

  • Run: 运⾏态, 某⼀个线程抢到了CPU时间⽚,可以执⾏这个线程中的逻辑 Block: 阻塞态,线程由于种种原因,暂时挂起, 处于阻塞(暂停)状态。这个 状态的线程,不参与CPU时间⽚的争抢。

  • Dead: 死亡态, 线程即将被销毁。

8.2线程的生命周期

 

9.理解多线程

9.1.对线程并发执行的说明

简单理解(cpu单核):从宏观上看,线程有并发执⾏,从微观上看,并没有,在线程完成任务时,实际⼯作的是cpu,我们将cpu⼯作描述为时间⽚(单次获取cpu的时间,⼀般在⼏⼗毫秒).cpu只有⼀个,本质上同⼀时间只能做⼀件事,因为cpu单次时间⽚很短,短到⾁眼⽆法区分,所以当cpu在多个线程之间快速切换时,宏观上给我们的感觉是多件事同时在执⾏.

注意:

1.cpu是随机的,线程之间本质上默认是抢cpu的状态,谁抢到了谁就获得了时间⽚,就⼯ 作,所以多个线程的⼯作也是默认随机的.

2.在使⽤多线程时,并不是线程数越多越好,本质上⼤家共同使⽤⼀个cpu,完成任务的时间并没有减少.要根据实际情况创建线程,多线程是为了实现同⼀时间完成多件事情 的⽬的.⽐如我们⽤⼿机打开⼀个app时,需要滑动界⾯浏览,同时界⾯的图⽚需要下载, 对于这两个功能最好同时进⾏,这时可以使⽤多线程.

9.2多线程的实例演示

  • 代码演示的是主线程和垃圾回收线程在同时⼯作时的状态

  • 什么叫任务区?

我们将线程⼯作的地⽅称为任务区.

每⼀个线程都有⼀个任务区,任务区通过对应的⽅法产⽣作⽤

  • JVM默认是多线程吗?

⾄少要有两个线程:

主线程:任务区:main函数

垃圾回收线程:任务区:finalize函数

1.gc()⽅法:之前讲过,是垃圾回收器

原理:当执⾏gc时,会触发垃圾回收机制,开启垃圾回收线程,执⾏finalize⽅法

2.finalize()⽅法:垃圾回收线程的任务区

正常情况下,这个函数是由系统调⽤的,重写只是为了更好的观察多线程的发⽣,当Test对象被释放的时候,会⾃动的调⽤finalize⽅法

3.线程和任务的关系

任务区结束,线程随着任务的结束⽽结束,线程随着任务的开始⽽开始.当线程还在⼯作的时候,进程不能结束.

对于主线程来说:当main函数结束时,主任务区结束,主线程结束

对于垃圾回收线程:当finalize函数结束,垃圾回收任务结束,垃圾回收线程结束

cpu的特性是多个线程之间是抢cpu的关系,cpu有随机性

10.创建线程

10.1原因分析

默认情况下,主线程和垃圾回收线程都是由系统创建的,但是我们需要完成⾃⼰的功能, 所以需要创建⾃⼰的线程

java将线程⾯向对象了,形成的类就是Thread,在Thread类内部执⾏任务的⽅法叫run()

10.2线程对象的实例化

在Java中,使⽤Thread类来描述⼀个线程。实例化⼀个线程,其实就是⼀个 Thread对象。

10.3直接使用Thread类创建线程对象

  • 线程对象刚刚被实例化的时候, 线程处于新⽣态,还没有线程的功能。 如果需要让这个线程执⾏他的任务, 需要调⽤ start() ⽅法, 使线程进⼊到就绪态, 争抢 CPU时间⽚。

  • 为什么通过调⽤start()⽅法开启线程,⽽不是通过⼿动调⽤run()?

    答:因为线程获取cpu是随机的,run是线程的任务区,代表功能.如果⼿动执⾏run,此时线程可能没有拿到cpu,⽆法⼯作,操作失败.通过start,让线程处于就绪状态,随时拥有抢cpu的能⼒,当抢到cpu后,再⾃动执⾏run,实现并发的任务.

  • 为什么要使⽤Thread类的⼦类对象?

答:我们实现的实际功能,Thread类作为系统类不能提前知晓,所以⽆法将功能代码放⼊ Thread的run⽅法⾥.如果想实现⾃⼰的功能,可以写Thread类的⼦类,重写run⽅法,这也是为什么Thread的run⽅法是⼀个空⽅法。

10.4继承Thread类

  • 继承⾃Thread类,做⼀个Thread的⼦类。 在⼦类中,重写⽗类中的run⽅法,在这个重写的⽅法中,指定这个线程需要处理的任务。

  • Thread.currentThread() : 可以⽤在任意的位置,获取当前的线程。

    如果是Thread的⼦类, 可以在⼦类中, 使⽤this获取到当前的线程。

  • 当我们⼿动调⽤run的时候,他失去了任务区的功能,变成了⼀个普通的⽅法. 当run作为⼀个普通⽅法时,内部对应的线程跟调⽤他的位置保持⼀致.

  • 结果分析:

主线程和两个⼦线程之间是随机打印的,他们是抢cpu的关系.

  • 通过创建Thread⼦类的⽅式实现功能,线程与任务绑定在了⼀起,操作不方便,我们可以将任务从线程中分离出来,哪个线程需要⼯作,就将任务交给谁,操作⽅便,灵 活使⽤Runnable接⼝

10.5使用Runable接口

  • 在Thread类的构造⽅法中, 有⼀个重载的构造⽅法, 参数是 Runnable 接⼝。 因此, 可以通过Runnable接⼝的实现类对象进⾏Thread对象的实例化。

  • 这⾥Thread内部默认有⼀个run,⼜通过runnable传⼊⼀个run,为什么优先调⽤ 的是传⼊的run?

    如果该线程是使⽤独⽴的 Runnable 运⾏对象构造的,则调⽤该 Runnable 对象 的 run ⽅法;否则,该⽅法不执⾏任何操作并返回。

10.6优缺点对比

  • 继承的⽅式:优点在于可读性⽐较强, 缺点在于不够灵活。 如果要定制⼀个线 程,就必须要继承⾃Thread类,可能会影响原有的继承体系。

  • 接⼝的⽅式: 优点在于灵活, 并且不会影响⼀个类的继承体系。 缺点在于可读性较差。

11.线程同步

11.1临界资源问题

11.1.1临界资源

在⼀个进程中, 多个线程之间是可以资源共享的。如果在⼀个进程中的⼀个资源同时被多个线程访问,这个资源就是⼀个临界资源。

如果多个线程同时访问临界资源,会对这个资源的值造成影响。

11.1.2临界资源问题

多个线程同时访问⼀个资源的情况下,⼀个线程在操作这个资源的时候,将值取出进⾏运算, 在还没来得及进⾏修改这块空间的值之前,值⼜被其他的线程取⾛了。此时就会出现临界资源的问题,造成这个资源的值出现不是我们预期的值。

11.1.3解决方案

临界资源问题出现的原因就是多个线程在同时访问⼀个资源,因此解决⽅案也很简 单,就是不让多个线程同时访问即可。

在⼀个线程操作⼀个资源的时候,对这个资源进⾏“上锁”,被锁住的资源,其他的线程⽆法访问。

11.2线程安全问题

  • 线程安全问题: 4个线程共⽤了⼀个数据,出现了-1,-2,-3等错误的数据

  • 原因分析

出现了临界资源问题
1.共⽤了⼀个数据
2.共享语句有多条,⼀个线程使⽤cpu,没有使⽤完,cpu被抢⾛,当再次抢到cpu的时候,
直接执⾏后⾯的语句,造成了错误的发⽣.
  • 解决:在代码中使⽤同步代码块或同步⽅法(同步锁)

  • 解释:在某⼀段任务中,同⼀时间只允许⼀个线程执⾏任务,其他的线程即使抢到了 cpu,也⽆法进⼊当前的任务区间,只有当当前的线程将任务执⾏完后,其他的线程才能有资格进⼊

12.线程锁

  • 线程锁,就是⽤来“锁住”⼀个临界资源,其他的线程⽆法访问。在程序中,可以分为对象锁类锁

  • 对作为锁的对象的要求: 1.必须是对象

    2.必须保证被多个线程共享

  • 对象锁: 任何的普通对象或者this,都可以被当做是⼀把锁来使⽤。但是需要 注意,必须要保证不同的线程看到的锁, 需要是同⼀把锁才能⽣效。如果不同 的线程看到的锁对象是不⼀样的,此时这把锁将没有任何意义。注意: 不能直接使⽤匿名对象作为锁,因为这样每次都是在重新new,要保证锁是被⼤家共享.

  • 类锁: 可以将⼀个类做成锁, 使⽤类.class (类的字节码⽂件对象)来作为锁。因为类的字节码⽂件的使⽤范围太⼤,所以⼀般我们不使⽤他作为锁,只有在静态⽅法中。

13.synchronized

如果在⼀个⽅法中, 所有的逻辑, 都需要放到同⼀个同步代码段中执⾏。 这样的⽅ 法, 可以直接做成同步⽅法

13.1同步方法

  • ⾮静态同步⽅法,使⽤的对象锁(this)

    是某个对象实例内,synchronized aMethod(){}可以防⽌多个线程同时访问这个 对象的synchronized⽅法(如果⼀个对象有多个synchronized⽅法,只要⼀个 线程访问了其中的⼀个synchronized⽅法,其它线程不能同时访问这个对象中任 何⼀个synchronized⽅法)。这时,不同的对象实例的synchronized⽅法是不 相⼲扰的。也就是说,其它线程照样可以同时访问相同类的另⼀个对象实例中的 synchronized⽅法

  • 静态同步⽅法,使⽤的类锁(当前类的.class⽂件)

    是某个类的范围,synchronized static aStaticMethod{}防⽌多个线程同时访问 这个类中的synchronized static ⽅法。它可以对类的所有对象实例起作⽤。 静态同步函数在进内存的时候不会创建对象,但是存在其所属类的字节码⽂件对 象,属于class类型的对象,所以静态同步函数的锁是其所属类的字节码⽂件对象

13.2同步代码块

synchronized关键字⽤于⽅法中的某个区块中,表示只对这个区块的资源实⾏互斥访问。

同步代码段,是来解决临界资源问题最常⻅的⽅式。将⼀段代码放⼊到同步代码段中,将这段代码上锁。

第⼀个线程抢到了锁标记后,可以对这个紧接资源上锁,操作这个临界资源。此时 其他的线程再执⾏到synchronized的时候,会进⼊到锁池,直到持有锁的线程使⽤ 结束后,对这个资源进⾏解锁。此时,处于锁池中的线程都可以抢这个锁标记,哪⼀个线程抢到了,就进⼊到就绪态,没有抢到锁的线程,依然处于锁池中。

  • 同步代码块⼉的构成:

synchronized(锁(对象)){
    同步的代码
}
  • 同步代码块⼉的特点:1.可以保证线程的安全 2.由于每次都要进⾏判断处理,所以降低了执⾏效率

13.3 比较同步代码块和同步函数

  • 同步代码块使⽤更加的灵活,只给需要同步的部分代码同步即可,⽽同步函数是给这个函数内的所有代码同步.

  • 由于处于同步的代码越少越好,所以最好使⽤同步代码块

  • 什么时候使⽤同步代码块或者同步⽅法

1.多个线程共享⼀个数据
2.⾄少有两个线程

13.4 synchronized在继承中的使用

synchronized关键字是不能继承的,也就是说,基类的⽅法synchronized f(){}在继 承类中并不⾃动是synchronized f(){},⽽是变成了f(){}。继承类需要你显式的指定它的某个⽅法为synchronized⽅法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值