java杂谈

  • 如果boolean型变量是类变量(static修饰,成员变量也是没有默认值的,但是当类实例化以后成员变量就有了默认值false),则默认值为false.否则没有默认值。
swtich
switch (name) {
            case "城市" : return 0;
            case "景区" : return 1;
            case "商圈" : return 2;
            case "酒店" : return 3;
            case "地点" : return 4;
            case "医院" : return 5;
            case "景点" : return 6;
            case "地铁站" : return 7;
            case "学校" : return 8;
            case "车站" : return 9;
            case "集团" : return 10;
            default: return -1;
        }

以上的swtich语句在转为字节码的时候会成为下面的

byte var3 = -1;
        switch(name.hashCode()) {
        case 699010:
            if (name.equals("商圈")) {
                var3 = 2;
            }
            break;
        case 699015:
            if (name.equals("医院")) {
                var3 = 5;
            }
            break;
        case 720777:
            if (name.equals("地点")) {
                var3 = 4;
            }
            break;
        case 720884:
            if (name.equals("城市")) {
                var3 = 0;
            }
            break;
        case 751995:
            if (name.equals("学校")) {
                var3 = 8;
            }
            break;
        case 834219:
            if (name.equals("景区")) {
                var3 = 1;
            }
            break;
        case 841770:
            if (name.equals("景点")) {
                var3 = 6;
            }
            break;
        case 1169459:
            if (name.equals("车站")) {
                var3 = 9;
            }
            break;
        case 1177477:
            if (name.equals("酒店")) {
                var3 = 3;
            }
            break;
        case 1218780:
            if (name.equals("集团")) {
                var3 = 10;
            }
            break;
        case 22661480:
            if (name.equals("地铁站")) {
                var3 = 7;
            }
        }

        switch(var3) {
        case 0:
            return 0;
        case 1:
            return 1;
        case 2:
            return 2;
        case 3:
            return 3;
        case 4:
            return 4;
        case 5:
            return 5;
        case 6:
            return 6;
        case 7:
            return 7;
        case 8:
            return 8;
        case 9:
            return 9;
        case 10:
            return 10;
        default:
            return -1;
        }

从.class文件可以看出 swtich 对所有的判断想进行了 hash排序,对比下面if语句的字节码。可以粗略得出结论 swtich会优化你的代码。
可以做一个实验

StopWatch stopWatch = new StopWatch("speed");
        stopWatch.start("stwich");
        for (int i = 0; i < 10000; i++) {
            swtich("集团");
        }
        stopWatch.stop();
        stopWatch.start("if");
        for (int i = 0; i < 10000; i++) {
            if("集团");
        }
        stopWatch.stop();
        System.out.println(stopWatch);

测试结果10000的情况下StopWatch ‘speed’: running time (millis) = 3; [stwich] took 1 = 33%; [if] took 2 = 67%
100000的情况下StopWatch ‘speed’: running time (millis) = 21; [stwich] took 5 = 24%; [if] took 16 = 76%
下面是if语句的

if(name.endsWith("城市")) return 0;
		if(name.endsWith("行政区")) return 1;
		if(name.endsWith("景区")) return 2;
		if(name.endsWith("商圈")) return 3;
		if(name.endsWith("酒店")) return 4;
		if(name.endsWith("地点")) return 5;
		if(name.endsWith("医院")) return 6;
		if(name.endsWith("学校")) return 7;
		if(name.endsWith("景点")) return 8;
		if(name.endsWith("地铁站")) return 9;
		if(name.endsWith("机场")) return 10;
		if(name.endsWith("车站")) return 10;
		if(name.endsWith("品牌")) return 12;
		if(name.endsWith("集团")) return 13;
		return -1;

.class文件的代码如下

 if (name.endsWith("城市")) {
            return 0;
        } else if (name.endsWith("行政区")) {
            return 1;
        } else if (name.endsWith("景区")) {
            return 2;
        } else if (name.endsWith("商圈")) {
            return 3;
        } else if (name.endsWith("酒店")) {
            return 4;
        } else if (name.endsWith("地点")) {
            return 5;
        } else if (name.endsWith("医院")) {
            return 6;
        } else if (name.endsWith("学校")) {
            return 7;
        } else if (name.endsWith("景点")) {
            return 8;
        } else if (name.endsWith("地铁站")) {
            return 9;
        } else if (name.endsWith("机场")) {
            return 10;
        } else if (name.endsWith("车站")) {
            return 10;
        } else if (name.endsWith("品牌")) {
            return 12;
        } else {
            return name.endsWith("集团") ? 13 : -1;
        }

