目录
- 面向对象
-
- 面向过程和面向对象
- 构造器(构造方法)
- this关键字
-
- this的用法
- static关键字
- 局部变量、成员变量、静态变量
- 包机制
-
- java常见包
- 内部类
- 面向对象三大特性
-
- 继承
-
- instanceof运算符
- 重写override
- final
- 组合
- Object
-
- toString
- ==和equals
- super
- 继承树
- 封装
-
- 封装的实现——访问控制符
- 开发中封装的简单规则——javabean
- 多态
-
- 对象转型
- 抽象方法和抽象类
- 接口
-
- JDK8以前
- JDK8以后
- 多继承
- JVM
-
- JVM内存分析
- 垃圾回收机制garbage collection
-
- 垃圾回收算法
-
- 引用计数法
- 引用可达法(根搜索算法)
- 分代垃圾回收机制
- JVM调优和full GC
-
- 导致full GC的原因
- 容易造成内存泄露的操作
- 字符串三兄弟
-
- String
-
- 创建
- 方法
- StringBuilder
- StringBuffer
- 使用陷阱
- 数组
-
- 增强for循环
- 拷贝
- Arrays
- 多维数组
- 存储表格数据
-
- Object数组
- javabean和数组
- comparable接口
- 包装类
-
- 自动装箱和拆箱
- 包装类的缓存问题
- 常用类
-
- Date类
-
- java.util.Date
- DataFormat和java.text.SimpleDateFormat
- Calendar类
- Math类
- Random类
- java.io.File类
-
- 递归打印目录树
- enum
- 泛型
-
- 使用
-
- 泛型类
- 泛型接口
- 泛型方法
-
- 非静态
- 静态方法
- 可变参数
- 通配符
-
- 无界通配符
-
- 上限限定
- 下限限定
- 容器
-
- 单例集合——Collection接口
-
- List接口
-
- ArrayList类
-
- 增删改查清
-
- 转换为数组
- 并集、交集、差集
- 源码分析
- Vector类
-
- 源码分析
- Stack容器
-
- Stack匹配括号合法性
- LinkedList类
-
- 源码分析
- Set接口
-
- HashSet类
-
- 存储特征
- HashSet存储自定义对象
- 源码分析
- TreeSet类
-
- 源码分析
- 双例集合——Map接口
-
- HashMap类
-
- 源码分析
- TreeMap类
- Iterator迭代器
- Collections工具类
- 注解 java.annotaion和反射 java.Reflection
-
- 内置注解
- 元注解
- 自定义注解
- 静态语言 VS. 动态语言
- 反射
-
- 获取Class的方法
- 类加载和内存分析
-
- 类的加载过程
- 类初始化
-
- 什么时候会初始化类
- 什么时候不会初始化类
- 类加载器
- 获取类的运行时结构
- 动态创建对象
- 反射操作泛型
- 反射操作注解
- 多线程 java.Thread
-
- 概念
- 线程创建
-
- 继承Thread类
- 实现Runnable接口(推荐使用)
- 实现Callable接口
- 静态代理
- Lamda表达式
- 线程状态
-
- 五大状态
- 线程方法
-
- 线程停止
- 线程休眠 sleep()
- 线程礼让yield()
- 线程强制执行join()
- 观测线程状态getState()
- 线程优先级
- 守护线程
- 线程同步
-
- 锁机制(synchronized)
- 三大线程不安全案例
- 同步方法
- 同步块
- 死锁
-
- 必要条件
- Lock接口
-
- 与synchronized的区别
- 线程协作
-
- 生产者消费者问题
- 解决方式
-
- 管程法
- 信号灯法
- 线程池
面向对象
面向过程和面向对象
面向过程:oriented-procedure,执行者思维,关注问题的实现步骤(怎么实现),适合简单、不需要协作、关注如何执行的事务
面向对象:oriented-object,设计者思维,从整体上分析系统,但实现部分依然依靠面向过程处理,适合解决复杂、需要协作的问题
面向对象离不开面向过程
构造器(构造方法)
- 构造器通过new关键字调用,调用的时候用来初始化值,此时对象已经创建好
- 构造器有返回值,但不能定义返回类型,不能在构造器里用return
- 编译器会自动定义一个无参构造器,如果自定义了一个,编译器不会自动添加
- 构造器方法名称必须和类名一致
this关键字
this的本质是创建好的对象的地址
创建对象的步骤:
- 分配对象空间,成员变量初始化为0或空
- 执行构造方法,属性值显式初始化
- 返回对象的地址
this的用法
- 避免二义性,用this指明当前对象;普通方法中this指向调用该方法的对象;构造方法中this指向正要初始化的对象
- 调用重载的构造方法,避免相同的初始化代码,只能在构造方法中用并且必须位于第一句
- this不能用于static方法中,因为static方法不从属对象,this指代的是当前对象
尚学堂java基础教程
static关键字
静态属性和静态方法属于类,非静态属性属于对象;
静态方法中不能调用非静态方法;
静态初始化块用于类的初始化,在静态初始化块中不能直接访问非静态成员
局部变量、成员变量、静态变量
包机制
通过package解决类同名和类管理问题,package通常是类的第一句非注释语句;包名由域名倒着写加上模块名
注意:com.lqr和com.lqr.test两个包没有包含关系
java常见包
- java.lang:包含核心类,String/Math/Integer/System/Thread,不需要导入可以直接使用
- java.awt:包含抽象窗口工具集的多个类,用来构建和管理GUI
- java.net
- java.io
- java.util:包含实用工具类,定义系统特性、日期
静态导入:导入指定类的静态属性,可以直接使用静态属性
内部类
讯享网
- 非静态内部类:必须寄存在外部类对象里,可以直接访问外部类成员,不能有静态属性和静态方法
- 静态内部类:看作外部类的静态成员,只能访问外部类的静态成员
- 匿名内部类:适合只需要使用一次的类,比如键盘监听操作,在安卓开发、swing开发常见
- 局部内部类:定义在方法内
面向对象三大特性
继承
作用:代码复用和类的扩展;对事务建模
注意:
- java只有单继承,c++有多继承
- java的类没有多继承,但接口有多继承
- 子类继承父类的全部属性和除构造器外的所有方法,但不能直接访问父类的私有属性和私有方法
- 定义类如果没有显式extends,则父类为java.lang.Object
instanceof运算符
对象 instanceof 类,判断对象是否是类或子类所创建的
重写override
- ==:方法名、形参列表相同
- <=:返回类型和声明异常类型,子类<=父类
-
=:方法权限修饰符(public/protected/private),子类>=父类
final
作用:
- 修饰变量不可改变
- 修饰方法不可重写(但可以重载)
- 修饰类不能被继承,例如Math/String
组合
实现代码复用的方法:继承和组合,组合更加灵活
组合:将父类对象作为子类属性,子类通过调用属性来获得父类属性和方法
Object
toString
IDEA快捷键: 类的结构视图(alt+7)
System.out.println(对象)就是打印一个对象的类别+地址
重写toString()方法可以在类里面自定义打印的内容
讯享网
==和equals
Object的equals方法是用==判断两个对象地址是否一样(是不是同一个对象);
重写equals方法,用来判断两个对象是不是逻辑上相等:右键constructer生成
super
继承树
绘制类的继承树:类文件右键->show Diagram->右键->show implements
封装
封装的实现——访问控制符
同一个包内,子类可以访问父类和父类对象的protected
不同包内,子类可以通过super访问父类的protected,但不能访问父类对象的protected
开发中封装的简单规则——javabean
- 属性一般使用private,并提供public get/set方法访问(boolean类型的get方法是is开头)(IDEA中直接输入set/get可以快捷生成)
- 方法只用于本类的用private修饰,希望其他类调用的用public
多态
- 多态是方法的多态,与属性无关
- 多态存在的必要条件:继承、重写、父类引用指向子对象并调用子类重写的方法
对象转型


