避免过度同步(67):在一个被同步的方法或代码块中,不要调用哪些被设计成被覆盖的方法或者是由客户端以函数对象的形式提供的方法(21)。

有点拗口,书上提供的创建者与观察者模式,add方法太多,看得眼花缭乱,重新写了一个例子:

package com.example.demo.effective;

public interface Believer {
void confide();
}
package com.example.demo.effective;

import com.google.common.collect.Lists;

import java.util.List;

/**
* 上帝
*/
public class God {
/**
* 天国
*/
private List<Believer> believers = Lists.newArrayList(); public void addBeliever(Believer be){
synchronized (believers){
believers.add(be);
}
} public void removeBeliever(Believer be){
synchronized (believers){
believers.remove(be);
}
} public void getBeliever(Believer be){
synchronized (believers){
//选一个大主教
System.out.println(believers.get(0));
}
} /**
* 倾听每位信徒倾述
*/
public void listenEveryBeliever(){
synchronized (believers){
for(Believer be : believers){
be.confide();
}
}
} }

测试:

 @Test
public void test1(){
God god = new God();
god.addBeliever(new Believer() {
@Override
public void confide() {
System.out.println("I love my son....");
}
});
god.listenEveryBeliever();
}

I love my son....

没问题,再看下面:

@Test
public void test2(){
God god = new God(); god.addBeliever(new Believer() {
@Override
public void confide() {
System.out.println("I love my son....");
}
}); //现在出现一个亵神者,放弃信仰了
god.addBeliever(new Believer() {
@Override
public void confide() {
god.removeBeliever(this);
}
});
god.listenEveryBeliever();
}

结果,在上帝做倾听信徒时,居然出现了渎神者,大家都知道,集合在循环时,时不可以增减元素的。

在继续看,死锁了....

 @Test
public void test3(){
God god = new God(); god.addBeliever(new Believer() {
@Override
public void confide() {
System.out.println("I love my son....");
}
}); //现在出现一个亵神者,放弃信仰了
god.addBeliever(new Believer() {
@Override
public void confide() {
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
executor.submit(()->{god.getBeliever(this);}).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}finally {
executor.shutdown();
}
}
});
god.listenEveryBeliever();
}

这里死锁,分析下:god.listenEveryBeliever(),主线程锁住天堂,上帝开始倾听信徒倾述,但此时有一个信徒倾述时另启一个线程,并在此等待主线程执行结束,当然永远也等不到....

所以正确的姿势:原则:不要在同步块中,调用可能被覆盖的方法

 /**
* 倾听每位信徒倾述
*/
public void listenEveryBeliever(){ List<Believer> believersBat = null;
synchronized (believers){
//快照副本
believersBat = Lists.newArrayList(believers);
}
for(Believer be : believersBat){
be.confide();
}
}

请不要在新代码中使用原生态类型(23)

jdk1.5版本增加了泛型,如果还是再用低于1.5的jdk,不说了。

泛型的好处:1、表述性 2、安全性(主要体现在编译阶段的泛型检查,有些错误不必要等到运行时才发现)

    @Test
public void test1(){
//运行时才发现类型异常,追悔吧...
List list = Lists.newArrayList("adc");
getFirstEle1(list);//java.lang.String cannot be cast to java.lang.Integer
} @Test
public void test2(){
List list = Lists.newArrayList("adc");
getFirstEle2(list);//adc
} @Test
public void test3(){
//结合test2() List和List<Object>主要区别:
//1、List 时原生态类型,逃避了类型检查。List<Object>则明确告知编译器
//2、List<String> 时List的字类,而不是List<Object>子类
List<String> list = Lists.newArrayList("adc");
//getFirstEle2(list);//编译报错
} @Test
public void test4(){
List<String> list = Lists.newArrayList("adc");
getFirstEle3(list);//adc
} @Test
public void test5(){
List<? extends String> list1 = Lists.newArrayList("adc");
List<String> list2 = Lists.newArrayList("ap"); //<? extends E> 是 Upper Bound(上限) 的通配符,用来限制元素的类型的上限
//list2 元素类型String,list1元素类型继承String,大类型->小类型,报错
//list1.addAll(list2);
list2.addAll(list1);
System.out.println(list2); //[ap, adc]
} @Test
public void test6(){
// <? super E> 是 Lower Bound(下限) 的通配符 ,用来限制元素的类型下限
List<? super String> list1 = Lists.newArrayList("adc");
List<String> list2 = Lists.newArrayList("ap");
list1.addAll(list2);
System.out.println(list1); //[ap, adc]
} /**
* List 原生态类型
* @param list
*/
public static void getFirstEle1(List list){
int i = (int) list.get(0);
System.out.println(i);
} /**
* List<Object> 从表述上,时所有Object元素都ok
* @param list
*/
public static void getFirstEle2(List<Object> list){
Object i = (Object) list.get(0);
System.out.println(i);
} /**
* <?> 可接受任意泛型,多定义在变量中
* @param list
*/
public static void getFirstEle3(List<?> list){
System.out.println(list.get(0));
} /**
* <T> 表示方法要用到泛型参数,多用在类和方法上
* @param list
* @param <T>
*/
public static <T> void getFirstEle4(List<T> list){
T t = list.get(0);
System.out.println(t);
}

私有构造器强化不可实例化

使用场景:通常用在工具类,因为他们都不希望被实例化。

