目录

一.Object类、常用API

概述

1.object类

1.1toString方法

1.2equals方法

2.date类

2.1几个常用方法

2.1DeteFormat类(抽象类)

3.Calendar类(抽象类)

3.1设置日期

4.System类

4.1获取毫秒

4.2数组的复制

5. StringBuilder类

5.1StringBuilder的使用

6.包装类

6.1概述

6.2装箱与拆箱

6.3自动拆箱和自动装箱

6.4基本类型和字符串类型的转换

二.Collection、泛型

1.Collection集合

1.1概述

1.2集合框架

1.3Collection的常用功能

2.Iterator迭代器

2.1概念

2.2迭代器的方法

2.3迭代器的使用

3.增强for循环

3.1遍历数组

3.2遍历集合

4.泛型

4.1概述

4.2 使用泛型的好处

4.3 泛型的定义与使用

4.4定义含有泛型的方法

4.5含有泛型的接口

4.6泛型的通配符

三.List、Set、数据结构、Collections

1.数据结构(与集合相关)#了解

1.1数据结构的作用

1.2常见的数据结构

2.List集合

2.1概述和特点

2.2方法

2.3ArrayList类

2.4 LinkedList集合

3.Set类

3.1概述

3.2HashSet集合

3.3 HashSet集合存储数据的结构(哈希表)

3.4Set集合不允许储存重复元素的原理

3.5 HashSet存储自定义类型元素

4.LinkedHashSet类

4.1概述

4.2LinkedHashSet的有序性

5.可变参数

5.1概述及格式

​ 6.Collections工具类

6.1概述和部分方法

6.2使用

四.Map

1.Map集合

1.1概述

1.2Map集合常用实现类

1.3Map接口中的常用方法

1.4Map集合遍历的键找值方式

1.5Entry键值对对象

1.6HashMap存储自定义类型键值

1.7LinkedHashMap集合(继承HashiMap)

1.8Hashtable集合

2.补充

3.Debug追踪

五. 异常

1.异常

1.1 异常概念

1.2Throwable体系

1.3Throwable中的常用方法:

1.4异常的分类

2.异常的处理

2.1throw关键字分析

2.2Objects非空判断

2.3声明异常throws关键字

2.4异常捕获try-catch

2.5finally代码块

2.6注意事项

2.7自定义异常

六.线程、同步

1.线程 I

1.1并发和并行

1.2进程与线程

1.3线程调度

1.4主线程

1.5创建线程类

用Runnable的好处

1.6多线程的原理

1.7Thread类中的常用方法

2.同步

2.1线程安全问题

2.2解决线程安全问题

3.线程状态

3.1概述

3.2Tined Wiating(计时等待)(了解)

3.3BLOCED(锁阻塞)(了解)

3.4Waiting(无限等待)

3.5其他方法

七.线程池、Lambda表达式

1.等待唤醒机制

1.1线程间通信

1.2等待唤醒机制

1.3 生产者与消费者问题

2.线程池

2.1概述

2.2线程池的好处

2.3线程池的使用

3.Lambda表达式

3.1 函数式编程思想概述

3.2 冗余的Runnable代码

3.4 体验Lambda的更优写法

3.5Lambda表达式的标准格式

3.6Lanbda表达式的练习

3.7Lambda省略格式

3.8 Lambda的使用前提


一.Object类、常用API

概述

java.lang.Object类是Java语言中的根类,即所有类的父类。它中描述的所有方法子类都可以使用。在对象实例化的时候,最终找的父类就是Object。

如果一个类没有特别指定父类, 那么默认则继承自Object类。

1.object类

1.1toString方法

1.2equals方法

object中的equals方法(容忍null)

2.date类

2.1几个常用方法

public class Date {public static void main(String[] args) {System.out.println(System.currentTimeMillis());//获取当前日期到1970年1月1日00.00.00经历了多少毫秒demo();}//date类中常用的成员方法和构造方法public static void demo() {Date date1 = new Date();//打印出当前日期System.out.println(date1);Date date2=new Date(0L);//把毫秒值传递成日期System.out.println(date2);long date3.getTime();//相当于System.currentTimeMillis()方法System.out.println(date3);}
}

2.1DeteFormat类(抽象类)

 1)String format(Date date)

2)Date parse(String source)

 

3.Calendar类(抽象类)

3.1设置日期

1)获取日期

2)设置日期

3)增加/减少 日期

4.System类

4.1获取毫秒

