A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

[石家庄校区]就业班笔记[Map集合]及[线程]

Day03 List Set

数据结构
数据结构:就是数据的储存方式
栈: 先进后出(FILO)  (子弹夹原理)
适用场景:内容反转 栈内存
队列:先进先出(FIFO) (安检原理)
适用场景: 秒杀,抢购,在线售票    (处理高并发场景)

数组:查询块,增删慢
适用场景:查询多,增删少的数据存储场景  如 城市

链表:查询慢,增删快
链表由多个节点(Node)组成
适用场景:查询少,增删多的场景 (链表可以实现栈和队列的结构)
单向链表节点中只有向下的地址值只能单向从开头查询,双向链表节点中有上下的地址值可以上下查询

红黑树:
是一种 平衡 二叉 查找(排序) 树
红黑树特点:
趋近于平衡树,查询速度快,查询叶子节点最大和最小次数不能超过2倍
查询比链表快, 增删比数组快 (数组和链表的折中)
查最小值一直往左,查最大值一直往右
约束:
1,节点可以是红色或者黑色的(红色节点主要是调节平衡)
2,根节点是黑色的
3,叶子节点(空节点)是黑色的
4,每个红色的节点的子节点都是黑色的
5,任何一个节点到其每一个叶子节点的所有路径上黑节点数相同
适用场景: 查询和增删都有, 需要元素自动排序的场景

List集合
特点:
1,有序的集合,存储和取出元素的顺序是一致的
2,有索引,包含了一些带索引的方法
3,允许存储重复元素

List集合子类:
Arraylist集合
底层是数组结构,
特点: 查询快,增删慢,线程不安全 效率高
适用场景:存储的数据"查询多, 增删少"的场景.同数组
LinkedList集合
底层是链表结构
特点:查询慢,增删快 线程不安全, 效率高(里边包含大量操作首尾元素的方法)
注意:使用LinkedList集合含有特有方法,不能使用多态
适用场景:存储的数据"查询少, 增删多"的场景 同栈和队列
Vector集合 现在几乎不用
底层是数组结构
特点:查询慢 增删快 线程安全,效率低

Set集合体系
特点:1,不允许存储重复的元素
         2,没有索引
set集合在用add方法的时候,会调用元素的hashCode方法和equals方法,判断是否重复
HashSet集合
特点:1,是一个无序集合,存储/取出元素顺序可能不一致
  2,底层是一个哈希表结构(查询速度快)
哈希表 = 数组 + 链表或红黑树(长度超过8,会用红黑树)
有序 无序 :指的是存入和取出的顺序
排序:指的是集合内部从小到大/从大到小的排序
HashSet原理:哈希值
哈希值:是一个十进制的整数,由系统随机给出(是个逻辑性的不是真实的地址)
public native int hashCode();
        可以调用系统本地代码(C/C++)计算出一个对象地址的哈希值
hashCode() 返回该对象的哈希码值   可以被重写
hashCode()方法的作用:       
方法内部的算法用于将对象计算为一个哈希值, 便于根据哈希值比较对象是否"相等"
哈希值主要是为了提高对象存储在 哈希表 中的效率
注意事项:
1, 如果我们不满意Object中的哈希值计算方法, 可以重写hashCode()方法.
       但在Java代码中不能直接重写带有 native 的方法, 重写时应该将 native 去掉
                        @Override
                        public int hashCode() {}
2,hashCode() 方法有可能将"不同的对象"计算出"相同的哈希值", 这称为"哈希冲突", 在
出现冲突后, 一般再通过 equals() 方法来继续判断对象是否"相等"
"重地" 和 "通话"  这两个字符串的哈希值巧合的一样
HashSet原理:哈希表结构(类似表格结构)
jdk 8 之前 哈希表 = 数组+链表
jdk 8 之后 哈希表 = 数组+链表/红黑树(提高查询速度)
哈希表特点:查询速度快
HashSet原理:存储元素不重复的原理
HashSet集合保证添加元素不重复的原理:
调用 add(E e) 添加元素时, 先调用 hashCode() 获取哈希值, 和当前HashSet集合中的元
素比较
        如果哈希值不同, 则认为元素不重复, 添加, 并返回true
        如果哈希值相同, 则可能是哈希冲突, 所以继续调用元素的 equals() 方法和所有哈希值