抽象方法和抽象类
- 有抽象方法的类必须是抽象类
- 抽象类不能实例化(不能用new)
- 抽象类可以包含属性、方法、构造方法,但构造方法只能被子类调用
- 抽象类只能用来被继承
- 非抽象子类必须实现抽象方法
接口
JDK8以前
接口的方法都是public abstract,属性都是public static final
接口可以多继承
JDK8以后
允许在接口内定义默认方法(扩展方法)和静态方法
default关键字声明默认方法,可以有具体实现,类通过实现接口继承和重写默认方法,通过对象调用
接口中的静态方法从属于接口(接口也是特殊的类),通过接口名调用
若子类中定义了同名的静态方法,则该方法从属于子类,通过子类名调用(不叫重写)
多继承
A接口继承B接口和C接口(extends),D类实现A接口时(implements)需要实现ABC接口的所有方法
JVM
JVM内存分析
不含参数的函数有一个隐式参数为this,在调用时会在虚拟机栈中新建一个栈帧,存储所需的变量
若栈中变量指向对象,对象存储在堆中
若对象的属性不是基本类型,则指向方法区中属于这个类的区域中,方法体也存在这里
方法调用结束后,从栈中退出
创建对象时,调用的构造方法也会新开辟一块栈帧,创建结束后退出

