NTP

SNTP是简单网络时间协议(Simple Network Time protocol)的简称,它是目前Internet网上实现时间同步的一种重要工程化方法。
SNTP协议采用客户/服务器工作方式,服务器通过接收GPS信号或自带的原子钟作为系统的时间基准,客户机通过定期访问服务器提供的时间服务获得准确的时间信息,并调整自己的系统时钟,达到网络时间同步的目的。客户和服务器通讯采用UDP协议,端口为123。

验证ntp服务:ntpq -p
查看当前时间:date -R

查找当前地区最适合的时间服务器:打开网站 http://www.pool.ntp.org/zone/asia,会在页面上部推荐一组最合适的同步服务器。在配置ntp服务时可以把它们复制粘贴到etc/ntp.conf中。

对时请求时设置超时时间在1秒内,超时即对时不成功,保证时间精度。
https://blog.csdn.net/hexiangqwqwqw/article/details/49801573
CentOS下NTP时间服务器搭建
http://blog.51cto.com/superpcm/2089888
Centos7配置ntp时间服务器
https://blog.csdn.net/zzy5066/article/details/79036674
docker 搭建ntp服务器
https://www.cnblogs.com/liubin0509/p/6282858.html

java实现NTP客户端

添加依赖commons-net

<!-- https://mvnrepository.com/artifact/commons-net/commons-net -->
<dependency>
    <groupId>commons-net</groupId>
    <artifactId>commons-net</artifactId>
    <version>3.6</version>
</dependency>

获取时间

String getDateTime(){
        try {
            NTPUDPClient timeClient = new NTPUDPClient();
            InetAddress timeServerAddress = InetAddress.getByName("cn.pool.ntp.org");
            TimeInfo timeInfo = timeClient.getTime(timeServerAddress);
            TimeStamp timeStamp = timeInfo.getMessage().getTransmitTimeStamp();
            DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return dateFormat.format(timeStamp.getDate());
        } catch (UnknownHostException e) {
            e.printStackTrace();
            System.out.println("与ntp服务器同步时间错误!");
            return dateFormat.format(new Date());
        } catch (IOException e) {
            System.out.println("与ntp服务器同步时间错误!");
            return dateFormat.format(new Date());
        }
    }

Java基础

命令行运行java

jar包都放在lib文件夹里

java -Djava.ext.dirs=lib Main

ubuntu 安装jdk

JDK是java的开发环境,它包括了运行环境(jre)和开发中需要的编译、调试、程序诊断。
很多软件已不需要通过环境变量JAVA_HOME来找到java的运行环境。
http://www.cnblogs.com/a2211009/p/4265225.html

jre是提供java运行环境而不是开发环境
OpenJDK 和 Sun JDK 的关系

sudo apt-get install openjdk-8-jdk

Centos安装OpenJDK

#yum搜索OpenJDK8软件包 
yum search java-1.8.0|grep openjdk 
#安装java-1.8.0-openjdk.i686
yum install java-1.8.0-openjdk.x86_64 -y 

Map和List的简单构建方式

new HashMap(){{
            put("saleState",saleState);
            put("serveType",serveType);
        }}

new ArrayList<List>(){{
add(masterSocketDeviceIds);
add(dimmingLightDeviceIds);

Array转List
List list= Arrays.asList(strArr);
}};

Java没有范型数组

枚举

ordinal()返回的值是索引,value()返回的是给枚举变量赋予的值。

public enum EnumDeviceType {
    Unknow(0),
    MasterSocket(1),
    SlaveSocket(2),
    DoorbellController(42),
    PowerControl(43);

    private int value = 0;

    private EnumDeviceType(int value) {
        this.value = value;
    }

    public int value() {
        return value;
    }
}

byte

char与byte:
byte是8位的一个字节,char是UNICOEDE字符,是16位的整数;

java中的byte、int等都是有符号的,取byte的无符号数只要进行&0xff操作即可:

public static int[] toUbytes(byte[] bytes){
        int[] ints=new int[bytes.length];
        for(int i=0;i<bytes.length;i++){
            ints[i] = bytes[i] & 0xff;
        }
        return ints;
    }

public static String bytes2HexString(byte[] bytes) {
        String ret="";
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            ret+=hex;
        }

        return ret.toUpperCase();
    }

Java字段反射+POI示例:
http://blog.csdn.net/panpan96/article/details/76566475

Base64

BASE64Encoder/BASE64Decoder类在sun.misc包中,是sun公司的内部方法,以后有删除的潜在可能,
Base64类在java.util包中,推荐使用Base64.getEncoder()。

split

String[] subs=str.split(“,”,-1);//limit -1 不忽略两端的分隔符。

java -jar 命令行传参并获取的方法

方式一:
java -jar xxx.jar aaa bbb cccc,传了3个参数,通过main方法的参数获取。
方式二:
java -jar xxx.jar -Da1=aaa -Db1=bbb -Dc1=ccc,通过System.getProperty(“aaa”,”1″); 方式获取。作为环境变量。
方式三:
java -jar xxx.jar –a1=aaa –b1=bbb,springboot的写法,可以通过@Value(“${a1}”)获取。
注意参数的位置以及中划线符号的数目:
jar、Dspring.profiles.active前面是一个中划线,db.url等自定义参数前面是2个中划线。
-Dspring.profiles.active位于-jar之前,–db.url参数位于.jar之后。