4.2数组的复制

与我们说所的复制不同,其实是将数组对于索引位置覆盖在另一个数组上面。

5. StringBuilder类

java.lang.StringBuilder的API,StringBuilder又称为可变字符序列,它是一个类似于 String 的字符串缓冲区,通过某些方法调用可以改变该序列的长度和内容。

原来StringBuilder是个字符串的缓冲区,即它是一个容器,容器中可以装很多字符串。并且能够对其中的字符串进行各种操作。

5.1StringBuilder的使用

1)构造方法

2)成员方法

public StringBuilder append(....)添加任意类型数据的字符串形式,并返回当前对象自身。

有兴趣可以试试链式编程。

public StringBuilder toString()

6.包装类

6.1概述

Java提供了两个类型系统,基本类型与引用类型,使用基本类型在于效率,然而很多情况,会创建对象使用,因为对象可以做更多的功能,如果想要我们的基本类型像对象一样操作,就可以使用基本类型对应的包装类,如下:

6.2装箱与拆箱

在使用构造方法时会出现横线划掉Interger,说明方法过时了。

如果用Integer方法放置字符,则会报错:

6.3自动拆箱和自动装箱

由于我们经常要做基本类型与包装类之间的转换,从Java 5(JDK 1.5)开始,基本类型与包装类的装箱、拆箱动作可以自动完成。例如:

6.4基本类型和字符串类型的转换

1)基本数据-->字符串

这里只写最常用的方法:

其实就是令100变成“100”

 2)字符串-->基本数据类型

其他类型其实就是:

基本数据类型  用户标识符  =   对应包装类.parse+数据类型(原字符串对象名称) ,如

short  a = Short.parseByte  (基本数据类型首字母大写

二.Collection、泛型

1.Collection集合

1.1概述

  • 集合:集合是java中提供的一种容器,可以用来存储多个数据,集合与数组的差别:

  • 数组的长度是固定的。集合的长度是可变的。

  • 数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象。而且对象的类型可以不一致。在开发中一般当对象多的时候,使用集合进行存储。

1.2集合框架

集合按照其存储结构可以分为两大类,分别是单列集合java.util.Collection和双列集合java.util.Map,以下是Collection集合。

  • Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是java.util.Listjava.util.Set。其中,List的特点是元素有序、元素可重复。Set的特点是元素无序,而且不可重复。List接口的主要实现类有java.util.ArrayListjava.util.LinkedListSet接口的主要实现类有java.util.HashSetjava.util.TreeSet

1.3Collection的常用功能

共性方法:

创建对象

此处使用多态,如果后面是Hashset等其他类,也可以继续使用。

1)添加(返回类型boolean)

应该注意的是,此处的coll为字符串数组,即为“李四”,“张三”,“赵六”,“田七”四个字符串构成的字符串数组,若在后面删除李”四赵六”,则返回的是false。

2)删除(返回类型boolean)

3)判断是否包含元素(返回类型boolean)

4)判断集合是否为空(返回类型为boolean)

5)返回集合个数(int)

6)存储集合元素到数组

7)清空集合元素

8)获取索引位置对于的字符串

//public String get();String a=coll.get(0);System.out.println(a);

2.Iterator迭代器

2.1概念

  • 迭代:即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。

2.2迭代器的方法

2.3迭代器的使用

public class IteratorDemo {public static void main(String[] args) {// 使用多态方式 创建对象Collection<String> coll = new ArrayList<String>();// 添加元素到集合coll.add("串串星人");coll.add("吐槽星人");coll.add("汪星人");//遍历//使用迭代器 遍历   每个集合对象都有自己的迭代器Iterator<String> it = coll.iterator();//  泛型指的是 迭代出 元素的数据类型while(it.hasNext()){ //判断是否有迭代元素String s = it.next();//获取迭代出的元素System.out.println(s);}}
}

3.增强for循环

增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。

3.1遍历数组

public class NBForDemo1 {public static void main(String[] args) {int[] arr = {3,5,6,87};//使用增强for遍历数组for(int a : arr){//a代表数组中的每个元素System.out.println(a);}}
}

3.2遍历集合

public class NBFor {public static void main(String[] args) {        Collection<String> coll = new ArrayList<String>();coll.add("小河神");coll.add("老河神");coll.add("神婆");//使用增强for遍历for(String s :coll){//接收变量s代表 代表被遍历到的集合元素System.out.println(s);}}
}

4.泛型

4.1概述

在前面学习集合时,我们都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换。

我们来分析下:由于集合中什么类型的元素都可以存储。导致取出时强转引发运行时 报错ClassCastException。 怎么来解决这个问题呢?

Collection虽然可以存储各种对象,但实际上通常Collection只存储同一类型对象。例如都是存储字符串对象。因此在JDK5之后,新增了泛型(Generic)语法,让你在设计API时可以指定类或方法支持泛型,这样我们使用API的时候也变得更为简洁,并得到了编译时期的语法检查。