方法区是一种规范,可以有不同实现,jdk7/jdk8之前和之后都是不同的
垃圾回收机制garbage collection
java的内存管理很大程度指的是堆中的对象管理
对象空间的分配:使用new关键字创建对象
对象空间的释放:赋值null,垃圾回收器负责回收所有不可达对象的内存空间
垃圾回收算法
引用计数法
堆中每个对象对应一个引用计数器,引用计数器的值为0时,垃圾回收器认为该对象是无用对象并进行回收
优点:算法简单
缺点:无法识别循环引用的无用对象

引用可达法(根搜索算法)
将所有引用关系看作一张图,从根节点开始遍历引用的节点,结束后没有被访问过的节点就是没有被引用的节点,即无用的节点
分代垃圾回收机制
将对象分为年轻代、年老代、永久代,并放到不同区域
JVM将堆内存划分为Eden、Survivor和Tenured/Old空间
- 年轻代:新生成的对象先放在Eden区,由minor GC回收;垃圾回收后依然存在的就将对象存放到survivor1区域,再满就存到survivor2区域,循环回收两个survivor区域
- 年老代:在年轻代中经历了15次垃圾回收后依然存活的对象;由major GC和full GC回收,全面清理年轻代和年老代区域
- 永久代:存放静态类或静态方法;JDK7以前永久代就是一种方法区的实现,JDK8以后没有永久代,使用metaspace元数据空间和堆代替

- minor GC:用于清理年轻代区域,Eden区满了就触发一次minor GC,清理无用对象,将有用对象复制到survivor1和survivor2
- major GC:清理年老代区域
- full GC:清理年轻代、年老代区域;成本高,对系统性能会有影响
JVM调优和full GC
导致full GC的原因
- 年老代tenured被写满
- 永久代perm被写满
- System.gc()被显式调用(只是通知JVM,申请启动full GC,并不是直接调用GC,尽量少用,成本高,影响性能);finaliza方法,释放对象或资源的方法,类似析构函数,尽量少用)
容易造成内存泄露的操作
- 创建大量无用对象
- 静态集合类的使用,例如HashMap/Vector/List
- 各种连接对象未关闭,例如IO流对象/数据集连接对象/网络连接对象
- 监听器,例如在释放对象时没有删除相应监听器
字符串三兄弟
String
不可变字符序列,位于java.lang包,java默认导入
java字符串是unicode字符序列
java没有内置字符串类型,String只是一个预定义的类,字符串是String的一个实例
创建
方法

String是不可变字符序列,所有替换、截取、去空格、转换大小写都是生成了新的字符串
StringBuilder
可变字符序列,效率高但线程不安全,最常用

StringBuffer
可变字符序列,效率低但线程安全
使用陷阱

需要拼接字符串时一定使用StringBuilder
数组
特点:
- 长度确定,一旦创建,无法改变大小
- 元素类型相同
- 可以是任意类型
- 数组变量属于引用类型,数组也是对象
初始化类型:默认初始化;静态初始化;动态初始化
数组的长度length是属性,String的长度length()是方法
增强for循环
拷贝
System类中的arraycopy(src, scr_start, targ, targ_start, length)方法
Arrays
打印:
排序:
赋值:
二分查找:
多维数组
存储表格数据
Object数组
javabean和数组
- 定义javabean,包括属性、get/set方法、构造器
- 构造对象
- 创建数组并初始化
comparable接口
包装类
包装类均位于java.lang包中

Number类是抽象类,抽象方法intValue~doubleValue由数字类实现,可以实现数字类型的互相转换
自动装箱和拆箱
JDK1.5以前,是错误的
JDK1.5以后,JVM执行了,将基本类型转化为包装类,称为自动装箱
,JVM执行了,称为自动拆箱
包装类的缓存问题
当数字在-128~127时,返回的是缓存数组中的某个元素,因此b1和b2指向的同一个对象
常用类
Date类