java -Dspring.profiles.active=dev -jar yirui_clock_storage-1.0.jar --db.url=localhost --db.port=14000 --db.username=数据库用户名 --db.password=数据库密码

spring.profiles.active运行时指定方式

mvn命令运行:mvn spring-boot:run -Drun.profiles=test
命令行运行jar文件: java -jar -Dspring.profiles.active=test xxx.jar

Java对象的地址

对象的hashcode是否是它的地址,这取决于具体的实现,一般来说,它常常是对象的初始地址的整数表示,但是这种实现技术并不是Java编程语言所规定的。在GC后,对象的地址可能会变动,不同的JVM实现也有差异。

java知识网站

http://www.java1234.com

JVM

JVM

JVM,Java Virtual Machine Java虚拟机。JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

Java语言的一个非常重要的特点就是与平台的无关性,使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码,Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。
所以,跨平台的是Java程序,不是JVM。JVM是用C/C++开发的,是编译后的机器码,不能跨平台,不同平台下需要安装不同版本的JVM。

http://www.mamicode.com/info-detail-1028149.html

HotSpot VM

HotSpot VM是绝对的主流的JVM。大家用它的时候很可能就没想过还有别的选择,或者是为了迁就依赖了Oracle/Sun JDK某些具体实现的烂代码而选择用HotSpot VM省点心。Oracle / Sun JDK、OpenJDK的各种变种(例如IcedTea、Zulu),用的都是相同核心的HotSpot VM。从Java SE 7开始,HotSpot VM就是Java规范的“参考实现”(RI,Reference Implementation)。把它叫做“标准JVM”完全不为过。当大家说起“Java性能如何如何”、“Java有多少种GC”、“JVM如何调优”云云,经常默认说的就是特指HotSpot VM。可见其“主流性”。JDK8的HotSpot VM已经是以前的HotSpot VM与JRockit VM的合并版,也就是传说中的“HotRockit”,只是产品里名字还是叫HotSpot VM。不过要留意的是,这里我说的HotSpot VM特指“正常配置”版,而不包括“Zero / Shark”版。Wikipedia那个页面上把后者称为“Zero Port”。用这个版本的人应该相当少,很多时候它的release版都build不成功…

JVM分代垃圾回收

分代回收是目前比较先进的垃圾回收方案,代垃圾回收采用分而治之的思想,把不同生命周期的对象放在不同代上,不同代上采用最适合它的垃圾回收方式进行回收。虚拟机中共划分为三个代:

  • 年轻代(Young Generation):所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。
  • 年老代(Old Generation):在年轻代中经历了N次垃圾回收后仍然存活的对象会被放到年老代中,可以认为年老代中存放的都是一些生命周期较长的对象。
  • 持久代(Permanent Generation):也称为方法区、存储已经被虚拟机加载的类信息、常量、静态变量、JIT编译后的代码等数据。Java 7 已经把字符串常量池移动到了堆中,在调用 String 的 intern方法时,如果堆中存在相同的字符串对象,则会直接保存对象的引用,不会重新创建对象。

什么情况下触发垃圾回收?

由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GC和Full GC。

Scavenge GC(搜寻)
一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。

Full GC
对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比ScavengeGC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:
· 年老代(Tenured)被写满
· 持久代(Perm)被写满
· System.gc()被显示调用

从JDK1.2版本开始,把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速JVM对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。

https://my.oschina.net/ydsakyclguozi/blog/404389

Java调优之jvm和线程的内存分析

在内存小的机器上经常出现的问题就是Cann’t allocate memory和OutOfMemoryError错误。在jvm内存调整过程中,我们经常使用的参数有:
-Xms 为jvm启动时分配的堆内存,比如 -Xms200m 表示分配200M。
-Xmx 为jvm运行过程中分配的最大堆内存,比如 -Xms500m 表示jvm进程最多只能够占用500M内存。
-Xss 为jvm启动的每个线程分配的栈内存大小,默认JDK1.4中是256K,JDK1.5+中是1M。
-Xmn 年轻代大小。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。比如-Xmn2g设置年轻代大小为2G。

一般jvm出现Cannt’ allocate memory的错误是机器的内存不够,导致系统无法为jvm分配给定的内存,这个在启动时犹为突出,所以会在启动参数中设置-Xms来指定。
OutOfMemoryError错误则一般会在系统运行一段情况后出现,绝大部分也是机器内存不够或是JVM本身的内存空间已被用尽,这时就要根据情况进行调整:如果是JVM本身的内存空间用尽,则需要调整-Xmx参数来分类jvm的可用内存,如果是机器内存不够则要增加内存或是调优程序。

Xms、Xmx主要是来设置jvm的最小可用内存和最大可用内存,属于进程级别的内存控制。在java中每个线程需要分配线程内存,用来存储自身的线程变量,在java中每new一个线程,jvm都是向操作系统请求new一个本地线程,此时操作系统会使用剩余的内存空间来为线程分配内存,而不是使用jvm的内存。这样,当操作系统的可用内存越少,则jvm可用创建的新线程也就越少。操作系统对一个进程内的线程数是有限制的,不能无限生成。经验值在3000~5000左右。