  • 泛型:可以在类或方法中预支地使用未知的类型。

tips:一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。

4.2 使用泛型的好处

  • 将运行时期的ClassCastException,转移到了编译时期变成了编译失败。

  • 避免了类型强转的麻烦。

4.3 泛型的定义与使用

eg:首先定义一个含有泛型的类

使用这个类

4.4定义含有泛型的方法

eg: 定义泛型方法

泛型方法的使用

4.5含有泛型的接口

使用格式 (实现类)

1)已经确定泛型的类型

2)始终不确定泛型的类型,直到创建对象时,确定泛型的类型

测试类

4.6泛型的通配符

当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。

通配符高级使用----受限泛型

之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限下限

泛型的上限

  • 格式类型名称 <? extends 类 > 对象名称

  • 意义只能接收该类型及其子类

泛型的下限

  • 格式类型名称 <? super 类 > 对象名称

  • 意义只能接收该类型及其父类型

比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类。

三.List、Set、数据结构、Collections

1.数据结构(与集合相关)#了解

1.1数据结构的作用

当你用着java里面的容器类很爽的时候,你有没有想过,怎么ArrayList就像一个无限扩充的数组,也好像链表之类的。好用吗?好用,这就是数据结构的用处,只不过你在不知不觉中使用了。

现实世界的存储,我们使用的工具和建模。每种数据结构有自己的优点和缺点,想想如果Google的数据用的是数组的存储,我们还能方便地查询到所需要的数据吗?而算法,在这么多的数据中如何做到最快的插入,查找,删除,也是在追求更快。

我们java是面向对象的语言,就好似自动档轿车,C语言好似手动档吉普。数据结构呢?是变速箱的工作原理。你完全可以不知道变速箱怎样工作,就把自动档的车子从 A点 开到 B点,而且未必就比懂得的人慢。写程序这件事,和开车一样,经验可以起到很大作用,但如果你不知道底层是怎么工作的,就永远只能开车,既不会修车,也不能造车。当然了,数据结构内容比较多,细细的学起来也是相对费功夫的,不可能达到一蹴而就。我们将常见的数据结构:堆栈、队列、数组、链表和红黑树 这几种给大家介绍一下,作为数据结构的入门,了解一下它们的特点即可。

1.2常见的数据结构

数据存储的常用结构有:栈、队列、数组、链表和红黑树。

1)栈

  • stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。

简单的说:采用该结构的集合,对元素的存取有如下的特点

  • 先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。

  • 栈的入口、出口的都是栈的顶端位置。

注意两个名词:

2)队列

  • 压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。

  • 弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。

  • 队列queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。

简单的说,采用该结构的集合,对元素的存取有如下的特点:

  • 先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,小火车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。

  • 队列的入口、出口各占一侧。例如,下图中的左侧为入口,右侧为出口。

 3)数组

  • 数组:Array,是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租房子的人。

简单的说,采用该结构的集合,对元素的存取有如下的特点:

  • 查找元素:通过索引,可以快速访问指定位置的元素

  • 增删元素

 4)链表

链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时i动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。我们常说的链表结构有单向链表与双向链表 。

特点 

  • 查找慢

  • 增删快

5)红黑树

二叉树binary tree ,是每个结点不超过2的有序树(tree

简单的理解,就是一种类似于我们生活中树的结构,只不过每个结点上都最多只能有两个子结点。

二叉树是每个节点最多有两个子树的树结构。顶上的叫根结点,两边被称作“左子树”和“右子树”。

红黑树本身就是一颗二叉查找树,将节点插入后,该树仍然是一颗二叉查找树。也就意味着,树的键值仍然是有序的。

2.List集合

2.1概述和特点

2.2方法

1)指定位置添加元素(返回值类型void)

2)指定位置删除元素(返回值类型E)

3)指定位置替换元素 (返回值类型E)

4)List的遍历

增强for遍历快捷方式:List.for回车