public class MyUtils {
    private MyUtils(){
        //不管外部还是内部,该类实例化就会抛异常
        throw new AssertionError();
    }
    
    public static String method(){
        return "";
    }
}  

枚举类型:

通常程序中需要用到大量的常量,优雅的使用枚举将对代码可读性、健壮性、维护性有显著效果

enum代替int常量,用实例域代替序数

package com.example.demo.effective.enumDemo;

public enum StatusEnum {
APPLY(1),ACTIVE(2),FREEZE(3),FREE(4); private final int index; StatusEnum(int size){
this.index = size;
} public void method(){
//System.out.println(this.ordinal()); 位置顺序变动,会乱套
System.out.println(index);
}
}

用EnumSet代替位域

// num << x,相当于num * 2^x,
//|或运算符:即两个二进制数同位中,只要有一个为1则结果为1,若两个都为1其结果也为1
public static final int STYLE_BOLD = 1 << 0;
public static final int STYLE_ITALIC = 1 << 1;
public static final int STYLE_UNDERLINE = 1 << 2;
public static final int STYLE_STRIKETHROUGH = 1 << 3;
@Test
public void test3(){
System.out.println(STYLE_UNDERLINE|STYLE_STRIKETHROUGH);//
} @Test
public void test4(){
//用EnumSet代替位域
//位域表示法允许利用位操作,有效地执行先 union(联合)和 intersection(交集)这样的集合操作 如:test3()
EnumSet<StatusEnum> status1 = EnumSet.of(StatusEnum.APPLY, StatusEnum.ACTIVE);
System.out.println(status1); //确定,EnumSet可变,用unmodifiableSet 封装
Set<StatusEnum> unmodifiableStyle = Collections.unmodifiableSet(status1);
unmodifiableStyle.add(StatusEnum.FREE); //运行时:UnsupportedOperationException
}

Effective java 系列之避免过度同步和不要使用原生态类型,优先考虑泛型的更多相关文章

  1. Effective Java 第三版——78. 同步访问共享的可变数据

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  2. Effective java 系列之更优雅的关闭资源-try-with-resources

    背景: 在Java编程过程中,如果打开了外部资源(文件.数据库连接.网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们.因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制,如果我们不在 ...

  3. Effective java 系列之异常转译

    异常转译:当位于最上层的子系统不需要关心底层的异常细节时,常见的作法时捕获原始异常,把它转换一个新的不同类型的异常,在将新异常抛出. 通常方法捕获底层异常,然后抛高层异常. public static ...

  4. Effective Java 第三版——89. 对于实例控制,枚举类型优于READRESOLVE

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  5. Effective Java 第三版——79. 避免过度同步

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  6. Effective.Java第78-90条(同步相关)

    78.  同步访问共享的可变数据 为了在线程之间进行可靠的通信,也为了互斥访问,同步是必要的. 不共享可变的数据.要么共享不可变的数据,要么压根不共享.换句话说,将可变数据限制在单线程中. 当多个线程 ...

  7. Effective Java通俗理解(下)

    Effective Java通俗理解(上) 第31条:用实例域代替序数 枚举类型有一个ordinal方法,它范围该常量的序数从0开始,不建议使用这个方法,因为这不能很好地对枚举进行维护,正确应该是利用 ...

  8. 《Effective Java(中文第二版)》【PDF】下载

    <Effective Java(中文第二版)>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382186 Java(中文第二版)& ...

  9. Effective Java 第三版——48. 谨慎使用流并行

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

随机推荐

  1. ORA-16038 ORA-19809 ORA-00312

    问题表现: 连接数据库启动报错,ORA-03113, 查看详细的alert日志发现更多报错,如下 ORA-19809: 超出了恢复文件数的限制ORA-19804: 无法回收 209715200 字节磁 ...

  2. Linux和windows之间使用scp无密码传输文件,脚本自动化

    1.环境 windows2008 R2 和rhel 6.5 2.需求 通过在windows上指定计划任务,自动完成从Linux系统上备份文件到windows 3.工具 win: SSH Secure ...

  3. vue.js not detected 解决办法-vue.js devtools 安装

    国外网站:https://www.crx4chrome.com/ 国内网站:http://www.cnplugins.com/ http://chromecj.com/web-development/ ...

  4. Codeforces 825D Suitable Replacement - 贪心 - 二分答案

    You are given two strings s and t consisting of small Latin letters, string s can also contain '?' c ...

  5. 前端 --- 3 css 属性

    一. 标签嵌套规则 块级标签能够嵌套某些块级标签和内敛标签(行内标签) 内敛标签不能嵌套块级标签,只能嵌套内敛标签 二.   属性 1.宽和高 (块级标签能够设置高度和宽度 内敛标签不能设置,设置了没 ...

  6. git使用操作

    git config --system --unset credential.helper 重置git

  7. 剥开比原看代码03:比原是如何监听p2p端口的

    作者:freewind 比原项目仓库: Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockchai ...

  8. docker 命令2

    docker build -t dvm.adsplatformproxy:v1.0.0 . #build images docker run -e WWNamespace=dev -e ZKServe ...

  9. Caused by: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'zoneId' in 'class java.lang.String'

    本文为博主原创,未经允许不得而转载: 异常展示: dao层定义的接口为: public int getClientTotal(); 在mybatis中的sql为: <select id=&quo ...

  10. ZOJ 3987 Numbers(Java枚举)

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3987 题意:给出一个数n,现在要将它分为m个数,这m个数相加起来必须等于n ...