Server端-Xms值最好设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存,同时最好让-Xmn值约等于-Xmx的1/3。
增加Heap的大小虽然会降低GC的频率,但也增加了每次GC的时间。GC运行时所有的用户线程将暂停,也就是GC期间Java应用程序不做任何工作。
Heap大小并不决定进程的内存使用量。进程的内存使用量要大于-Xmx定义的值,因为Java为其他任务分配内存,例如每个线程的Stack。

运行时配置内存

当内存申请出现问题时,会出现错误日志:
Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x00000000f7d00000, 106430464, 0) failed; error=’Cannot allocate memory’ (errno=12)。
运行时配置最小运行内存、最大运行内存:java -Xms64m -Xmx512m -jar xxx.jar
-Xss:指定每个线程的栈大小。Xss越大,每个线程占用的内存越多, 总线程数就越少;Xss越小,则递归的深度越小,容易出现栈溢出StackOverflowError 问题。适量减少局部变量的声明,可以节省栈帧大小,增加调用深度。

https://stackoverflow.com/questions/31002612/java-hotspottm-64-bit-server-vm-warning

JAVA_OPTS设置 https://blog.csdn.net/kongls08/article/details/8468713

垃圾回收基础
https://segmentfault.com/a/1190000004638653

JMM

JMM:java memory model,java内存模型。
在多线程环境下,线程之间通信开发需要了解JMM(java内存模型) 。在JVM内部使用的java内存模型(JMM)将线程堆栈和堆之间的内存分开。

Spring-AOP

定义装备

定义切点

Spring中表示方式切点常用的有两种方式:1.使用正则表达式 2.使用AspectJ表达式
使用org.springframework.aop.support.JdkRegexpMethodPointcut来定义正则表达式切点

<bean id="sleepPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
 <property name="pattern" value=".*sleep"/>
</bean>

上面示例中pattern属性指定了正则表达式,它匹配所有的sleep方法

定义顾问

把通知和切点结合起来:
org.springframework.aop.support.DefaultPointcutAdvisor

<bean id="sleepHelperAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
 <property name="advice" ref="sleepHelper"/>
 <property name="pointcut" ref="sleepPointcut"/>
</bean>

定义代理对象

<bean id="humanProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
     <property name="target" ref="human"/>
     <property name="interceptorNames" value="sleepHelperAdvisor" />
     <property name="proxyInterfaces" value="test.spring.aop.bean.Sleepable" />
</bean>

Spring还提供了一种自动代理的功能,不用显示定义代理对象,能让通知者和业务对象自动匹配,如下:

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>

http://blog.csdn.net/udbnny/article/details/5870076
http://www.jb51.net/article/83173.htm

AOP概念

AOP是Aspect Oriented Programming的缩写,意思是面向切面(方面)编程。
主要的功能有日志记录,性能统计,安全控制,事务处理,异常处理等,主要意图是将功能代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非主导业务逻辑的方法中,进而改变这些行为的时候不影响主业务逻辑的代码。
AOP是其实是通过动态代理模式来实现业务逻辑的插入,使开发者在开发时不用关注其他与业务无关的点,通过代理的方式做到了插拔式操作。

AOP,OOP区别概念

  • AOP是OOP的补充
  • OOP编程将程序分解成各个层次的对象,AOP将程序运行过程分解成各个切面。
  • AOP从程序运行角度考虑程序的结构,提取业务处理过程的切面,
  • OOP是静态的抽象,AOP是动态的抽象,
  • AOP是对应用执行过程中的步骤进行抽象,从而获得步骤之间的逻辑划分。

AOP框架具有的两个特征:
1.各个步骤之间的良好隔离性
2.源代码无关性

IOC概念

IoC,(Inverse of Control)控制反转,其包含两个内容:其一是控制,其二是反转。在程序中,被调用类的选择控制权从调用它的类中移除,转交给第三方裁决。这个第三方比如Spring容器。IoC另解,依赖注入(Dependency Injection),调用类对被调用类的依赖关系由第三方注入。
IoC其实是遵循了软件设计理念的依赖倒转原则,面向对象的设计就是为了实现软件的更好的复用行和扩展性,我们就必须降低我们每个pojo的依赖关系,也就是解耦,耦合度低了,才能更好的重用和扩展。
IoC中用到了工厂模式
若不是所有操作都要求有相同的效应,则要考虑是否使用切面。

WebService

JAX-WS全称是JavaTM API forXML-Based WebServices
JAX-RS全称是JavaTM APIforRESTful Web Services
JAX-WS是针对WebService,JAX-RS是针对RESTful HTTP Service
这是两种风格的SOA架构风格:
JAX-WS以动词为中心,指定的是每次执行函数,大力支持的厂商如BEA,IBM,MS基本都是开发工具厂商
JAX-RS以名词为中心,每次执行的时候指的是资源,大力支持的厂商如Google,Yahoo,亚马孙等都是服务运营厂商