如获取索引位置大于数组最大索引,会报错。

2.3ArrayList类

java.util.ArrayList集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList是最常用的集合。

许多程序员开发时非常随意地使用ArrayList完成任何需求,并不严谨,这种用法是不提倡的。

当查询较多时,推荐使用ArrayList。

2.4 LinkedList集合

概述:

 方法的使用,先创建对象(了解)

1)添加元素

2)获取元素

 3)判断是否为空集

4)移除并返回元素

#Vector类,作为了解,已被淘汰。

3.Set类

3.1概述

java.util.Set接口和java.util.List接口一样,同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行功能上的扩充,只是比Collection接口更加严格了。与List接口不同的是,Set接口中元素无序,并且都会以某种规则保证存入的元素不出现重复 。

`Set`集合有多个子类,这里介绍其中的`java.util.HashSet`、`java.util.LinkedHashSet`两个集合。

3.2HashSet集合

java.util.HashSetSet接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致)。java.util.HashSet底层的实现其实是一个java.util.HashMap支持 。

此处说明了不能储存重复元素的特点。

3.3 HashSet集合存储数据的结构(哈希表)

哈希值:

是一个十进制的整数 由系统随机给出(是一个模拟出来的地址值,不是真的物理地址值)也是地址值的十进制   ( 地址值是十六进制)

方法的使用:

将p1,p2打印出来分别是他们哈希值对应的16进制。

哈希表

3.4Set集合不允许储存重复元素的原理

总结

3.5 HashSet存储自定义类型元素

eg

如下列所示

import java.util.Objects;public class Person {String name;Integer age;public Person() {}public Person(String name, Integer age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return Objects.equals(name, person.name) &&Objects.equals(age, person.age);}@Overridepublic int hashCode() {return Objects.hash(name, age);}

4.LinkedHashSet类

4.1概述

 HashSet的无序性

无论先添加“www”,还是“abc”,打印结果都不变,说明无序性。

4.2LinkedHashSet的有序性

打印结果与顺序相关。

5.可变参数

5.1概述及格式

数组在参数创建时同时创建。

eg

int i=add();//空参调用

5.2注意事项:

 6.Collections工具类

6.1概述和部分方法

6.2使用

1)添加多个元素

Comparable

2)排序(默认为升序)

3)自定义类型排序

Person类(自定义类型)

this-形式参数 升序

形式参数-this 降序

测试类

打印出结果

实现了升序排序。

Comparator

在使用Comparator进行排序是需要重写里面的方法

打印结果按照年龄升序排序。

总结:

Comparable接口,在自定义类(实现类)中重写compareTo方法

Comparator接口,在测试类中重写compare方法。

简述Comparable和Comparator两个接口的区别。

Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。只能在类中实现compareTo()一次,不能经常修改类的代码实现自己想要的排序。实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序,对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。

Comparator强行对某个对象进行整体排序。可以将Comparator 传递给sort方法(如Collections.sort或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。

四.Map

1.Map集合

1.1概述

现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象,即java.util.Map接口。

我们通过查看Map接口描述,发现Map接口下的集合与Collection接口下的集合,它们存储数据的形式不同,如下图

1.2Map集合常用实现类

HashMap

LinkedHashMap

1.3Map接口中的常用方法

1)存储

value允许重复。

2)删除

3)获取

4)查询集合是否包含key(返回值类型是Boolean

1.4Map集合遍历的键找值方式

1.5Entry键值对对象

我们已经知道,Map中存放的是两种对象,一种称为key(键),一种称为value(值),它们在在Map中是一一对应关系,这一对对象又称做Map中的一个Entry(项)Entry将键值对的对应关系封装成了对象。即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对(Entry)对象中获取对应的键与对应的值。

Map集合遍历键值对方式

1.6HashMap存储自定义类型键值

1)V为自定义类

2)K为自定义类

1.7LinkedHashMap集合(继承HashiMap)

与HashSet和LinkedHashMap一样,存在顺序关系,前者无序,后者有序。

1.8Hashtable集合

2.补充

此时的集合个数已经确定,再用add()方法添加的话,报错:不支持操作。

三种集合使用of()方法后都不允许再次添加元素。

 Set和Map不允许重复元素出现。报错:非法参数异常。

3.Debug追踪

五. 异常

1.异常

1.1 异常概念

异常,就是不正常的意思。在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响.在程序中的意思就是:

异常 :指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。

在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。

异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行.