相同的元素比较
        如果 equals() 比较所有元素都没有相同的, 则认为元素不重复, 添加, 并返回true
        如果 equals() 比较出有相同的元素, 则认为元素重复, 不添加, 并返回false
HashSet存储自定义类型元素
自定义元素需要重新 hashCode() equals() 方法
快捷键 :  alt + insert
LinkedHashSet集合
底层是一个哈希表(数组+链表/红黑树)+链表,其中, 哈希表用于存储数据,多了一条链表(记录
元素顺序),保证元素有序以便在获取元素时保持顺序一致
特点:是有序的集合,不允许重元素,没有索引
遍历的是记录顺序的链表而不是存储的哈希表
C:/Users/Administrator/AppData/Local/YNote/data/x409428917@163.com/a3b96e22b05647eaa91b536b92c197b6/clipboard.png
什么时候用List,什么时候用Set:
要存储的元素可以重复的, 用List集合:
        增删少, 用ArrayList
        增删多, 用LinkedList
要存储的数据要求不重复, 或者相对一个集合去重, 用Set集合:
        不要求存取顺序一致, 用HashSet
        要求存取顺序一致, 用LinkedHashSet

可变参数
使用前提:
当方法的参数列表数据类型已经确定,但是参数个数不确定,就可以使用可变参数
使用格式:定义方法时使用
修饰符 返回值类型 方法名(数据类型...变量名){}
可以直接当做数组名使用
可变参数的原理:
可变参数底层就是一个数组,根据传递参数个数不同,创建不同长度的数组,来存储参数
传递的参数个数,可以是0(不传递)个,1,2....n多个
可变参数的注意事项:
1,一个方法的参数列表,只能有一个可变参数
2,如果方法的参数有多个,那么可变参数必须写在参数列表末尾

Collections集合工具类
java.util.Collections类: 操作集合的工具类
        // 静态方法
        static?<T>?boolean?addAll(Collection<T>?c,?T...?elements):往集合中添加一些元素
T :代表要用数据类型
Collections.addAll(list,1,2,3,4,5);
        static?void?shuffle(List<?>?list) :打乱集合顺序
只能传递list集合,set不可以
static?<T>?void?sort(List<T>?list) :将集合中元素按照默认规则排序
默认的是"升序" 例如 数字 字母 都可以按升序排序
注意:
void?sort(List<T>?list) 使用的前提
被排序的集合里边存储的元素,必须实现comparable,重写接口中的方法comparable定义
排序的规则
自定义对象需要重写comparable方法;
实现接口comparable<写自定义对象名>
public class Person01 implements Comparable<Person01> {}
规则:  this - 参数 升序
   参数 - this 降序
static?<T>?void?sort(List<T>?list,Comparator<??super?T>?c):将集合中元素按照指定规
则排序
Comparator使用方式:
        1. 定义类实现Comparator<E>接口, 重写 int compare(E o1, E o2) 方法, 泛型为比
较元素的类型
例 Collections.sort(list, new Comparator<Student>() {
                                            @Override
                                            public int compare(Student o1, Student o2) {
                                                return o1.getAge()-o2.getAge();
                                            }
});
排序规则 :  o1 -o2 升序,   o2 - o1 降序
Comparable接口和Comparator接口区别
        Comparable: 让JavaBean自身具有可比较性 (自己和其他人比)
        Comparator: 定义一个比较器类, 用比较器对象比 (让第三个人来帮两个人比较)



Day04 Map双列集合
Map体系
特点:
  Map集合 每一个元素都包含两个值(一个键Key,一个值Value),组成键值对,所以map就双列集合
  Map集合中key不允许重复,value可以重复
  Map集合中key和value的数据类型可以相同,也可不同
  Map集合中key和value是一一对应的,一个键只能对应一个值
May常用的子类
HashMap集合
特点:1,底层是哈希表,查询速度特别快
  2,是一个无序集合,Key存储/取出元素可能不一致 不能重复
LinkedHashMap集合
特点:1,底层是哈希表+链表
  2,是一个有序集合,Key存储/取出元素是有序的 不能重复
Map中常用方法
java.util.Map接口: 双列集合的顶层
   // 成员方法 都是通过Key 来获取 值得数据类型不能是基本类型
   V?put(K?key,?V?value): 添加/修改 键值对.
       如果键存在, 则用新值替换已有值, V返回被替换的值;
       如果键不存在, 则添加键值对, V返回null