soap rest 区别
http://www.cnblogs.com/zhangchenglzhao/p/3728455.html

相对来说,REST是一种轻量级架构。
以前旧的基于方法的Web Service都是属于JAX-WS,现在较新的RESTful的Web Service则是属于JAX-RS。其中Jersey是SUN的JAX-RS的参考实现,RESTeasy则是Jboss的JAX-RS的实现。RESTful的Web Service简单来说就是将所有东西看作资源,每个资源都有一个URI,对资源的CRUD操作借助于HTTP协议的POST、GET、PUT、DELETE来实现。效率比旧的基于方法的Web Service要高。

开源WebService框架
CXF、Axis,SpringRMI

组播

组播 MulticastSocket 广播学习
http://hbiao68.iteye.com/blog/1943354
Java IP组播:MulticastSocket
http://m.blog.csdn.net/CorozoNut/article/details/71515455

在单播模式中有服务器端和客户端之分,而组播模式中每个端都是以路由器或交换机做为中转广播站,任意一端向路由器或交换机发送消息,路由或交换机负责发送消息到其他节点,每个节点都是同等的。所以在编程模式上可以用同一个类表示:MulticastSocket。

InetAddress和SocketAddress的区别
InetAddress 只有IP没有端口,InetAddress group = InetAddress.getByName(“localhost”);
InetSocketAddress IP+端口,SocketAddress remoteAddr=new InetSocketAddress(“localhost”,8000);

NetworkInterface 网络接口类
网络接口名是用于标识物理或逻辑网络接口的名字,通常是由操作系统设置的。网络接口名在多数操作系统上常以eth开头,后面是网络接口的索引號,从0開始。如本机安了三块网卡,那么网络接口名就依次是eth0、eth1和eth2。每一个网络接口都能够绑定一个ip地址,也能够据此得到设备的MAC地址。

广播(broadcast),组播(multicast),单播(unicast)的Java实现
http://blog.onlycatch.com/post/%E5%B9%BF%E6%92%AD%EF%BC%88broadcast-%EF%BC%8C%E7%BB%84%E6%92%AD-multicast-%EF%BC%8C%E5%8D%95%E6%92%AD-unicast-%E7%9A%84Java%E5%AE%9E%E7%8E%B0