异常机制其实是帮助我们找到程序中的问题,异常的根类是java.lang.Throwable,其下有两个子类:java.lang.Errorjava.lang.Exception,平常所说的异常指java.lang.Exception

1.2Throwable体系:

  • Error:严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。

  • Exception:表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。好比感冒、阑尾炎。

1.3Throwable中的常用方法:

  • public void printStackTrace():打印异常的详细信息。

    包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。

  • public String getMessage():获取发生异常的原因。

    提示给用户的时候,就提示错误原因

  • public String toString():获取异常的类型和异常描述信息(不用)。

1.4异常的分类

  • 编译时期异常:checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)

  • 运行时期异常:runtime异常。在运行时期,检查异常.在编译时期,运行异常不会编译器检测(不报错)。(如数学异常)

    1)编译期异常 

    第一种处理方式:给虚拟机处理,抛出异常(throws),此时这个程序可以正常运行。但如果把09-09改为0909,解析时异常,虚拟机会中断处理并打印出异常。

     2)运行期异常

    运行时索引超出,异常。

    第二种处理方式:try-catch,此时e为出现异常的内容。

    3)错误

    首先创建一个有异常的源代码

    执行结果

    解析

    ↓ 

    ↓ 

    2.异常的处理

    2.1throw关键字分析

    1) throw关键字后面创建的是RuntimeException或者是RuntimeException的子类对象,我们可以不处理,默认交给JVM处理(打印异常对象,中断程序)

    2)throw关键字后面创建的是编译异常(写代码时的报错),我们就必须处理这个异常,要么throws,要么try-catch

    测试

    定义一个方法

    调用

    执行结合会抛出空指针异常,属于运行期异常,我们可以不处理,默认交给JVM处理

    同理抛出指针越界异常,属于运行期异常

    2.2Objects非空判断

    2.3声明异常throws关键字

    测试

    在编译时会抛出异常,我们选择throws声明抛出, 由于文件路径对不上,执行会抛出异常。

    可以在方法体定义多个if语句来抛出异常:

此时我们必须继续使用throws声明抛出异常 ,由于FileNotFoundException是IOException的子类,我们可以选择只throws声明IOException即可。

2.4异常捕获try-catch

 注意:

测试: 

定义方法

调用 

与throws不同,在抛出异常后不会中断程序,后续代码(如上面代码在main方法中)依旧执行。

2.5finally代码块

finally必须和try-catch一起使用

2.6注意事项

1)使用一个try-catch对应一个异常

2)一个try对应多个catch,按顺序把对应异常写入catch

3)多个异常一次捕获一次处理

4)子父类方法异常 的重写

2.7自定义异常

六.线程、同步

1.线程 I

1.1并发和并行

图解:

并发(效率低)

并行(效率高)

1.2进程与线程

进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。

线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

图解:

进程:

线程( 先举例cpu,对电脑管家的运行进行分析):

1.3线程调度

可以通过任务管理器设置线程的优先级:

1.4主线程

首先定义一个Person类(这里就不把gettersatter等代码截出来了)

main方法执行

打印结果 (单线程的执行,如果中间出现异常,后续代码不再执行

.......

分析:

1.5创建线程类

1)创建多线程程序的第一种方式:创建Tread的子类

 java.long.Thread类,描述线程的类,我们要实现多线程,就必须继承Tread类

首先创建一个Thread的子类:

执行mian方法(注意里面也含有一个for循环(主线程)):

执行结果(两次执行结果不一样,具有随机性)

2)创建多线程程序的第二种方式:实现Runnable的接口

首先创建实现类

测试:

打印结果

用Runnable的好处

3) 匿名内部类创建线程

测试:

第一种:Thread类

第二种:Runnable接口

注意: 在main方法中,一个对象只能调用异常start()方法。

1.6多线程的原理

随机性

对前面的代码进行一个图解(图片较模糊可放大观看)

多线程内存图解

创建对象在堆内存空间实现

栈内存中:

1.7Thread类中的常用方法

1)获取线程名称

首先创建一个线程类,第一个方法

测试:

打印结果:

第二个方法(静态方法)和打印结果

2)设置线程名称

3)暂时停止执行(静态方法)

每隔一秒打印:1,2,3......

2.同步

2.1线程安全问题

概述:如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的

我们通过一个案例,演示线程的安全问题:电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是“战狼3”,本次电影的座位共100个(本场电影只能卖100张票)。