https://blog.csdn.net/qq_34337272/article/details/80012284

concurrenthashmap 分成N个hashtable 默认16 每个hashtable 加锁
hashmap是线程不安全的
ThreadPoolExecutor.AbortPolicy()-Always throws RejectedExecutionException.*直接抛异常
ThreadPoolExecutor.CallerRunsPolicy() -Executes task r in the caller’s thread, unless the executor has been shut down, in which case the task is discarded.*主线程执行 即调用线程执行
ThreadPoolExecutor.DiscardOldestPolicy() -Obtains and ignores the next task that the executor would otherwise execute, if one is immediately available,and then retries execution of task r, unless the executor is shut down, in which case task r is instead discarded.*丢弃队列中最早的任务,知道新的任务挤进队列
ThreadPoolExecutor.DiscardPolicy() -Does nothing, which has the effect of discarding task r.*直接丢弃

war 和 warexploded
war模式:将WEB工程以包的形式上传到服务器 ;
war exploded模式:将WEB工程以当前文件夹的位置关系上传到服务器;

(1)war模式这种可以称之为是发布模式,看名字也知道,这是先打成war包,再发布;
(2)war exploded模式是直接把文件夹、jsp页面 、classes等等移到Tomcat 部署文件夹里面,进行加载部署。因此这种方式支持热部署,一般在开发的时候也是用这种方式。
(3)在平时开发的时候,使用热部署的话,应该对Tomcat进行相应的设置,这样的话修改的jsp界面什么的东西才可以及时的显示出来。

list的深度拷贝

克隆方法1:利用原list作为参数直接构造方法生成。
克隆方法2:手动遍历将原listString0中的元素全部添加到复制表中。
克隆方法3:调用Collections的静态工具方法 Collections.copy
以上方法再list的元素为基础类型时候是没有问题的,但是一旦存放的是对象类型,咋不能实现深度拷贝,因为存的是对象的地址,只是拷贝出一个新的对象,指向原来的地址。
真的深度拷贝:可以转为流,然后再转回去。

@SuppressWarnings("unchecked")
public static <T> List<T> deepCopyList(List<T> dest, List<T> src) throws IOException, ClassNotFoundException {
    ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
    ObjectOutputStream out = new ObjectOutputStream(byteOut);
    out.writeObject(src);
    ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
    ObjectInputStream in = new ObjectInputStream(byteIn);
    dest = (List<T>) in.readObject();
    return dest;
}
null

对null进行类型强转不会报空指针,但是如果转为基础类型会报空指针,因为反编译后会发现调用intValue方法去获取value,
就是null.intValue.

设计模式

总体来说设计模式分为三大类:

  • 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
  • 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
  • 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

设计模式的六大原则

  • 1、开闭原则(Open Close Principle)
    开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。
  • 2、里氏代换原则(Liskov Substitution Principle)
    里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里 氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现 抽象化的具体步骤的规范。—— From Baidu 百科
  • 3、依赖倒转原则(Dependence Inversion Principle)
    这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。
  • 4、接口隔离原则(Interface Segregation Principle)
    这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。
  • 5、迪米特法则(最少知道原则)(Demeter Principle)
    为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。
  • 6、合成复用原则(Composite Reuse Principle)
    原则是尽量使用合成/聚合的方式,而不是使用继承。

设计模式

  • 单例模式(Singleton)
a.懒汉模式
public class Singleton {

    private volatile static Singleton instance = null; //声明成 volatile 保证编译器不进行优化
    
    private Singleton (){}