持续接收http消息

       
try {
            URL realUrl = new URL(url);
            HttpURLConnection connection = (HttpURLConnection)realUrl.openConnection();
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.connect();
            if(connection.getResponseCode()==200){
                InputStream is=connection.getInputStream();
                //ByteArrayOutputStream baos=new ByteArrayOutputStream();
                //10MB的缓存
                byte [] buffer=new byte[10485760];
                int len=0;
                while((len=is.read(buffer))!=-1){
                    //baos.write(buffer, 0, len);
                    System.out.print("len:" + len + " " + new String(buffer, 0, len));
                }
                //String jsonString=baos.toString();
                //baos.close();
                is.close();
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException ex) {
           ex.printStackTrace();
        }

服务器上组播网络设置过程:
/etc/network/interfaces中
外网网卡p3p1 设置为dhcp时
eth0 gateway 配置 打开时能收到message,但上不了外网。
eth0 gateway 配置 关闭时不能收到message,但能上外网。

初步测试是gateway影响的,组播有效和上外网有效中,只能配置一个gateway选择。同时配置两个gateway,能上外网、组播无效。

Hibernate

load与get

load(Object, Serializable):根据id查询 。查询返回的是代理对象,不会立刻访问数据库,是懒加载的。当真正去使用对象的时候才会访问数据库。
load()的时候会发现不会打印出查询语句,而使用get()的时候会打印出查询语句。
使用load()时如果在session关闭之后再查询此对象,会报异常:could not initialize proxy – no Session。处理办法:在session关闭之前初始化一下查询出来的对象:Hibernate.initialize(entity);

load()默认支持延迟加载,在用到对象中的其他属性数 据时才查询数据库,但是万一数据库中不存在该记录,只能抛异常ObjectNotFoundException;所说的load方法抛异常是指在使用该对象的数据时,数据库中不存在该数据时抛异常,而不是在创建这个对象时。由于session中的缓存对于hibernate来说是个相当廉价的资源,所以在load时会先查一下session缓存看看该id对应的对象是否存在,不存在则创建代理。
get()先在一级缓存找,没有就去二级缓存找,再没有就去数据库找,最后若没有就返回null ;而对于get方法,hibernate一定要获取到真实的数据,否则返回null。

TCP服务器

可选的TCP服务器有Mina、Netty、Twisted等,它们都是异步、事件驱动(asynchronous、event-driven)的网络编程框架。
其中MINA和Netty是基于Java语言的,Twisted是Python语言的。语言不是重点,重点的是理念。

  • 传统的BIO(Blocking IO/阻塞IO)进行网络编程时,进行网络IO读写时都会阻塞当前线程,使用BIO实现一个TCP服务器,需要对每个客户端连接开启一个线程,很多线程可能会阻塞住等待读写数据,系统资源消耗大。
  • NIO(Non-Blocking IO/非阻塞IO)或AIO(Asynchronous IO/异步IO)通过IO多路复用技术实现,不需要为每个连接创建一个线程,其底层实现使用了操作系统的一些特性如select、pool、epoll、iocp等。

Mina、Netty、Twisted一起学(一):实现简单的TCP服务器
Netty 4.x 用户指南
Netty in Action 学习
一起学Netty
netty(一) java NIO

同步阻塞I/O

Java的两个IO方法 read和write都是同步阻塞的。
阻塞指的是read和write都会阻塞线程直到读取或发送完成或异常。
同步指的是发送方和接收方是同步的,缓慢的发送和网络传输会使接收长时间阻塞,缓慢的接收和网络传输和狭小的TCP缓冲区也会使发送长时间阻塞,所以发送和接收的速度互相影响。
输入流和输出流的读和写操作都是同步阻塞的,阻塞的时间取决于对方I/O线程的处理速度和网络I/O的传输速度。

Java NIO

JDK1.4引入了新的输入/输出(NIO)库。相比jdk1.3,NIO弥补了原来同步阻塞I/O的不足,提供了高速的、面向块的I/O。
一些NIO新增的类库和概念:
1.缓冲区Buffer
在面向流的I/O中,可以将数据直接写入或读到Stream对象中。
在NIO库中,数据读写都是要经过缓冲区。缓冲区实质上是一个数组,并且一个缓冲区不仅仅是一个数组,它还提供了对数据的结构化访问以及维护读写位置(limit)等信息。
最常用的缓冲区是ByteBuffer,一个ByteBuffer提供了一组功能用于操作byte数组。包括ByteBuffer,每一种java基本类型(除了Boolean类型)都对应一种缓冲区:
ByteBuffer:字节缓冲区
CharBuffer:字符缓冲区
ShortBuffer:短整型缓冲区
IntBuffer:整形缓冲区
LongBuffer:长整型缓冲区
FloadBuffer:浮点型缓冲区
DoubleBuffer:双精度浮点型缓冲区
2.通道Channel
通道与流的不同之处在于通道是双向的,可以用于读、写或者二者同时进行,流只是在一个方向上移动(一个流必须是InputStream或者OutputStream的子类)。
因为Channnel是双全工的,所以它可以比流更好地映射底层操作系统的API。在UNIX网络编程模型中,底层操作系统的通道都是双全工的,同时支持读写操作。
实际上Channel可以分为两大类:用于网络读写的SelectbleChannel和用于文件操作的FileChannel。
与Socket类和ServerSocket类相对应,NIO提供了SocketChannel和ServerSocketChannel两种不同的套接字通道实现,它们都是SelectableChannel的子类,都支持阻塞和非阻塞两种模式。
3.多路复用器Selector
多路复用器提供选择已经就绪的任务的能力。Selector会不断地轮询注册在其上的Channel,如果某个Channel上面发生读写事件,这个Channel就处于了就绪状态,处于了就绪状态的Channel会被Selector轮询出来,然后通过SelectionKey可以读取就绪Channel集合,进行后续的I/O操作。
JDK在Linux等主流操作系统上通过epoll实现,所以它并没有最大连接句柄1024/2048的限制。这意味着只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端。
Reactor反应器模式:使用单线程模拟多线程,提高资源的利用率和程序的效率,增加系统吞吐量。
Selector类似一个观察者,我们把需要探知的socketchannel告诉Selector,当有事件发生时,Selector会通知我们并传回一组SelectionKey,我们读取这些Key,会获得注册过的socketchannel,然后从这个Channel中读取数据。即主程序不用主动阻塞的等待数据,而是被通知有数据就绪。
4.AIO
JDK1.7升级了NIO类库,升级后的NIO类库被成为NIO2.0,正式提供了异步文件I/O操作,同时提供了与UNIX网络编程事件驱动I/O对应的AIO。NIO2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。异步通道提供以下两种方式获取操作结果。
(1)通过java.util.concurrent.Future类来表示异步操作的结果;
(2)在异步操作的时候传入一个java.nio.channels.CompletionHandler接口的实现类作为操作完成的回调。
NIO2.0的异步套接字通道是真正的异步非阻塞I/O,对应了UNIX网络编程中的事件驱动I/O(AIO),它不需要通过多路复用器(selector)对注册的通道进行轮询操作即可实现异步读写,从而简化了NIO的编程模型。

Base 128 Varints

Google Protobuf3

Google Protobuf 官方文档之Language Guide (proto3)
http://blog.163.com/lnsjc321@126/blog/static/5348428720154325730234/
https://developers.google.com/protocol-buffers/docs/proto3

Java多线程

Runnable

Runnable不是线程,它只是一个接口定义了一个方法,Thread启动才是一个线程。

Thread

thread.join()方法
在主线程中调用子线程的join方法,目的是使主线程阻塞,直到子线程执行完毕才返回到主线程中。
简单理解,在主线程中调用t.join(),也就是在主线程中加入了t线程的代码,必须让t线程执行完毕之后,主线程(调用方)才能正常执行。

Java线程的状态有以下几种:
新建(new):新创建了一个线程对象。
可运行(runnable):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。
运行(running):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。
阻塞(block):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:
(一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。同时释放对象锁
(二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
(三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。
死亡(dead):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

内存可见性

可见性是并发的三个特性之一,指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。即其它线程要能立即使其工作内存中的变量副本值保持最新。
线程工作内存是cpu寄存器和高速缓存的抽象描述,使用频率高的数据从主存拷贝到高速缓存中,每个线程在cpu高速缓存中对拷贝的数据进行读取、计算、赋值,再在合适的时候同步更新到主存。

解决方法:需要各线程间可见的变量前加上volatile修饰,在一个线程的高速缓存中改变该值时,其他线程会获得该值的更新值

• final:初始化 final宇段确保可见性,这里需要注意 final修饰基本数据类型,可以 保证此字段的可见性,但是如果修饰的是对象,那么需要保证此对象是状态不可变 的才能保证可见性,即保证对象的每个字段也是 final 的 。
• volatile:此关键字的语义保证了新值能够立即同步到主内存,并且每次使用前都立 即从主内存刷新。 volatile保证了多线程操作时变量的可见性。
• synchronized:在同步块内读/写字段也确保了可见性 。

可重入锁

可重入锁,也叫做递归锁,指的是在同一线程内,外层函数获得锁之后,内层递归函数仍然可以获取到该锁。换一种说法:同一个线程再次进入同步代码时,可以使用自己已获取到的锁。作用是防止在同一线程中多次获取锁而导致死锁发生。

线程同步

线程同步一般是要解决在“单对象多线程”的情况下,控制共享变量的访问,或是控制执行步骤顺序。
控制共享变量的策略:

  • 将“单对象多线程”修改成“多对象多线程”;
  • 将“全局变量”降级为“局部变量”;
  • ThreadLocal机制,线程中使用ThreadLocal类型的包装变量,为每一个使用该变量的线程提供一个副本。
  • 用final域,有锁保护的域和volatile域可以避免非同步的问题。

使用特殊域变量(volatile)实现线程同步
a.volatile关键字为域变量的访问提供了一种免锁机制,
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
volatile保证此变量对所有线程的可见性,以及禁止指令重排序优化。

volatile

volatile关键字两个特性:
1、保证变量在线程间可见,即内存可见性。得益于java内存模型—”先行发生原则”,当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存。当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量。
2、禁止指令重排序优化;

但volatile变量的运算在并发下是并不保证原子性,即不是多线程安全的,例如java里的运算等复合操作比如自增对于变量不是原子性的。
java.util.concurrent.atomic包下提供了12种原子操作类型,例如 AtomicInteger。可通过CAS(Compare and Swap)来实现原子操作。CAS是一种乐观锁技术。

synchronized

java的每个对象都有一个内置锁,synchronized通过锁住一个对象,来控制所对应的代码段的并发访问。用在代码段上,synchronized锁住的是括号里的对象,用在实例方法上,synchronized锁的是对象本身也就是this。static synchronized方法锁住的是Class对象,相当于全局锁。
synchronized的原理和数据库中事务锁的原理类似。在使用过程中应该尽量缩减synchronized覆盖的范围,原因是被它覆盖的范围是串行的,效率低,并且容易产生死锁。应减小锁的粒度,从而使代码更大程度的并发。
synchronized就是一种独占锁(悲观锁)。

synchronized 与 reentrantlock

Synchronized 与 Lock都是可重入锁,都实现了多线程同步和内存可见性。
synchronized可以修饰实例方法(锁住实例对象)、静态方法(锁住类对象)、代码块(显示指定锁对象),reentrantlock要显示调用trylock()/lock()方法,需要在finally块中释放锁。
synchronized依赖jvm内存模型保证包含共享变量的多线程内存可见性,reentrantlock 是在jdk中通过volatile state保证包含共享变量的多线程内存可见性。

等待/通知机制

等待/通知机制,是指WaitThread首先获取了对象的锁,然后调用对象的wait(mills)方法,从而放弃了锁进入了对象的等待队列中,进入等待状态。NotifyThread随后获取了对象的锁,并调用对象的notify()或notifyAll()方法,将WaitThread从等待队列中移到同步队列中,此时等待线程的状态变为阻塞状态。
等待方遵循以下原则:
获取对象的锁
如果条件不满足,那么调用对象的wait(mills)方法,被通知后仍要检查条件
条件满足则执行后续操作
通知方遵循以下原则:
获得对象的锁
改变条件
通知所有等待在对象上的线程

1、 必须在同步环境内调用wait()、notify()、norifyAll()方法。线程不能随意调用对象上等待或通知的方法,除非它拥有那个对象的锁。即wait()等方法的调用必须放在synchronized方法或synchronized块中。当线程调用了wait()方法时,它会释放掉对象的锁,而Thread.sleep()虽然也会导致线程暂停,但线程在睡眠的过程中是不会释放掉对象的锁的。
2、 wait(),notify(),notifyAll()都是Object类的实例方法。与每个对象具有锁一样,每个对象可以有一个线程列表,他们等待通知信号。线程通过执行对象上的wait()方法获得等待列表。notify()方法会唤醒一个等待当前对象的锁的线程。被唤醒的线程需要等到当前线程放弃这个对象的锁才会被执行。被唤醒的线程将和其他线程以通常的方式进行竞争来获得对象的锁。
https://blog.csdn.net/u011159417/article/details/52301135

Java5中的concurrent工具包

JavaSE5.0中新增了一个java.util.concurrent包来支持同步,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier和Semaphore。concurrent工具包中也提供了线程池,分为3类:ScheduledThreadPool、FixedThreadPool和CachedThreadPool。
有些情况下需要使用线程的返回值,可以使用Callable和CompletionService,前者返回单个线程的结果,后者返回一组线程的结果。
在concurrent工具包中,我们可以使用BlockingQueue来实现生产者-消费者模型。
使用信号量来控制线程:
JDK提供了Semaphore来实现“信号量”的功能,它提供了两个方法分别用于获取和释放信号量:acquire和release。
可以用synchronized关键字来控制单个线程中的执行步骤,要对线程池中的所有线程的执行步骤进行控制的,有两种方式,一种是使用CyclicBarrier,一种是使用CountDownLatch。
CyclicBarrier使用了类似于Object.wait的机制,它的构造函数中需要接收一个整型数字,用来说明它需要控制的线程数目,当在线程的run方法中调用它的await方法时,它会保证所有的线程都执行到这一步,才会继续执行后面的步骤。
使用重入锁实现线程同步
ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。
关于Lock对象和synchronized关键字的选择:
如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码。
如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁。

ThreadLocal

使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
ThreadLocal不属于同步机制,ThreadLocal采用以”空间换时间”的方法,而同步机制采用以”时间换空间”的方式。ThreadLocal用于需要进行数据隔离而不需要数据同步的场景中。
ThreadLocal不是集合,它不存储任何内容,真正存储数据的集合在Thread中。ThreadLocal只是一个工具,一个往各个线程的ThreadLocal.ThreadLocalMap中table的某一位置set一个值的工具。

Future

java1.5中Future是一个未来对象,里面保存线程处理结果,它像一个提货凭证,可以随时去提取结果。Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
在两种情况下离开Future较难处理:

  • 一种情况是拆分订单,比如应用收到一个批量订单,此时如果要求最快的处理订单,那么需要并发处理,并发的结果如何收集的问题如果自己编程非常繁琐,此时可以使用CompletionService解决这个问题。CompletionService将Future收集到一个队列里,可以按结果处理完成的先后顺序进队。
  • 另一种情况是如果需要并发查询一些东西(比如爬虫),并发查询只要有一个结果返回就认为查询到了,并且结束查询,这时也需要用CompletionService和Future来解决。

Semaphore

Semaphore,即信号量,是操作系统中的概念。在Java并发编程中,信号量控制线程并发数量,即把同一个Semaphore对象用在不同的线程中,根据设定值,来控制最大并行线程数。

import java.util.concurrent.Semaphore;
            Semaphore semaphore = new Semaphore(1);
            //获取一个许可,等待直到获得许可
            semaphore.acquire();
            Thread.sleep(1000);
            //释放一个许可
            semaphore.release();

CountDownLatch

CountDownLatch即倒数器,它有CountDown和Await两个方法,比如可以实现在主线程中等待其它所有线程完成这样的功能。即把同一个CountDownLatch对象用在不同线程中,其中在子线程中调用latch.countDown(),在主线程中调用latch.await()使主线程挂起,等待到计数为0。

CyclicBarrier

CyclicBarrier 字面意思回环栅栏,通过它可以实现让一组线程互相等待至某个状态之后,再全部同时执行。当调用它的await()方法之后,其所在的线程就处于阻塞状态了,当所有线程都达到这个状态后,各线程才再继续运行。
叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier还可以被重用。

联系和区别

Semaphore、CountDownLatch、CyclicBarrier,它们就像一个调度员,同时驻守到这些线程中,来管理这些线程的并发行为。
1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:
CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;
而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;
另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。
2)Semaphore和锁有点类似,它一般用于控制对某组资源的访问权限。

《Java工程师修炼之道》7.3节 Java 并发编程

并发是CPU多核特性充分利用的关键技术。
Java 内存模型是屏敲了底层硬件环境 的差异而 给 Java程序提供的统一的内存访问模式,可以认为是一种虚拟内存环境。 在 Java程序中, 所有线程都共享主内存,但是对于每一个线程都有自己的工作内存( 一个虚拟的概念,包 括寄存器、械、写缓冲区、缓存以及其他硬件 、编译器优化等),工作内存与主内存通过 一些规定的操作来交互同步数据,而线程只能访问自己的工作内存。 因此在多线程环境下, 很容易造成工作内存数据不一致而引起并发问题。
并发过程中围绕原子性、可见性以及有序性展开处理。
从 Java 5开始提出了 happens-before (先行发生原则)的概念,通过此概念阐述操作之间的内存可见性 : 如果 一个操作执行的结果需要对另 一个操作可见,那么这两个操作之间必须存在 happens-before 关系。此原则在一定程度上解决了可见性和有序性的问题,是 Java 内存模型中的“天然的” 先行发生关系,无须任何同步器协助就已经存在 。 如果两个操作不在上述的规则中,并且不能通过以上规则推导出来,那么虚拟机就可以随意地对它们进行重排序。 需要注意的是, 这里的先行发生原则和时间先后顺序之间基本没有关系 。

java 高并发面试题

https://blog.csdn.net/u012998254/article/details/79400549

Web服务端基础技术

使用jstl
<%@ taglib uri=”http://java.sun.com/jsp/jstl/core” prefix=”c” %>
<%@ taglib uri=”http://java.sun.com/jsp/jstl/functions” prefix=”fn” %>

jsp界面的$通常不嵌套
jstl的字符串拼接不需要+,直接让多个$相邻即可。

标签及函数使用示例:

${fn:substring(image.path,7,pathLength)}
‘${sub}’,${strArray}

jsp页面中jstl标签详解
http://blog.csdn.net/justjackwang/article/details/8804528

在java领域,表现层技术主要有三种:jsp、freemarker、velocity。

jsp是大家最熟悉的技术
优点:
1、功能强大,可以写java代码
2、支持jsp标签(jsp tag)
3、支持表达式语言(el)
4、官方标准,用户群广,丰富的第三方jsp标签库
5、性能良好。jsp编译成class文件执行,有很好的性能表现
缺点:
jsp没有明显缺点,非要挑点骨头那就是,由于可以编写java代码,如使用不当容易破坏mvc结构。

velocity是较早出现的用于代替jsp的模板语言
优点:
1、不能编写java代码,可以实现严格的mvc分离
2、性能良好,据说比jsp性能还要好些
3、使用表达式语言,据说jsp的表达式语言就是学velocity的
缺点:
1、不是官方标准
2、用户群体和第三方标签库没有jsp多。
3、对jsp标签支持不够好

freemarker
优点:
1、不能编写java代码,可以实现严格的mvc分离
2、性能非常不错
3、对jsp标签支持良好
4、内置大量常用功能,使用非常方便
5、宏定义(类似jsp标签)非常方便
6、使用表达式语言
缺点:
1、不是官方标准
2、用户群体和第三方标签库没有jsp多

选择freemarker的原因:
1、性能。velocity应该是最好的,其次是jsp,普通的页面freemarker性能最差(虽然只是几毫秒到十几毫秒的差距)。但是在复杂页面上(包含大量判断、日期金额格式化)的页面上,freemarker的性能比使用tag和el的jsp好。
2、宏定义比jsp tag方便
3、内置大量常用功能。比如html过滤,日期金额格式化等等,使用非常方便
4、支持jsp标签
5、可以实现严格的mvc分离

Servlet

Servlet容器默认采用单实例多线程的方式来处理所有请求,这样减少产生Servlet实例的开销,提升了对请求的响应时间。对于Tomcat可以在server.xml中通过元素设置线程池中线程的数目。
servlet中的init方法只有在启动(例如web容器启动,要看loadOnStartup的设置)的时候调用,也就是只初始化一次,这就是单实例。
servlet在处理请求的时候调用的是service方法。

动态网页的技术进化

1.在程序中输出html:最早实现动态网页的方法是使用Perl和CGI。在Perl程序中输出HTML内容,由HTTP服务器调用Perl程序,将结果返回给客户端。这种方式在互联网刚兴起的20世纪90年代非常流行。但问题在于如果HTML内容比较多,维护非常不方便。
2.以模板为中心的架构:大概在2000年左右,以ASP、PHP、JSP 的为代表的以模板为基础的语言出现了,这种语言的使用方法与CGI相反,是在以HTML为主的模板中插入程序代码。这种方式在2002年前后非常流行,但它的问题是页面和程序辑紧密耦合,任何一个网站规模变大以后,都会遇到结构混乱,难以处理的问题。
3.MVC架构:为了解决这种问题,以MVC架构为基础的平台逐渐兴起, Ruby on Rails、Django、Zend Framework 都是基于 MVC 架构。

Session

Session又称为会话状态,用于维护和当前浏览器实例相关的一些信息。Session对于每一个客户端(或者说浏览器实例)是“人手一份”,用户首次与Web服务器建立连接的时候,服务器会给用户分发一个SessionID作为标识。SessionID是一个由24个字符组成的随机字符串。用户每次提交页面,浏览器都会把这个SessionID包含在HTTP头中提交给Web服务器,这样Web服务器就能区分当前请求页面的是哪一个客户端。

前后端分离,session会失效,因为前端是跨域请求,每次跨域请求时ajax发送的都是新的sessionid。spring boot+react项目中使用session成功解决方案的文章 https://blog.csdn.net/fwk19840301/article/details/80675547
https://segmentfault.com/a/1190000009208644

多负载会话管理的方式:
1.会话复制
2.集中式会话。在分布式架构中,可以把session存储到redis等一个公共服务中。

自定义注解

@interface的作用是用来自定义注解,使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口,由编译程序自动完成其他细节。在定义注解时,不能继承其他的注解或接口。
@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

定义注解格式:
public @interface 注解名 {定义体}

Redirect与Forward

Redirect,即重定向,会让请求端重新发一次请求,要求客户端跳转。
Forward,是服务端跳转。