我们来模拟电影院的售票窗口,实现多个窗口同时卖“葫芦娃大战奥特曼”这场电影票(多个窗口一起卖这100张票)需要窗口,采用线程对象来模拟;需要票,Runnable接口子类来模拟。

1)图解:

图1

图2:

图三:

 2)代码实现

首先创建Runnable实现类

测试:

执行结果:

不同线程卖了同样的票,且出现不存在的票-1

这就产生了线程安全问题。

3)线程安全问题产生的原理

可以观看此视频理解:

链接:https://pan.baidu.com/s/164FXTK0E7ILmPFsuuCnnag 
提取码:tao2

2.2解决线程安全问题

1)解决方案一 :同步代码块

使用前面定义的实现类:

执行main后发现打印结果从第100张到第1张,达到了我们想要的结果。

同步技术的原理

可以观看此视频了解

链接:https://pan.baidu.com/s/1yFE_-psOaKDAndV4UjfPyA 
提取码:tao2

2)解决方案二 :同步方法

创建实现类 :

执行main后发现打印结果从第100张到第1张,达到了我们想要的结果。

3)静态同步方法 

与同步方法大同小异,在synchronized前加上static,使用静态变量。

两者的区别为锁对象:

4)解决方案三:Lock锁 

创建实现类:

 改进:

这样的好处是是否异常程序都有释放锁对象。

3.线程状态

3.1概述

3.2Tined Wiating(计时等待)(了解)

3.3BLOCED(锁阻塞)(了解)

3.4Waiting(无限等待)

等待与唤醒案例(线程之间的通信):

图解:

两个方法均为objects中的方法。

代码实现:

 

分别创建匿名内部类使用同步代码块定义“顾客”和“老板”:

顾客:

老板:

执行结果:

等待5秒后

3.5其他方法

七.线程池、Lambda表达式

1.等待唤醒机制

1.1线程间通信

概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。

比如:线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消费,那么线程A与线程B之间就存在线程通信问题。

为什么要处理线程间通信:

多个线程并发执行时, 在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据。

如何保证线程间通信有效利用资源:

多个线程在处理同一个资源,并且任务不同时,需要线程通信来帮助解决线程之间对同一个变量的使用或操作。 就是多个线程在操作同一份数据时, 避免对同一共享变量的争夺。也就是我们需要通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制。

1.2等待唤醒机制

什么是等待唤醒机制

这是多个线程间的一种协作机制。谈到线程我们经常想到的是线程间的竞争(race),比如去争夺锁,但这并不是故事的全部,线程间也会有协作机制。就好比在公司里你和你的同事们,你们可能存在在晋升时的竞争,但更多时候你们更多是一起合作以完成某些任务。

就是在一个线程进行了规定操作后,就进入等待状态(wait()), 等待其他线程执行完他们的指定代码过后 再将其唤醒(notify());在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。

wait/notify 就是线程间的一种协作机制。

等待唤醒中的方法

等待唤醒机制就是用于解决线程间通信的问题的,使用到的3个方法的含义如下:

  1. wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中

  2. notify:则选取所通知对象的 wait set 中的一个线程释放;例如,餐馆有空位置后,等候就餐最久的顾客最先入座。

  3. notifyAll:则释放所通知对象的 wait set 上的全部线程。

注意:

哪怕只通知了一个等待的线程,被通知线程也不能立即恢复执行,因为它当初中断的地方是在同步块内,而此刻它已经不持有锁,所以她需要再次尝试去获取锁(很可能面临其它线程的竞争),成功后才能在当初调用 wait 方法之后的地方恢复执行。

总结如下:

  • 如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE 状态;

  • 否则,从 wait set 出来,又进入 entry set,线程就从 WAITING 状态又变成 BLOCKED

调用wait和notify方法需要注意的细节

  1. wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。

  2. wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。

  3. wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法。

1.3 生产者与消费者问题

生产者:

消费者:

测试类:

 代码实现:

创建包子类:

创建包子铺类:

 public class BaoZiPu extends Thread {private BaoZi bz;public BaoZiPu(String name,BaoZi bz){super(name);this.bz = bz;}@Overridepublic void run() {int count = 0;//造包子while(true){//同步synchronized (bz){if(bz.flag == true){//包子资源  存在try {bz.wait();} catch (InterruptedException e) {e.printStackTrace();}}// 没有包子  造包子System.out.println("包子铺开始做包子");if(count%2 == 0){// 冰皮  五仁bz.pier = "冰皮";bz.xianer = "五仁";}else{// 薄皮  牛肉大葱bz.pier = "薄皮";bz.xianer = "牛肉大葱";}count++;bz.flag=true;System.out.println("包子造好了:"+bz.pier+bz.xianer);System.out.println("吃货来吃吧");//唤醒等待线程 (吃货)bz.notify();}}}}

吃货类:

 private BaoZi bz;public ChiHuo(String name,BaoZi bz){super(name);this.bz = bz;}@Overridepublic void run() {while(true){synchronized (bz){if(bz.flag == false){//没包子try {bz.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("吃货正在吃"+bz.pier+bz.xianer+"包子");bz.flag = false;bz.notify();}}}
}

测试类:

public class Demo {public static void main(String[] args) {//等待唤醒案例BaoZi bz = new BaoZi();ChiHuo ch = new ChiHuo("吃货",bz);BaoZiPu bzp = new BaoZiPu("包子铺",bz);ch.start();bzp.start();}
}

执行结果:

2.线程池

2.1概述

我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?

在Java中可以通过线程池来达到这样的效果。

线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。由于线程池中有很多操作都是与优化资源相关的,我们在这里就不多赘述。我们通过一张图来了解线程池的工作原理:

2.2线程池的好处

  1. 降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

  2. 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。

  3. 提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

2.3线程池的使用

Java里面线程池的顶级接口是java.util.concurrent.Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是java.util.concurrent.ExecutorService

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在java.util.concurrent.Executors线程工厂类里面提供了一些静态工厂,生成一些常用的线程池。官方建议使用Executors工程类来创建线程池对象。

代码实现:

 创建指定对象的线程池:

创建Runnable实现类:

 调用:

关闭:

3.Lambda表达式

3.1 函数式编程思想概述

在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做

面向对象的思想:做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情。

函数式编程思想:只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程。

3.2 冗余的Runnable代码

传统写法:

当需要启动一个线程去完成任务时,通常会通过java.lang.Runnable接口来定义任务内容,并使用java.lang.Thread类来启动该线程。代码如下:

public class Demo01Runnable {public static void main(String[] args) {// 匿名内部类Runnable task = new Runnable() {@Overridepublic void run() { // 覆盖重写抽象方法System.out.println("多线程任务执行!");}};new Thread(task).start(); // 启动线程}
}

本着“一切皆对象”的思想,这种做法是无可厚非的:首先创建一个Runnable接口的匿名内部类对象来指定任务内容,再将其交给一个线程来启动。

代码分析:

对于Runnable的匿名内部类用法,可以分析出几点内容:

  • Thread类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心;

  • 为了指定run的方法体,不得不需要Runnable接口的实现类;

  • 为了省去定义一个RunnableImpl实现类的麻烦,不得不使用匿名内部类;

  • 必须覆盖重写抽象run方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;

  • 而实际上,似乎只有方法体才是关键所在。

 生活举例:

当我们需要从北京到上海时,可以选择高铁、汽车、骑行或是徒步。我们的真正目的是到达上海,而如何才能到达上海的形式并不重要,所以我们一直在探索有没有比高铁更好的方式——搭乘飞机。

而现在这种飞机(甚至是飞船)已经诞生:2014年3月Oracle所发布的Java 8(JDK 1.8)中,加入了Lambda表达式的重量级新特性,为我们打开了新世界的大门。

3.4 体验Lambda的更优写法

借助Java 8的全新语法,上述Runnable接口的匿名内部类写法可以通过更简单的Lambda表达式达到等效:

这段代码和刚才的执行效果是完全一样的,可以在1.8或更高的编译级别下通过。从代码的语义中可以看出:我们启动了一个线程,而线程任务的内容以一种更加简洁的形式被指定。

3.5Lambda表达式的标准格式

3.6Lanbda表达式的练习

有兴趣可以观看一下博主的文章练习:

链接:https://blog.csdn.net/qq_37722734/article/details/82148490 3.7

3.7Lambda省略格式

 对于以上第3点:

3.8 Lambda的使用前提

Lambda的语法非常简洁,完全没有面向对象复杂的束缚。但是使用时有几个问题需要特别注意:

  1. 使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。 无论是JDK内置的RunnableComparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。

  2. 使用Lambda必须具有上下文推断。 也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

Java学习:从入门到精通week3相关推荐

  1. Java学习从入门到精通的学习建议

    想要学好java技术,首先打好基础很重要,不论学什么基础都是重中之重,学习Java更是如此.如:基础语法.核心类库.面向对象编程.异常.集合.IO流等基础如果学不好,那么后边更深入的语法也不容易学会. ...

  2. Java学习从入门到精通-旧版

    为什么80%的码农都做不了架构师?>>>    Java学习从入门到精通-旧版 http://tech.ccidnet.com/art/3737/20051017/465333_1. ...

  3. Java学习从入门到精通

    Java Learning Path (一).工具篇 一. JDK (Java Development Kit) JDK是整个Java的核心,包括了Java运行环境(Java Runtime Envi ...

  4. Java学习从入门到精通[转]

    Java Learning Path (一).工具篇   一. JDK (Java Development Kit) JDK是整个Java的核心,包括了Java运行环境(Java Runtime En ...

  5. 真正的Java学习从入门到精通

    一. 工具篇JDK (Java Development Kit) oF[l�bZk0   `WR4j-   JDK 是整个Java的核心,包括了Java运行环境(Java Runtime Envirn ...

  6. Java学习从入门到精通(1) [转载]

    Java Learning Path (一).工具篇 一. JDK (Java Development Kit) JDK是整个Java的核心,包括了Java运行环境(Java Runtime Envi ...

  7. [转,借鉴]Java学习从入门到精通-过程篇

    每个人的学习方法是不同的,一个人的方法不见得适合另一个人,我只能是谈自己的学习方法.因为我 学习Java是完全自学的,从来没有问过别人,所以学习的过程基本上完全是自己摸索出来的.我也不知道 这种方法是 ...

  8. Java视频教程从入门到精通(2023完整版)

    java视频教程从入门到精通(2023完整版),Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因此Java语言具有功能强大和简单易用两个 ...

  9. 《Java Web从入门到精通》PDF 百度网盘

    http://www.java1234.com/a/javabook/javaweb/2014/1219/3407.html <Java Web从入门到精通>PDF 下载 <Java ...

  10. Java如何快速入门?Java初学者从入门到精通必看!

            作为刚刚接触Java的小白来说,最担心的应该就是Java怎么学,都需要掌握哪些内容?今天这篇文章希望能帮助大家快速入门Java,少走弯路! 如何快速入门Java? 一.作为刚接触Jav ...

最新文章

  1. PCL学习笔记,区域生长分割(region growing segmentation)
  2. 超酷实用的jQuery焦点图赏析及源码
  3. python一切皆对象的理解_在 Python 中万物皆对象
  4. 牛客网题目——不用四则运算符号,计算两个数字的和
  5. arduino byte转string_Java数组转List集合的三驾马车
  6. 前端工程师有哪些面试技巧值得掌握?
  7. javaweb学习总结—jsp简单标签标签库开发
  8. Netbeans 安装和配置 C/C++ 支持
  9. Windows C++ 获取当前文件夹下有几个文件
  10. android音频框架书籍,Android高级架构师系统学习——Android 音频可视化
  11. excel如何比对两列数据是否相同
  12. windows无法访问 计算机打印机,windows 7 无法连接到打印机 (错误0x0000000d)的解决方法...
  13. HugePages 大内存页
  14. 这些专业考上研以后再考公务员,非常吃香!
  15. 怎样进行结构化思维思考?
  16. hsv白色h值是多少_rgb颜色模型与hsv颜色模型的理解
  17. java不支持bks,java不支持bks
  18. 微信wifi认证的实现方式和功能——时讯wifi认证
  19. TSC Deluxe 300 pro 打印机驱动
  20. 爱快路由安装mysql_ESXi安装爱快iKuai OS路由(图文教程)

热门文章

  1. uniapp封装request请求简洁明了(使用Promise封装)
  2. PAT初级1031(C++)查验身份证
  3. 【Android 逆向】ART 脱壳 ( dex2oat 脱壳 | /art/dex2oat/dex2oat.cc#Dex2oat 函数源码 )
  4. photoshopCC 2018入门学习
  5. Linux系统备份工具 REAR (RELAX-AND-RECOVER)
  6. 链队列——-链式存储实现队列的入队出队(带next,front,rear指针)
  7. ESXI 7.0 版本配置N卡显卡直通
  8. 0922CSP-S模拟测试赛后总结
  9. python降序排序_python中如何降序排列
  10. pygame设计舒尔特方格游戏python舒尔特方格小程序