记录一下java学习过程的重要知识点
out: for (int i = 0; i < 10; i++) {
System.out.print("i = " + i + "\t");
for (int j = 0; j < 5; j++) {
System.out.print("j =" + j + "\t");
if (j == 3) {
break out;
}
}
System.out.print("\n");
}
7.当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存中,随着方法的执行结束,这个方法的内存栈也将自然销毁。 因此,所有在方法中定义的局部变量都是放在栈内存中的;在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成本通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随着方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(在方法的参数传递时很常见),则这个对象依然不会被销毁。只有当一个对象没有任何引用变量引用它时,系统的垃圾回收器才会在合适的时候回收它。
8.static修饰符的真正作用就是用于区分成员变量、方法、内部类、初始化块这四种成员到底属于类本身还是属于实例。在类中定义的成员,static相当于一个标志,有static修饰的成员属于类本身,没有static修饰的成员属于该类的实例。
10.如果同一个类中定义了test(String... books)方法,同时还定义了一个test(String)方法,则test(String... books)方法的books不可能通过直接传入一个字符串参数来调用,如果只传入一个参数,系统会执行重载的test(String)方法。如果需要调用test(String... books)方法,又只想传入一个字符串参数,则可采用传入字符串数组的形式。
11.如果一个Java源文件里定义的所有类都没有使用public修饰,则这个源文件的文件名可以是一切合法的文件名;但如果一个Java源文件里定义了一个public修饰的类,则这个源文件的文件名必须与public修饰的类的类名相同。
13.构造器相关:a.子类构造器执行体的第一行使用super显式调用父类构造器,系统将根据super调用里传入的实参列表调用父类对应的构造器;b.#### 子类构造器执行体的第一行代码使用this显式调用本类中重载的构造器,系统将根据this调用里传入的实参列表调用本类中的另一个构造器。执行本类中另一个构造器时即会调用父类构造器;c.子类构造器执行体中既没有super调用,也没有this调用,系统将会在执行子类构造器之前,隐式调用父类无参数的构造器。
14.不管上面哪种情况,当调用子类构造器来初始化子类对象时,父类构造器总会在子类构造器之前执行;不仅如此,执行父类构造器时,系统会再次上溯执行其父类构造器......依次类推,创建任何java对象,最先执行的总是java.lang.Object类的构造器。
16.当创建java对象时,系统总是先调用该类里定义的初始化块。初始化块虽然也是Java类的一种成员,但它没有名字,也就没有标识,因此无法通过类、对象来调用初始化块。初始化块只在创建java对象时隐式执行,而且在执行构造器之前。
18.与构造器类似,创建一个Java对象时,不仅会执行该类的普通初始化块和构造器,而且系统还会一直上溯到java.lang.Object类,先执行java.lang.Object类的初始化块,开始执行 java.lang.Object的构造器,依次向下执行其父类的初始化块,开始执行其父类的构造器..#### ...最后才执行该类的初始化块和构造器,返回该类的对象。
19.初始化块定义时使用static修饰符,这个初始化块就变成了静态初始化块,也被称为类初始化块(普通初始化块负责对对象执行初始化,类初始化块负责对类进行初始化)。静态初始化块是类相关的,系统将在类初始化阶段执行静态初始化块,而不是在对象创建时执行。 #### 因此静态初始化块总是比普通初始化块先执行。
20.与普通初始化块类似的是,系统在类初始化阶段执行静态初始化块时,不仅会执行本类的静态初始化块,而且还会一直上溯到java.lang.Object类(如果他包含静态初始化块),先执行java.lang.Object类的静态初始化块(如果有),然后执行其父类的静态初始化块.....#### .最后才执行该类的静态初始化块。
22.java提供了final关键字来修饰变量、方法和类,系统不允许为final变量重新赋值,子类不允许覆盖父类的final方法,final类不能派生子类。通过使用final关键字,允许java实现不可变类,不可变类会让系统更安全。
Integer int1 = 2;
Integer int2 = 2;
System.out.println(int1 == int2);
Integer int3 = 128;
Integer int4 = 128;
System.out.println(int3 == int4);
结果是true,false。原因在于观察Integer源码可知内部会管理一个长度为256的Integer数组,里面存了-128-127所有整数,所以把一个-128-127之间的数自动装箱成一个Integer实例时,实际是指向cache数组中的一个元素,所以它们完全相等,而每次把不在-128-127之间的整数自动装箱,就会不相等。
24.对于直接做+运算的两个字符串(字面量)常量,并不会放入String常量池中,而是直接把运算后的结果放入常量池中;对于先声明的字符串字面量常量,会放入常量池,但是若使用字面量的引用进行运算就不会把运算后的结果放入常量池中了;总结一下就是JVM会对String常量的运算进行优化,未声明的,只放结果;已经声明的,只放声明
25.final修饰的成员变量必须由程序员显式的指定初始值,final修饰的类变量必须在静态初始化块中指定初始值或声明该类变量时指定初始值,而且只能在两个地方的其中之一指定;final修饰的实例变量必须在非静态初始化块、声明该实例变量或构造器中指定初始值,而且只能在三个地方的其中之一指定。
27.当时用final修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。但对于引用类型变量,它保存的仅仅是一个引用,final只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但是这个对象完全可以发生改变。
30.抽象类必须使用abstract修饰符来修饰,抽象方法也必须使用abstract修饰符来修饰,抽象方法不能有方法体;抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类实例。即使抽象类里不包含抽象方法,这个抽象类也不能创建实例;抽象类可以包含成员变量、方法(普通方法和抽象方法都可以)、构造器、初始化块、内部类(接口、枚举)5种成分。抽象类的构造器不能用于创建实例,主要是用于被其子类调用;含有抽象方法的类(包括直接定义了一个抽象方法;或继承了一个抽象父类,但没有完全实现父类包含的抽象方法;或实现了一个接口,但没有完全实现接口包含的抽象方法三种情况#### )只能被定义成抽象类。
32.java8允许在接口中定义默认方法,默认方法必须使用default修饰,该方法不能使用static修饰,无论程序是否指定,默认方法总是使用public修饰如果开发者没有指定public,系统会自动为默认方法添加public修饰符。由于默认方法并没有static修饰,因此不能直接使用接口来调用默认方法,需要使用接口的实现类的实例来调用默认方法。
33.java8允许在接口中定义类方法,类方法必须使用static修饰,该方法不能使用default修饰,无论程序是否指定,类方法总是使用public修饰--如果开发者没有指定public,系统会自动为类方法添加public修饰符。类方法可以直接使用接口来调用。
38.Lambda表达式:形参列表。形参列表允许省略参数类型。如果形参列表只有一个参数,甚至连形参列表的圆括号也可以省略;箭头(->)。必须通过英文中画线号和大于符号组成;代码块。如果代码块只包含一条语句,Lambda表达式允许省略代码块的花括号,那么这条语句就不要用花括号表示语句结束。Lambda代码块只有一条return语句,甚至可以省略return关键字。Lambda表达式需要返回值,而它的代码块中仅有一条省略了return的语句,Lambda表达式会自动返回这条语句的值。
39.Lambda表达式的类型,也被称为“目标类型”,Lambda表达式的目标类型必须是“函数式接口”。函数式接口代表只含有一个抽象方法的接口。函数式接口可以包含多个默认方法、类方法,但只能声明一个抽象方法。
41.为了保证Lambda表达式的目标类型是一个明确的函数式接口,一般有如下三种常见形式:将Lambda表达式赋值给函数式接口类型的变量;将Lambda表达式作为函数式接口类型的参数传给某个方法;使用函数式接口对Lambda表达式进行强制类型转换。
43.引用类方法:示例(类名::类方法),说明(函数式接口中被实现方法的全部参数传给该类方法作为参数),对应的Lambda表达式:(a,b,c...)->类名.类方法(a,b,c...);引用特定对象的实例方法:示例(特定对象::实例方法),说明(函数式接口中被实现方法的全部参数传给该方法作为参数),对应的Lambda表达式:(a,b,c...)->特定对象.实例方法(a,b,c...);引用某类对象的实例方法:示例(类名::实例方法),说明(函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数),对应的Lambda表达式:(a,b,c...)->a.实例方法(b,c...);引用构造器:示例(类名::new),说明(函数式接口中被实现方法的全部参数传给该构造器作为参数),对应的Lambda表达式:(a,b....)->new 类名(a,b,...);
@FunctionalInterface
public interface Converter {
Integer converter(String from);
}
Converter converter=from->Integer.valueOf(from); 改写之后:Converter converter=Integer::valueOf;
Integer val1=converter.converter("99");
System.out.println(val1);
Converter converter2 = from -> "fkit.org".indexOf(from); 改写之后:Converter converter2="fkit.org"::indexOf;
@FunctionalInterface
public interface MyTest {
String test(String a, int b, int c);
}
MyTest myTest = (a, b, c) -> a.substring(b, c); 改写之后:MyTest myTest = String::substring;
@FunctionalInterface
public interface YourTest {
JFrame win(String title);
}
YourTest yourTest = a -> new JFrame(a); 改写之后:YourTest yourTest = JFrame::new;
44.Lambda表达式和匿名内部类的相同点:Lambda表达式与匿名内部类一样,都可以直接访问“effectively final”的局部变量,以及外部类的成员变量(包括实例变量和类变量);Lambda表达式创建的对象与匿名内部类生成的对象一样,都可以直接调用从接口中继承的默认方法。
45.Lambda表达式和匿名内部类的不同点:匿名内部类可以为任意接口创建实例--不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方法即可,但Lambda表达式只能为函数式接口创建实例;;匿名内部类可以为抽象类甚至普通类创建实例,但Lambda表达式只能为函数式接口创建实例;匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法;但Lambda表达式的代码块不允许调用接口中定义的默认方法。
46.枚举类和普通类的区别:枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是默认继承Object类,因此枚举类不能显式继承其他父类。其中java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable接口;使用enum定义、非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类;枚举类的构造器只能使用private访问控制符,如果省略了构造器的访问控制符,则默认使用private修饰,如果强制指定访问控制符,则只能指定private修饰符;枚举类的所有实例必须在枚举类的第一行显式列出,否则这个枚举类永远都不能产生实例,列出这些实例时,系统会自动添加public static final修饰,无需程序员显式添加。
48.java垃圾回收机制的特性:垃圾回收机制只负责回收堆内存中的对象,不会回收任何物力资源(例如数据库连接、网络IO等资源);程序无法精确控制垃圾回收的运行,垃圾回收会在合适的时候进行。当对象永久的失去引用后,系统就会在合适的时候回收它所占的内存;在垃圾回收机制回收任何对象之前,总会先调用它的finalize()方法,该方法可能使该对象重新复活(让一个引用变量重新引用该对象),从而导致垃圾回收机制取消回收。
49.大部分时候,程序强制系统垃圾回收后总会有一些效果。强制系统垃圾回收有如下两种方式:调用System类的gc()静态方法:System.gc();调用Runtime对象的gc()实例方法:Runtime.getRuntime().gc()。
50.对象的引用:强引用(StrongReference,程序创建一个对象,并把这个对象赋给一个引用变量,程序通过该引用变量来操作实际的对象);软引用(SoftReference,需要通过SoftReference类来实现,当一个对象只有软引用时,它有可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存空间足够时,他不会被系统回收,程序也可使用该对象,当系统内存不足时,系统可能会回收它。软引用通常用于堆内存敏感的程序中);弱引用(WeakReference,通过WeakReference类实现,弱引用和软引用类似,但是引用级别更低。对于只有弱引用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存。当然,并不是说当一个对象又有弱引用时,它就会立即被回收--正如那些失去引用的对象一样,必须等到系统垃圾回收机制运行时才会被回收);虚引用(PhantomReference:通过PhantomReference类实现,虚引用完全类似于没有引用。虚引用对对象本身没有太大影响,对象甚至感觉不到虚引用的存在。如果一个对象只有一个虚引用时,那么它也没有引用效果大致相同。虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚引用必须和引用队列(ReferenceQueue)联合使用)。
51.使用这些引用类可以避免在程序执行期间将对象留在内存中。如果以软引用、弱引用或者虚引用的方式引用对象,垃圾回收器就能够随意地释放对象。如果希望尽可能减小程序在其生命周期中所占用的内存大小时,这些引用类就很有用处。
obj = wr.get();
if(obj == null){
wr = new WearReference(recreateIt());
obj = wr.get();
}
//使用obj...
obj = null;
obj = wr.get();
if(obj == null){
obj = recreateIt();
wr = new WearReference(obj);
}
//使用obj...
obj = null;
54.System类提供了一个identityHashCode(Object x)方法,该方法返回指定对象的精确hashCode值,也就是根据该对象的地址计算得到的hashCode值。当某个对象的hashCode方法被重写后,该类实例的hashCode()方法就不能唯一的标识该对象;但是通过identityHashCode()方法返回的hashCode值,依然是根据该对象的地址计算得到的hashCode值。所以如果两个对象的identityHashCode值相同,则两个对象绝对是同一个对象。
Runtime runtime=Runtime.getRuntime();
try {
runtime.exec("notepad.exe");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
56.Object类的clone()方法是一种“浅克隆”,只克隆该对象的所有成员变量值,不会对引用类型的成员变量值所引用的对象进行克隆。如果需要对对象进行“深克隆”,则需要开发者自己进行“递归”克隆,保证所有引用类型的成员变量所引用的对象都被复制了。
57.Random类专门用于生成一个伪随机数,它有两个构造器:一个构造器使用默认的种子(以当前时间作为种子),另一个构造器需要程序员显式传入一个long型整数的种子。ThreadLocalRandom类是java7新增的一个类,它是Random的增强版。在并发访问的环境下,使用ThreadLocalRandom来代替Random可以减少多线程资源竞争,最终保证系统具有更好的线程安全性。
59.由于java的double类型进行基本运算时会造成精度丢失,所以java提供了BigDecimal类。创建BigDecimal对象时,不要直接使用double浮点数作为构造器参数来调用BigDecimal构造器,否则同样会发生精度丢失。如果使用BigDecimal(String val)构造器的结果是可预知的,因此通常建议优先使用基于string的构造器,如果必须使用double浮点数作为BigDecimal构造器的参数时,不要直接将double浮点数作为参数,应该通过BigDecimal.valueOf(double value)静态方法来创建BigDecimal对象。
60.关于Calendar类有个好用的方法add()和roll()功能类似都是接收两个参数,一个field代表操作的是年月日时分秒之一,第二个参数是要添加(正数)减少(负数)的值,两个方法的区别add如果field添加超过限制会改变前一位,roll不会。
61.Calendar类的set方法有延时性,每次执行set之后Calendar对象并没有立即更改只是在内部标记了一个set了某字段的属性,当再次调用get()/getTime()/getTimeInMillis()/add()或roll()时才会改变。这样可以避免不必要的计算。
63.Iterator必须依附于Collection对象,若有一个Iterator对象,则必须有一个与之关联的Collection对象。Iterator提供了两个方法来迭代访问Collection集合里的元素,并可通过remove()方法来删除集合中上一次next()方法返回的集合元素。
64.当使用Iterator迭代访问Collection集合元素时,Collection集合里的元素不能被改变,只有通过Iterator的remove()方法删除上一次next()方法返回的集合元素才可以,否则将发生java.util.ConcurrentModificationException异常。
66.java8位Collection集合新增了一个removeIf(Predicate filter)方法,可以批量删除filter条件的所有元素,Predicate也是函数式接口,因此可使用Lambda表达式作为参数。使用Predicate的test()方法判断该对象是否满足Predicate指定的条件。
67.当向hashSet中添加可变对象时,必须十分小心,如果修改HashSet集合中的对象,有可能导致该对象与集合中的其他对象相等(HashCode值相等),从而导致HashSet无法准确访问该对象。
69.TreeSet是SortedSet接口的实现类,可以确保元素处于排序状态,因为TreeSet中的元素是有顺序的,所以增加了访问第一个、前一个、后一个、最后一个元素的方法,并提供了三个从TreeSet中截取子TreeSet的方法。
70.Treeset根据元素的实际值大小来进行排序,不是根据插入顺序。HashSet根据hash算法确定元素的存储位置,TreeSet采用红黑树的数据结构来存储集合元素。Treeset支持自然排序和定制排序。
72.Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现该接口的类必须实现该方法。例如obj1.compareTo(obj2),如果该方法返回0则表明两个对象相等;如果该方法返回一个正整数,则表明obj1大于obj2;如果该方法返回一个负整数,则表明obj1小于obj2。
73.向TreeSet中添加元素时,只有第一个元素无需实现Comparable接口,后面的所有元素必须实现,这样试图从TreeSet中取出元素时,会引发ClassCastException,所以最好所有元素都要实现Comparable接口。
74.一旦改变了TreeSet集合里可变元素的实例变量,当再企图删除该对象时,TreeSet也会删除失败(甚至集合中原有的、实例变量没有被修改但与修改后元素相等的元素也无法删除)。所以为了程序更加健壮,不要修改放入HashSet和TreeSet集合中元素的关键实例变量。
80.ArrayList和Vector都是List的实现类,都是基于数组实现的List类,内部封装了一个动态的、允许再分配的Object[]数组,它们内部使用initialCapacity参数来设置该数组的长度,每次添加元素超出该数组长度时,它们的initialCapacity会自动增长,如果向ArrayList或Vector集合中添加大量元素时,可使用ensureCapacity(int minCapacity)(将ArrayList或Vector集合的数组长度增加大于或等于minCapacity值)方法一次性的增加initialCapacity。可以减少重分配的次数,提高性能。
82.ArrayList和vector用法相同,Vector是一个非常古老的借口从JDK1存在,方法名大多很长不方便使用,但是是线程安全的,ArrayList使用方便但是多线程使用要注意很多问题(线程不安全)。
84.固定长度的List:Arrays.ArrayList是一个gudingchangdudeList集合,程序只能遍历访问该集合里的元素,不可增加、删除。通过Arrays工具类的asList(Object... a)获得实例。
85.Queue集合:Queue用于模拟队列这种数据结构,队列先进先出,头部保存在队列中存放时间最长的元素,尾部保存在队列中存放时间最短的元素。Queue接口有一个PriorityQueue实现类。除此之外,Queue还有一个Deque接口,Deque代表一个双端队列,双端队列可以同时从#### 两端来添加删除元素,因此Deque的实现类既可以当队列使用也可以当成栈使用。Java为Deque提供了ArrayDeque和LinkedList两个实现类。
88.LinkedList是List和Deque的实现类,与ArrayDeque和ArrayList实现机制不同,它内部以链表形式保存元素,因此随机访问集合元素时性能较差,但是在插入、删除元素时性能出色。而以数组形式实现的集合随机访问元素的性能较好。
92.查看表结构desc 表名;查看建表约束show create 表名;删除约束alter table 表名 drop index street;添加约束alter table [table_name] add constraint [constraint_name] [unique| primary key|foreign key] ([column_name]);
93.主键约束相当于非空约束和唯一约束,即主键约束的列既不允许出现重复值,也不允许出现null值;如果对多列组合建立主键约束,则多列里包含的每一列都不能为空,但只要求这些列组合不能重复。主键列的值可用于唯一的标识表中的一条记录。
99.cross join(交叉连接即笛卡尔积),natural join(自然连接,基于有相同的列名的表作连接,如果没有相同列效果和交叉连接相同),using字句连接(和自然连接相比可以指定连接参考的列,如果指定的列在某张表不存在会报错),on字句连接(on后面跟连接条件),左右全外连接(left、right、full join,连接条件也用on,左外连接把左表不符合条件的行列出,右外连接把右表不符合条件的行列出)。
100.Windows的路径分隔符使用(\),而java程序中的反斜线表示转义字符,所以如果需要在windows的路径下包括反斜线,则应该使用两条反斜线或者直接使用(/)也可以,java程序支持将斜线当成平台无关的路径分隔符。
103.通过在实例变量前面使用transient关键字修饰,可以指定Java序列化时无需理会该实例变量。使用transient修饰的实例变量将被完全隔离在序列化机制之外,这样导致在反序列化恢复Java对象时无法取得该实例变量的值。
104.文件锁控制文件的全部或部分字节的访问,Java提供了FileLock支持文件锁功能。在FileChannel中提供了lock()/tryLock()方法可以获得文件锁FileLock对象,从而锁定文件。两个方法区别:lock()试图锁定某个文件时,如果无法得到文件锁,阻塞线程;而tryLock()是尝试锁定文件,它将直接返回不会阻塞,返回文件锁或者null。
105.如果FileChannel只想锁定文件的部分内容可以使用lock(long position,long size,boolean shared)对文件从position开始,长度为size的内容加锁,阻塞式;tryLock(long position,long size,boolean shared),非阻塞式加锁。当shared为true时,表明该锁是一个共享锁,它将允许多个进程来读取该文件,但阻止其他进程获得对应文件的排它锁。当shared为false,表明该锁是一个排它锁,它将锁住对该文件的读写,可以通过FileLock的isShared来判断它获得的锁是否为共享锁。
108.多线程优势:进程之间不能共享内存,但是线程之间共享内存非常容易。系统创建进程时需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务并发比多进程的效率高。Java语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了Java的多线程编程。
110.Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体,而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法。
111.采用Runnable接口的方式创建的多个线程可以共享线程类的实例变量。这是因为在这种方式下,程序所创建的Runnable对象只是线程的target,而多个线程可以共享同一个target,所以线程可以共享一个线程类(实际上应该是线程的target类)的实例变量。
113.new出一个线程类之后该线程处于新建状态,调用start方法之后处于就绪状态,如果手动调用了线程的run方法,该线程类将被当做普通Java对象调用(单线程的),只能对处于新建状态的线程调用start()方法,否则将引发IllegalTheadStateException异常。
114.如果希望调用子线程的start()方法后子线程立即开始执行,程序可以使用Thread.sleep(1)来让当前运行的线程(主线程)睡眠1毫秒-1毫秒就够了,因为在这1毫秒内CPU不回空闲,它会去执行另一个处于就绪状态的线程,这样就可以让子线程立即开始执行。
116.Thread提供了一个让一个线程等待另一个线程完成的方法-join()方法。当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join()方法加入的join线程执行完为止。
117.调用setDaemon(true)(必须在start方法之前调用)方法可以指定线程设置成后台线程,如果所有前台线程都死亡,后台线程会自动死亡,前台线程创建的子线程默认是前台线程,后台线程创建的子线程默认是后台线程。
119.yield()方法也是Thread类的一个静态方法,可以让正在执行的线程暂停,但不会阻塞该线程,它只是将该线程转入就绪状态。该方法只是让当前线程暂停,让系统的线程调度器重新调度一次,完全有可能还调度到自己。事实上只有优先级比当前线程高或者和当前线程相等且处于就绪状态的线程才有可能执行。
122.线程会在如下几种情况释放对同步监视器的锁定:a、当前进程的同步方法、同步代码块执行结束;b、当前线程在同步方法、同步代码块中遇到break、return终止了执行;c、当前线程在同步代码块、同步方法出现未处理的error或Exception导致异常结束;d、当前线程在执行同步代码块或者同步方法时,程序执行了同步监视器对象的wait()方法,则当前线程暂停,并释放同步监视器。
123.如下情况线程不会释放同步监视器:a、线程执行同步方法或者同步代码块,调用Thread.sleep()、Thread.yield()方法来暂停当前线程的执行,当前线程不会释放同步监视器;b、线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放同步监视器。
124.Object类提供的wait(),notify()和notifyAll()三个方法必须由同步监视器对象来调用,对于有synchronized修饰的同步方法,因为该类的默认实例(this)就是同步监视器,所以可以在同步方法中直接调用这三个方法。对于使用synchronized修饰的同步代码块,同步监视器是synchronized后括号里的对象,所以必须使用该对象调用这个方法。
125.wait():导致当前线程等待,直到其他线程调用该同步监视器的notify()方法或notifyAll()方法来唤醒该线程。wait()方法有三种形式,无参(一直等待,直到其他线程通知),带毫秒参数的wait(),带毫秒微妙参数表示一段时间后释放对该同步监视器的锁定。
126.notify():唤醒在此同步监视器上等待的单个县城。如果所有线程都在此同步监视器上等待,则会选择唤醒其中一个线程。选择是任意性的。只有当前线程放弃对该同步监视器的锁定后(使用wait()方法),才可以执行被唤醒的线程。
130.BlockingQueue:可以作为线程同步工具,当生产者线程试图向BlockingQueue中放入元素时,如果该队列已满,则线程被阻塞;当消费线程试图从BlockingQueue中取出元素时,如果该队列已空,则改程序被阻塞。
131.BlockingQueue继承了Queue接口,可以使用Queue接口中的方法,归纳如下:a.在队列尾部插入元素。包括add(E e)、offer(E e)和put(E e)方法,当该队列已满,这三个方法分别会抛出异常、返回false、素色队列;b.在队列头部删除并返回删除的元素。包括remove()、poll()和take()方法。当该队列已空时,这三个方法分别会抛出异常、返回false、阻塞队列;c.在队列头部取出但不删除元素。包括element()和peek()方法,当队列已空时,这两个方法分别抛出异常、返回false。