java.util.Date
要点:new Date() getTime()
DataFormat和java.text.SimpleDateFormat
实现时间对象和字符串的互相转化:DateFormat是抽象类,一般用SimpleDateFormat
- 规定时间格式 new SimpleDateFormat()
- 字符串转成Date:Date d = df.parse(str),需要抛出异常
- Date转成字符串:String s = df.format(new Date())
Calendar类
关于日期计算的相关功能
Calendar是抽象类,子类为GregorianCalendar
月份用0~11表示,Calendar中用常量JANUARY/FEBRUARY…表示
- new GregorianCalendar对象,指定年月日时分秒
- get()方法获得Calendar属性
- new GregorianCalendar对象,未指定年月日时分秒
- set()方法设置Calendar属性
- add()方法计算日期
- 日历对象转时间对象:Date d = calendar.getTime()
- 时间对象转日历对象:g.setTime(new Date())
Math类
Random类
java.io.File类
- new File(“”)创建对象
- createNewFile()、exist()、isDirectory()、isFile()、lastModified()、length()、getName()、getPath()、delete()、mkdir()、mkdirs()、listFiles()
递归打印目录树
enum
泛型
数据类型参数化,调用泛型时必须传入实际类型
泛型类,泛型接口,泛型方法
类型擦除:编码时采用泛型写的类型参数,编译器在编译时会去掉,编译后的class不包括泛型定义,会被编译器替换成具体的Object
注意
- 基本类型不能用于泛型,应该使用包装类
- 不能通过泛型T创建对象,即是错误的
使用
定义:泛型字符可以是任何标识符,一般采用E,T,K,V,N,?

泛型类
泛型接口
泛型方法
非静态

静态方法
静态方法不能使用类上定义的泛型

可变参数
通配符
无界通配符
上限限定
通配符的类型是T或T的子类,T接口或T接口的子接口,适用于泛型类


下限限定
通配符类型是T或T的父类,T接口或T接口的父接口,不适用于泛型类


容器
单例集合——Collection接口
数据一个一个地存储
抽象方法:

List接口
存储有序,可重复,动态数组
List接口中增加的方法(因为list是有序的,添加的方法都是跟元素位序有关的):

ArrayList类
ArrayList类是List接口的实现类,底层是用数组实现的存储
特点:查询效率高,增删效率低,线程不安全
增删改查清
转换为数组
- 转换为Object数组:
- 转换为泛型类型数组:
并集、交集、差集
源码分析
JDK1.7中使用立即加载,初始化时分配默认10个大小的空间(长度为10),JDK1.8之后使用延迟加载,初始化默认为空(长度为0)
Vector类
底层用数组实现,线程安全但效率低,大部分与ArrayList类似
源码分析
- 立即初始化的方式,初始化长度为10
- ,add()方法有并行化处理,是线程安全的
- ArrayList扩容时是1.5倍,Vector是2倍
Stack容器
Vector的一个子类
通过5个方法对Vector进行扩展,实现堆栈操作
empty()、peek()、pop()、push()、search()
Stack匹配括号合法性
LinkedList类
底层使用双向链表实现,查询效率低,增删效率高,线程不安全
使用与ArrayList基本一致,额外添加的方法使用,需要在声明时使用LinkedList变量
源码分析
指定位置添加元素时寻找元素位置——二分:
寻找索引位置时先对size进行>>1,与index进行大小比较;
若index在前半部分从前往后找,否则从后往前找
Set接口
存储无序,不可重复,“集合”,List接口有相比Collection新增的方法,但Set没有
HashSet类
存储特征
- 不保证元素顺序底层用数组和链表实现,底层数组创建默认长度为16,对元素哈希值进行运算决定元素在数组中的位置
- 没有重复元素:哈希值相同的元素利用equals方法判断是否相同,相同则不添加,不同则用单向链表保存
- 允许null元素、线程不安全
HashSet存储自定义对象
HashSet存储两个属性相同的对象时,无法分别出这是两个相同的元素,因为二者的HashCode值不同;因此需要在类中重写equals方法
源码分析
TreeSet类
- 元素自身比较:存储自定义类时实现Comparable接口的CompareTo(o1)方法
- 比较器:新建一个比较器实现Comparator接口,重写compare(o1, o2)方法,new TreeSet时将比较器对象作为参数进行初始化
源码分析
TreeSet是通过实现NavigableMap间接实现TreeMap
双例集合——Map接口
基于key+value的结构存储数据,类似函数f(x)
不能包含重复的键,可以包含重复的值,每个键只能对应一个值
HashMap类
- keySet()获取所有元素:
- entrySet()获取元素:
源码分析