V表示的是值
   V?remove(Object?key): 根据键删除键值对, 返回被删除元素的值
        如果键不存在, 返回null
如果键存在,返回被删除键的值
   V?get(Object?key): 根据键获取值.
            如果键不存在, 则返回null
  如果键存在,返回对应键的值
   boolean containsKey(Object key): 判断是否包含指定的键
是boonlean类型  
包含返回true,不包含返回false.
Map遍历方式一 keyset()方法实现通过键找值
Set<K>?keySet(): 获取Map集合中所有的键, 存储到Set集合中
K:代表数据类型,要跟Map集合中键的类型一致:
keySet()遍历步骤:
1,使用Map集合中的方法keySet(),把Map集合所有的key取出,存储到一个set集合中
2,遍历set集合,获取Map中的每一个key
3,通过Map集合中的get方法,找到value值
C:/Users/Administrator/AppData/Local/YNote/data/x409428917@163.com/b9f7ee2c17e640189685c1bd876de76d/clipboard.png
Entry键值对对象
Map.Entry(K,V):在map接口中有一个内部接口Entry
作用:当Map一创建,那么就会在集合中创建一个entry对象,用来记录键与值
Map遍历方式二 entrySet()方法获取Entry对象形式遍历
Set<Map.Entry<K,V>>?entrySet(): 获取到Map集合中所有的键值对对象的集合(Set集合)       
                    写上泛型
遍历步骤:
1,使用Map集合中的方法entrySet(),把Map中的多个Entry对象取出,存储到set集合
2,遍历set集合,获取没有个Entry对象
3,使用Entry对象中的方法getKey()和getValue()获取键与值
C:/Users/Administrator/AppData/Local/YNote/data/x409428917@163.com/670521d5c5d142bba4a3614a7f2ca0f2/clipboard.png
HashMap存储自定义类型键值
Map集合保证key是唯一的;
自定义JavaBean
作为键(key)的元素,必须重写hashCode方法和equals方法
作为值(value)的元素,没有要求
LinkedHashMap类
LinkedHashMap底层:哈希表+链表
key不允许重复,但key存取有序,是有序集合
小知识:HashSet底层其实是HashMap
Hashtable类(目前很少使用)
底层构成是一个哈希表 线程安全 速度慢
不能存储null键,null值
    treemap类是HashMap的子类 具有排序的特性
JDK9对集合添加的优化
list set map接口:里边增加了一个静态方法of,可以给集合一次性添加多个元素
static List<E> of(E... e)
使用前提:当集合中存储的元素的个数已经确定,不在改变时
静态方法,可以用(接口.of)直接调用使用
注意:
1,of方法只试用于list set map接口,不适用于接口的实现类
2,of方法的返回值是一个不能改变的集合,集合不能再使用add,put方法添加元素
3,set接口和map接口在调用of方法是,不能有重复元素,否则会抛出异常



Day05 异常 多线程
异常
异常:程序在运行中出现非正常情况,导致JVM无法运行,非正常停止
异常的体系:
java.lang.Throwable  // 体系最顶层
        |_ Error         // 不应该试图捕获的严重问题, 不能处理的错误
        |_ Exception     // 可以处理的异常
异常的分类
错误(Error): 不能捕获处理的严重问题. 绝症
        必须将程序停下来, 修改代码才能解决
        错误的类名都是 "XxxError" 方式
异常(Exception): 可以捕获处理的问题. 发烧感冒吃个药就好了
     程序执行起来后, 如果有合适的处理方式, 即使发生异常, 程序也能处理该异常并继续运行
        异常的类名都是 "XxxException" 方式
        1. 编译时异常:
           编译时期就会发生的异常, 必须在编译时期处理  Unhandled exception XxxException
        2. 运行时异常:
                编译时正常, 运行时才会发生的异常
C:/Users/Administrator/AppData/Local/YNote/data/x409428917@163.com/5ba638f1e3fc491ea9e2968fe212e27f/clipboard.png

异常关键字:throw制造异常
作用:在方法中抛出指定的异常对象,起到通知的作用
格式: throw new 异常类名("异常原因字符串");
注意:1,throw必须写在方法内部    throw处理异常后会结束方法
  2,new的对象必须是Exception或者是其子类对象
  3,throw关键字抛出指定的异常对象,我们必须处理这个异常