    public static Singleton getSingleton() {
        if (instance == null) {                         
            synchronized (Singleton.class) {
                if (instance == null) {       
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }   
}
b.饿汉模式
public class Singleton{
    //类加载时就初始化
    private static final Singleton instance = new Singleton();
    
    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}

LUNIX

Xshell操作指令
tail -f 实时查日志
pwd 当前目录路径
sz 把文件拷贝出来
vi 查看文件
top –c 显示进程运行信息列表 再按P 按照cpu使用率排序
top –Hp pid号 显示进程的线程信息列表 P 排序
jstack –l pid号 > ./pid号.stack 导出进程快照
cat pid号.stack |grep ‘bda’ –C8 bda 线程pid转为16进制后的值

netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’ 查看服务器性能情况
TCP/IP
CLOSE_WAIT 等待从客户端发来的中断请求(如果过多是因为自己没有释放连接)
ESTABLISHED 当前连接数
FIN_WAIT 等待服务器的连接中断请求或者中断请求的确认
netstat -nat|awk ‘{print awk $NF}’|sort|uniq -c|sort -n分析FIN_WAIT过多的原因
TIME_WAIT 确保服务器接收到了连接中断的请求(保持了2MSL-max segment lifetime)
如果time_wait过多可以更改自己的服务器参数,/etc/sysctl.conf
#对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃,不应该大于255,默认值是5,对应于180秒左右时间
net.ipv4.tcp_syn_retries=2
#net.ipv4.tcp_synack_retries=2
#表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒
net.ipv4.tcp_keepalive_time=1200
net.ipv4.tcp_orphan_retries=3
#表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间
net.ipv4.tcp_fin_timeout=30
#表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_max_syn_backlog = 4096
#表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭
net.ipv4.tcp_syncookies = 1

#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
net.ipv4.tcp_tw_reuse = 1
#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭
net.ipv4.tcp_tw_recycle = 1

##减少超时前的探测次数
net.ipv4.tcp_keepalive_probes=5
##优化网络设备接收队列
net.core.netdev_max_backlog=3000
修改完之后执行/sbin/sysctl -p让参数生效。
net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle的开启都是为了回收处于TIME_WAIT状态的资源。
net.ipv4.tcp_fin_timeout这个时间可以减少在异常情况下服务器从FIN-WAIT-2转到TIME_WAIT的时间。
net.ipv4.tcp_keepalive_*一系列参数,是用来设置服务器检测连接存活的相关配置。
修改方法:
sudo vi /etc/sysctl.conf
增加如下:
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.ip_local_port_range = 10000 65000
net.ipv4.tcp_max_syn_backlog = 8192
net.ipv4.tcp_max_tw_buckets = 10000
sudo /sbin/sysctl -p
实时生效

在这里插入图片描述
常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。

客户端TCP状态迁移:
CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
服务器TCP状态迁移:
CLOSED->LISTEN->SYN收到 ->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED

当客户端开始连接时,服务器还处于LISTENING,
客户端发一个SYN包后,他就处于SYN_SENT状态,服务器就处于SYS收到状态,
然后互相确认进入连接状态ESTABLISHED.

当客户端请求关闭连接时,客户端发送一个FIN包后,客户端就进入FIN_WAIT_1状态,等待对方的确认包,
服务器发送一个ACK包给客户,客户端收到ACK包后结束FIN_WAIT_1状态,进入FIN_WAIT_2状态,等待服务器发过来的关闭请求,
服务器发一个FIN包后,进入CLOSE_WAIT状态,
当客户端收到服务器的FIN包,FIN_WAIT_2状态就结束,然后给服务器端的FIN包给以一个确认包,客户端这时进入TIME_WAIT,
当服务器收到确认包后,CLOSE_WAIT状态结束了,
这时候服务器端真正的关闭了连接.但是客户端还在TIME_WAIT状态下,

什么时候结束呢.我在这里再讲到一个新名词:2MSL等待状态,其实TIME_WAIT就是2MSL等待状态,
为什么要设置这个状态,原因是有足够的时间让ACK包到达服务器端,如果服务器端没收到ACK包,超时了,然后重新发一个FIN包,直到服务器收到ACK 包.

TIME_WAIT状态等待时间是在TCP重新启动后不连接任何请求的两倍.
大家有没有发现一个问题:如果对方在第三次握手的时候出问题,如发FIN包的时候,不知道什么原因丢了这个包,然而这边一直处在FIN_WAIT_2状 态,而且TCP/IP并没有设置这个状态的过期时间,那他一直会保留这个状态下去,越来越多的FIN_WAIT_2状态会导致系统崩溃.
1. CLOSE_WAIT过多
CLOSE_WAIT,TCP的癌症,TCP的朋友。
CLOSE_WAIT状态的生成原因
首先我们知道,如果我们的服务器程序APACHE处于CLOSE_WAIT状态的话,说明套接字是被动关闭的!
因为如果是CLIENT端主动断掉当前连接的话,那么双方关闭这个TCP连接共需要四个packet:
Client —> FIN —> Server
Client <— ACK <— Server
这时候Client端处于FIN_WAIT_2状态;而Server 程序处于CLOSE_WAIT状态。
Client <— FIN <— Server
这时Server 发送FIN给Client,Server 就置为LAST_ACK状态。
Client —> ACK —> Server
Client回应了ACK,那么Server 的套接字才会真正置为CLOSED状态。

Server 程序处于CLOSE_WAIT状态,而不是LAST_ACK状态,说明还没有发FIN给Client,那么可能是在关闭连接之前还有许多数据要发送或者其 他事要做,导致没有发这个FIN packet。
通常来说,一个CLOSE_WAIT会维持至少2个小时的时间。如果有个流氓特地写了个程序,给你造成一堆的 CLOSE_WAIT,消耗你的资源,那么通常是等不到释放那一刻,系统就已经解决崩溃了。
只能通过修改一下TCP/IP的参数,来缩短这个时间:修改tcp_keepalive_*系列参数有助于解决这个 问题。

解决这个问题的方法是修改系统的参数,系统默认超时时间的是7200秒,也就是2小时, 这个太大了,可以修改如下几个参数:
sysctl -w net.ipv4.tcp_keepalive_time=30
sysctl -w net.ipv4.tcp_keepalive_probes=2
sysctl -w net.ipv4.tcp_keepalive_intvl=2
然后,执行sysctl命令使修改生效。
超时后服务端错误为:java.net.SocketException: Connection reset
客户端错误为:java.net.SocketTimeoutException: Read timed out

Redisson

之前使用setnx没能解决问题,现在使用Redisson
Redisson 功能和jedis类似,封装了连接操作,但没有jedis全面。要使用Redisson的分布式锁需要redis支持eval,因为Redisson的分布锁的实现中使用了lua脚本:加锁
下面参数的含义先说明下 :
KEYS[1] :需要加锁的key,这里需要是字符串类型。ARGV[1] :锁的超时时间,防止死锁ARGV[2] :锁的唯一标识,也就是刚才介绍的

id(UUID.randomUUID()) + “:” + threadId// 检查是否key已经被占用,如果没有则设置超时时间和唯一标识,初始化value=1
if (redis.call('exists', KEYS[1]) == 0) 
then  
redis.call('hset', KEYS[1], ARGV[2], 1); 
redis.call('pexpire', KEYS[1], ARGV[1]);  
return nil; 
end; 
// 如果锁重入,需要判断锁的key field 都一直情况下 value 加一
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) 
then 
redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);//锁重入重新设置超时时间
return nil; 
end; 
// 返回剩余的过期时间
return redis.call('pttl', KEYS[1]);

解锁
也还是先说明一下参数信息:
– KEYS[1] :需要加锁的key,这里需要是字符串类型。
– KEYS[2] :redis消息的ChannelName,一个分布式锁对应唯一的一个channelName:“redisson_lock__channel__{” + getName() + “}”
– ARGV[1] :reids消息体,这里只需要一个字节的标记就可以,主要标记redis的key已经解锁,再结合redis的Subscribe,能唤醒其他订阅解锁消息的客户端线程申请锁。
– ARGV[2] :锁的超时时间,防止死锁
– ARGV[3] :锁的唯一标识,也就是刚才介绍的 id(UUID.randomUUID()) + “:” + threadId

if (redis.call('exists', KEYS[1]) == 0) 
then
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
end;
// key和field不匹配,说明当前客户端线程没有持有锁,不能主动解锁。
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0)
then 
return nil;
end; 
// 将value减1
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); 
// 如果counter>0说明锁在重入,不能删除key
if (counter > 0)  
then
redis.call('pexpire', KEYS[1], ARGV[2]);                             return 0; 
else 
// 删除key并且publish 解锁消息
redis.call('del', KEYS[1]);                            redis.call('publish', KEYS[2], ARGV[1]);
return 1; 
end; 
return nil;

// 如果key已经不存在,说明已经被解锁,直接发布(publihs)redis消息

具体见博文:http://blog.jobbole.com/99751/

待续。。。。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值