链表使用Node类,红黑树使用TreeNode类,继承Node类
TreeNode类继承Entry类继承Node类实现Entry接口;
数组初始化采用延迟初始化,等有内容了再调用resize()方法扩容和初始化数组;
计算hash值:
- 调用key.hashCode()获得key的hashcode值
- 根据hashcode值计算hash值,要求在[0, length-1]之间,计算方法:
hashcode%length,但对CPU来说除法效率低;
改进:要求数组长度是2的整数幂,hashcode^(hashcode>>>16)&(length-1)
添加元素:
调用put方法时,实际执行putVal方法,先利用key计算hash值,得到放置位置,再new Node对象,放在table数组对应位置上;若对应位置已有元素,判断是替换value还是添加节点;添加节点时需要判断是否要将链表转化为红黑树,判断是否需要扩容
扩容:
每次扩容数组长度double
TreeMap类
效率低于TreeMap,但可以对键值进行排序,底层基于红黑树实现
与TreeSet相同,可以是类实现Comparable接口或自定义比较器作为TreeMap的初始化参数
firstKey():获得最小key值
lastKey():获得最大key
Iterator迭代器
Collection接口继承了Iterator接口,接口中包括iterator抽象方法,返回一个Iterator对象,包含三个方法用于单例容器的迭代处理
- boolean hasNext():当前游标位置是否含有元素
- Object next():获取当前游标位置元素,并将游标后移
- void remove()
注意:
- 最好不要在循环中删除或添加List的元素,删除或添加后元素会移动,list的大小会改变,结果并不是删除或添加一个元素的结果,而会一直删或一直加
- 最好不要在一个循环中多次调用iterator.next()
Collections工具类
- void sort(List)//对List容器内的元素排序,排序的规则是按照升序进行排序。
- void shuffle(List)//对List容器内的元素进行随机排列。
- void reverse(List)//对List容器内的元素进行逆续排列。
- void fill(List, Object)l/用一个特定的对象重写整个List容器。
- int binarySearch(List, Object)//对于顺序的List容器,采用折半查找的方法查找特定对象。
注解 java.annotaion和反射 java.Reflection
annotaion是JDK5.0引入的新技术
作用:对程序作出解释(类似注释);可以被其他程序(如编译器)读取
格式:“@注释名”,可以添加参数值
例如
内置注解

元注解
作用:负责注解其他注解

自定义注解
静态语言 VS. 动态语言

反射
允许程序在执行期借助reflection API获得任何类的内部消息,并能直接操作任意对象的内部属性及方法
作用:在运行时判断任意一个对象所属的类、构造任意一个类的对象、判断类具有的成员变量和方法、获取泛型信息、调用对象的成员变量和方法、处理注解、生成动态代理
优点:实现动态创建对象和编译
缺点:影响性能


获取Class的方法
拥有Class对象的类型: class、interface、数组、enum、annotaion、基本数据类型、void
类加载和内存分析

类的加载过程