throw后边创建的是RuntimeException或去子类,我们可以不处理,默交给JVM处理
throw后边创建的是编译异常,我们就必须处理这个异常,通过throws 或者try...catch

异常的处理方式1:throws声明抛出异常
throws:声明抛出(自己不处理,交给方法调用者处理)
作用: 告诉方法的调用者, 调用此方法有可能发生异常, 需要在编写代码时处理
格式:在方法声明时使用
修饰符 返回值类型 方法名(参数列表) throws
注意:
1,throws关键字必须写在方法声明处
2,throws关键字声明的异常必须是Exception或者是其子类对象
3,方法内部抛出多个异常对象,那么throws后边必须也需要声明多个
如果抛出的异常时字符类关系,可以直接声明父类异常
4,调用一个声明抛出异常的方法,我们必须处理声明的异常
要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM
要么try...catch自己处理异常

异常的处理方式2:捕获异常(try..catch)
try..catch : 捕获并自己处理异常
格式:       catch可以有多个
    try {
        // 可能产生异常的代码
    } catch (异常类名 变量名) {
        // 处理异常的代码
        // 一般会将异常信息存储到日志中
    }
        ...
注意:
1,try中可能会抛出多个异常,那么就可以使用多个catch来处理这些异常
2,如果try中产生了异常,那么就会执行catch中的处理方法,执行完毕继续下边的代码
   如果try中没有产生异常,那么不会执行catch中的处理逻辑,执行完try后继续后边代码
Throwable中的3个异常处理方法
java.lang.Throwable
        // 成员方法    只能是异常对象的接收对象调用
        String getMessage(): 返回异常的信息. 没有原因返回null  需要sout打印
        String toString(): 返回异常的类型和原因信息 需要sout打印
        void printStackTrace(): 使用标准错误输出流打印异常信息,异常信息显示最全面 不需要sout
finally代码块
格式:   
    try {
        // 可能发生异常的代码
    } catch(异常类型 异常变量名) {
        // 处理异常
    }
        ...
        catch(异常类型 异常变量名) {
        // 处理异常
    } finally {
        // 无论是否发生异常, 是否捕获, 最后都会执行的代码.
        // 通常在这里执行释放资源的操作
    }
注意:1,finally不能单独使用,必须和try一起使用
  2,一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源释放(Io流)

异常注意事项
1,捕获多个异常的3中方式
1,多个异常分别处理
2,一个try 多个catch(如果异常存在继承关系, 子类异常在上, 父类异常在下)
3,多个异常,一次捕获一次处理
用Exception多态捕获
2,finally中有return语句,永远返回的都是finally中的return,return不要写在finally中
3,子父类继承重写方法时的异常要求
子父类继承关系下, 子类重写父类带有 throws 的方法:
                        1. 如果父类抛出多个异常, 子类重写父类方法时可以有3种方式:
                                a: 抛出和父类相同的异常
                                b: 抛出父类异常的子类
                                c: 不抛出异常
                        2. 父类方法没有抛出异常, 子类重写父类该方法时也不可抛出异常
                                此时子类产生该异常, 只能捕获处理, 不能声明抛出
一般父类方法声明的异常是什么样的, 子类重写的方法声明异常就什么样, 保持一致即可

自定义异常类
如果Java提供的异常类不足以满足我们的需求, 我们也可以自己定义异常类
        定义编译时异常: 继承 Exception
        定义运行时异常: 继承 RuntimeException

多线程
并发: (交替执行),指两个或多个事件在"同一时间段内"发生
并行:(同时执行),指两个或多个事件在"同一时刻"发生
我们研究的是并发
进程:一个应用程序在内存中的一次执行过程(一个程序可以有多个进程)
C:/Users/Administrator/AppData/Local/YNote/data/x409428917@163.com/f625e3aead2e46458c85f9c5d4adffd4/clipboard.png
线程:是进程内的一个独立执行单元(一条代码执行路径)
一个程序运行后至少有一个进程,一个进程中可以包含多个线程
程序 > 进程 > 线程
多线程的好处:
效率高.  多线程之间互不影响
C:/Users/Administrator/AppData/Local/YNote/data/x409428917@163.com/1f494f3b8dc848ee9c10dc5ebb570ea3/clipboard.png