类初始化
什么时候会初始化类
类的主动引用——一定会初始化类:
- 虚拟机启动,初始化main方法所在类;
- new一个类的对象;
- 调用类的静态成员和静态方法;
- java.lang.Reflect包的方法对类进行反射调用;
- 初始化类时若父类没有被初始化,则会先初始化它的父类
什么时候不会初始化类
类的被动引用:
- 访问静态域时,只有真正声明这个域的类才会被初始化(例如通过子类引用父类静态变量,只会初始化父类,不会初始化子类)
- 通过数组定义引用,不会初始化数组中的类
- 调用类中的常量
类加载器
作用:将class文件字节码内容加载到内存,在堆中生成这个类的java.lang.Class对象,
类型:根加载器(C++编写、无法直接获取)、扩展加载器、系统类加载器
双亲委派机制:若根加载器中含有的包,则不会调用扩展加载器中的同名包,以此类推
获取类的运行时结构
:获得包名+类名
:获得类名
:获得public属性或本类及父类的所有方法
:获得所有属性或本类的方法
:获得构造器
动态创建对象
反射调用多的话,关闭程序的安全检测能提高效率
反射操作泛型
- parameterizedType:参数化类型,如Map<String,Integer>
反射操作注解
多线程 java.Thread
概念
程序:指令和数据的有序集合,是静态概念
进程process:执行程序的一次执行过程,是动态概念,是系统资源分配的单位
线程thread:一个进程可以包含若干线程,是CPU调度和执行的单位
真正的多线程指有多个CPU(多核);模拟出来的多线程是在一个CPU的情况下切换的很快
- 线程就是独立的执行路径
- 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程
- main()称之为主线程,为系统的入口,用于执行整个程序
- 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的
- 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
- 线程会带来额外的开销,如cpu调度时间,并发控制开销
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
线程创建
继承Thread类
- 自定义线程类继承Thread类
- 重写run()方法
- 创建线程对象,调用start()启动
注意:创建线程后不一定立即执行,执行时间由CPU调度
实现Runnable接口(推荐使用)
- 自定义类实现Runnable接口
- 重写run()方法
- 创建线程对象,new Thread()并将Runnable接口实现类的对象作为构造器的参数传进去
- 调用thread对象的start()方法
与Thread相比,避免了java单继承的局限性
实现Callable接口
- 实现Callable接口
- 重写call()方法
- 创建目标对象
- 创建执行服务:
- 提交执行:
- 获取结果:
- 关闭服务:
静态代理
- 真实对象和代理对象实现同一接口
- 真实对象作为构造参数传入代理对象
- 代理对象调用真实对象执行方法
Lamda表达式
作用:避免匿名内部类定义过多;代码简洁;
函数式接口:任何接口如果只包含一个抽象方法,那么它就是函数式接口
函数式接口可以通过lamda表达式创建对象
- 接口必须是函数式接口
- 方法体只有一行代码时才能省略{}
- 多个参数可以省略参数类型,但必须全部省略且加上()
线程状态
五大状态


线程方法
线程停止
线程休眠 sleep()
slepp()存在异常interruptedException;
sleep()时间达到后线程进入就绪状态;
slepp()可以模仿网络延时、倒计时等;
每个对象都有一个锁,sleep()不会释放锁
线程礼让yield()
线程强制执行join()
观测线程状态getState()
线程优先级
线程优先级用数字表示1~10:
Thread.MIN_PRIORITY=1;
Thread.MAX_PRIORITY=10;
Thread.NORM_PRIORITY=5;
getPriority()获取优先级,setPriority()改变优先级
优先级低只是被调度的概率低,不一定不会被调用
守护线程
线程分为用户线程和守护线程,正常的线程都是用户线程
虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完毕,用户线程全部执行完毕后程序就终止
例如,后台操作日志、监控内存、垃圾回收
线程同步
锁机制(synchronized)
- 一个线程持有锁会导致其他所有需要此锁的线程挂起
- 多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
- 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置﹐引起性能问题.
三大线程不安全案例
- 买票线程,票数出现负数
- 取钱线程,钱数出现负数
- 集合list线程不安全:两个线程操作同一个list
同步方法
synchronized方法:
必须获得调用该方法的对象的锁才能执行,否则线程会阻塞;
方法一旦执行就独占该锁,直到方法返回才释放
缺点:影响效率,只有修改的代码里面需要锁,只读代码添加锁的话会浪费资源
同步块
,Obj称之为同步监视器,可以是任何对象﹐但是推荐使用共享资源作为同步监视器
同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身或者是class
同步监视器的执行过程:
- 第一个线程访问,锁定同步监视器,执行其中代码
- 第二个线程访问,发现同步监视器被锁定,无法访问
- 第一个线程访问完毕,解锁同步监视器
- 第二个线程访问,发现同步监视器没有锁,然后锁定并访问
死锁
两个或多个线程都在等待对方释放资源,都停止执行
同一个同步块有两个以上对象的锁时,可能发生死锁
必要条件
- 互斥:一个资源每次只能被一个进程使用
- 请求与保持:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
Lock接口
与synchronized的区别
- lock是显式锁,需要手动开启和关闭;synchronized是隐式锁,出了作用域自动释放
- lock只能锁代码块,synchronized可以锁方法
- 使用lock锁JVM调度线程花费时间较少,性能更好,扩展性更好
- 优先使用顺序:lock>synchronized代码块>synchronized方法
线程协作
生产者消费者问题
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费
如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止;
如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止.

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/5183.html