线程的调度
分时调度:所有线程轮流使用CPU,平分占用CPU的时间
抢占式调度:优先让优先级高的线程使用CPU.Java用的是抢占式调度
主线程(main线程)
主线程:
        我们以前编写的代码, 也在一条线程中执行, 该线程叫作"main线程", 也称为"主线程"
单线程的执行特点:
        同一个线程内的代码, 从上往下依次执行

创建多线程程序的第一种方式:继承Thread类
实现多线程的第一种方式(固定写法):
1. 定义子 继承 Thread 类
        2. 子类重写 run() 方法, run方法内部是线程要执行的任务
        3. 创建Thread子类的对象, 调用 start() 方法启动线程
注意:
        必须调用 start() 方法来开启线程, 不能直接调用 run() 方法, 调用 run() 会变成单线程
        同一个线程对象, 不能多次调用 start() 方法
        Java是抢占式调度, 不同线程的代码, 执行顺序是随机的




Day06 线程 同步 线程间通信
线程
多线程原理1:线程执行的随机性
第一种方式: 继承Thread类
        定义类继承Thread
        重写run()方法, 要执行的任务
        创建子类的对象, 调用start()方法启动线程
C:/Users/Administrator/AppData/Local/YNote/data/x409428917@163.com/cd54f37273c74fadae35186c489f4943/clipboard.png
多线程原理2:多线程的内存
多线程运行时,每个线程都有各自的栈内存
C:/Users/Administrator/AppData/Local/YNote/data/x409428917@163.com/4a900fb45d60476e885a33b94144113e/clipboard.png

Thread常用方法:getName(), currenThread()
获取线程名称:
1,getName():获取当前线程的名称
2,先获取当前执行的线程(currenThread()),使用线程中的getName()获取名称
格式: Thread.currenThread().getName()

Thread常用方法:setName(), Thread(String name)
设置线程名称:
1,setName():设置线程名称
2,创建有参构造方法,参数传递线程名称,调用父类的带参构造,让父类给子类起名

Thread常用方法:sleep()  静态方法,类名直接调用
static void sleep(long millis): 让所在线程睡眠指定的毫秒 (暂停毫秒值后继续执行)
sleep()是个异常方法,需要用try...catch处理异常

创建多线程程序的方式2:实现Runnable接口
创建线程的第2种方式:
        1. 定义类, 实现Runnable接口
        2. 重写 run() 方法, 要执行的代码(任务)
        3. 创建Runnable实现类对象 (任务对象)
        4. 创建Thread类对象, 在构造方法中传入Runnable实现类对象 (将任务和线程绑定)
        5. 通过Thread对象调用 start() 方法启动线程
java.lang.Thread类: 表示线程. 实现了Runnable接口
        // 构造方法
    Thread Thread(Runnable target): 通过Runnable对象创建Thread对象
    Thread Thread(Runnable target, String threadName): 通过Runnable对象创建对象并指定线程名
Thread和Runnable的区别
实现Runnable的好处:
        1. 避免单继承的局限性
        2. 增强了程序的扩展性, 降低了程序的耦合性(解耦)
                线程是Thread, 任务是Runnable实现类对象. 相当于将线程和任务分离
耦合性: 相互之间的关系的紧密程度
耦合性高: 相互之间的关系非常紧密
耦合性低: 相互之间的关系不太紧密
我们追求 "低耦合"
C:/Users/Administrator/AppData/Local/YNote/data/x409428917@163.com/a1728306bb324d77a45aca34a711cb87/clipboard.png
匿名内部类方式创建线程
匿名内部类(没有名字写在其他类内部的类)
格式 new 父接口/父类(){
重写方法
};
作用:简化代码
把子类(实现类)继承(实现接口),重写父类方法(接口中的方法),创建子类(实现类)对象合成一步完成
线程安全问题
多线程访问共享线程会出现线程安全问题        
C:/Users/Administrator/AppData/Local/YNote/data/x409428917@163.com/e73f9e13ab3a47f28d85ce595334c295/clipboard.png
线程安全问题的原因
问题发生场景:
        多个线程操作共享资源
问题发生原因:
        JVM是抢占式调度, CPU在每个线程之间切换是随机的, 代码执行到什么位置是不确定的
        在操作共享资源时, 由于一个线程还没有执行完, 另一个线程就来操作, 就会出现问题
如何解决:
        在操作共享资源时, 让线程一个一个来执行, 不要并发操作共享变量, 就可以解决问题
解决线程安全问题方式1:同步代码块
同步代码块: 使用 synchronized 关键字修饰的代码块, 并传入一个当作锁的对象
小知识  锁对象 也叫同步锁或对象监视器
格式:
    synchronized (锁对象) {
                // 操作共享数据的代码
      }  
注意:
        锁对象可以是"任意类型的一个对象"
        锁对象必须是"被多个线程共享的唯一的"对象
        锁对象的作用: 只让一个线程在同步代码块中执行
同步技术解决线程安全问题的原理:
    线程进入同步代码块前, 会"争夺锁对象", "只有一个线程"会抢到锁对象
    进入同步代码块的线程, 会"持有锁对象", 并执行同步代码块中的代码
    此时同步代码块外的线程, 处于"阻塞"状态, 只能等待
    当同步代码块内的线程执行完代码块, 会离开同步代码块, 并"归还锁对象"给同步代码块
    等在同步代码块外的其他线程就可以继续争夺锁对象
优缺点: 可以保证线程的安全,但需要频繁判断锁 获取锁 效率低
C:/Users/Administrator/AppData/Local/YNote/data/x409428917@163.com/8e4e9005cbb248d4a70da1bc15dd6640/clipboard.png
解决线程安全问题方式2:同步方法
同步方法: 使用 synchronized 关键字修饰的方法, 具有默认的锁对象
注意 默认锁对象是this
        this必须是唯一的 不然无法保证安全性
// 非静态同步方法
        public synchronized void method(){
                // 可能会产生线程安全问题的代码
        }
静态同步方法
静态同步方法:
        public static synchronized void method(){
                // 可能会产生线程安全问题的代码
        }
静态同步方法的锁对象: 当前类的字节码对象 Class对象
解决线程安全问题方式3:Lock锁
java.util.concurrent.locks.Lock接口: JDK 5 新增的Lock接口
        // 成员方法
        void lock(): 获取锁
        void unlock(): 释放锁       
java.util.concurrent.locks.ReentrantLock类: Lock的实现类
使用方式:
public class RunnableImpl implements Runnable {
    // 成员变量创建锁对象, 该锁对象也要所有线程共享唯一一个
    Lock lock = new ReentrantLock();  // 成员变量
    @Override
    public void run() {
       // 加锁
        lock.lock();
        try {
            // 操作共享变量的代码...
        } finally {  
            // 在finally中保证释放锁
            lock.unlock();  
        }
    }
}
lock.unlock(); 放在 finally里边,可以保证程序是否正常运行都会释放锁,提高效率

线程间的通信
线程状态概述
线程的生命周期中, 可以出现有6种状态:
    1. "NEW 新建"
       线程被创建, 但没有调用 start() 启动
    2. "RUNNABLE 可运行"
       调用 start()方法后已启动, 但可能正在执行 run() 方法的代码, 也可能正在等待CPU的调度
    3. "BLOCKED (锁)阻塞"
       线程试图获取锁, 但此时锁被其他线程持有
    4. "WAITING 无限等待"
       通过锁对象调用无参的 wait() 进入此状态.
       等待其他线程通过锁对象执行 notify() 或 notifyAll() 才能结束这个状态
    5. "TIMED_WAITING 计时等待"
       如通过锁对象调用有参的 wait(long millis) 或 sleep(long millis), 则进入此状态.
       直到时间结束之前被其他线程通过锁对象执行 notify()或 notifyAll()唤醒, 或时间结束自动唤醒
    6. "TERMINATED 终止"
       run()方法结束(执行结束, 或内部出现异常), 则进入此状态
Object类中wait(long timeout)和notifyAll()方法
java.lang.Object类:
        // 成员方法 (只能通过"锁对象"调用)
        void notifyAll(): 唤醒所有在同一个锁对象上处于等待状态的线程
        void wait(long timeout): 让当前线程处于计时等待状态, 时间到或被唤醒后结束此状态
wait() 和 sleep() 的区别:
        1. wait会释放锁, 恢复时需要重新获取锁; sleep不会释放锁
        2. wait可以被notify/notifyAll唤醒; sleep不会
        3. wait要用锁对象调用; sleep要用Thread类名调用








0 个回复

您需要登录后才可以回帖 登录 | 加入黑马