Java锁

一、宏观上的锁类型

乐观锁

是一种思想,适用于读多写少的情况,每次 取数据—-更新数据 过程中,更新数据的时候判断在此间隙有没有其他线程去更新这个数据,如果有则进行重试,没有则更新成功。(其实也就是不加锁)

CAS(Compare And Swap)操作:在set之前先比较该值有没有变化,只有在没变的情况下才对其赋值。

  • CAS的问题:

1).CAS自旋平白无故消耗cpu

2).ABA问题:第一个线程先读了a,然后另外一个线程读了a,改成了b,又改成了a,这时候第一个线程并不知道有过修改。

如下所示:用单向列表实现堆栈。

线程1希望用CAS将栈顶替换为B,需要判断栈顶是不是A;

此时线程2介入,将A、B出栈,再pushD、C、A,此时堆栈结构如下图,而对象B此时处于游离状态:

然后线程T1执行CAS操作,检测发现栈顶仍为A,所以CAS成功,栈顶变为B,但实际上B.next为null,所以此时的情况变为:

其中堆栈中只有B一个元素,C和D组成的链表不再存在于堆栈中,平白无故就把C、D丢掉了。

 

自旋锁、轻量级锁都是乐观锁。

悲观锁

每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁。

 

二、锁类型

  • 自旋锁
  • 偏向锁/轻量级锁/重量级锁
  • 公平锁/非公平锁
  • 可重入锁
  • 共享锁/独占锁(排它锁)
  • 互斥锁/读写锁
  • 分段锁

自旋锁:

如果该线程没有获取到锁,则不会立即阻塞,而是采用循环的方式去不断重试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

偏向锁/轻量级锁/重量级锁

  • 偏向锁:单个线程运行的时候,这个锁偏向这个线程,当一直被该线程访问时,那么该线程会自动获得该锁。这时当有其他线程来争用时,会转为轻量级锁。
  • 轻量级锁:轻量级锁是当线程获取锁时,如果没获取到,则会进行自旋(等待一会再重试,不断自旋会消耗cpu性能),这样不会进入到阻塞状态。但是当自旋一定次数后还未获取到锁,则会转为重量级锁。
  • 重量级锁:会让其他申请锁的线程进入阻塞,性能降低。

上面这叫锁升级。

公平锁/非公平锁

多个线程按照申请锁的顺序来获取锁,先来的先拿到锁,后来的后拿到锁。非公平锁的优点在于吞吐量比公平锁大。

ReentrantLock是非公平锁,但是可以通过参数设置为公平锁。

可重入锁

同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。可重入锁的一个好处是可一定程度避免死锁。

比如synchronized修饰的方法中,又有一个synchronized修饰的代码块,那么该线程执行代码块的时候能自动获取锁,而不用再重新获取锁了。所以synchronized是可重入锁。

共享锁/排它锁(独占锁)

共享锁是指该锁可被多个线程所持有。排它锁(独占锁)是指该锁一次只能被一个线程所持有。

比如ReentrantLock是独享锁,ReadWriteLock中,读锁时共享锁,写锁是独占锁。共享读锁,独占写锁。

互斥锁/读写锁

上面讲的共享锁/独占锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。
互斥锁在Java中的具体实现就是ReentrantLock
读写锁在Java中的具体实现就是ReadWriteLock

分段锁

分段锁是一种锁的设计,例如ConcurrentHashMap中,对每个Segment(段)加锁,细化了锁的粒度,可以实现并行的插入。

三、对象头

java虚拟机中的对象由三部分组成:1.对象头2.实例数据3.对齐填充。

对象头:

无锁状态下:对象头的前25位为hash值,4位分代年龄,1位是否偏向锁,2位锁标志位

四、synchronized锁

synchronized:重量级锁、非公平锁、可重入锁、独占锁、悲观锁

JVM类加载机制

一、类加载概念

Java虚拟机把.class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机使用的类型,这就是虚拟机的加载机制。

二、类加载的生命周期

生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(using)、和卸载(Unloading)七个阶段。

其中验证、准备和解析三个部分统称为连接(Linking),这七个阶段的发生顺序如下图所示:

 

(1) 加载:查找和导入Class文件;

(2) 链接:把类的二进制数据合并到JRE中;

(a)校验:检查载入Class文件数据的正确性;

(b)准备:给类的静态变量分配存储空间;

(c)解析:将符号引用转成直接引用;

(3) 初始化:对类的静态变量,静态代码块执行初始化操作

总结过程:加载.class文件,把加载的数据合并到JRE中,然后对类进行初始化操作以便后续使用。

三、类加载器

启动类加载器:加载系统环境变量下JAVA_HOME/lib目录下的类库。

扩展类加载器:加载JAVA_HOME/lib/ext目录下的类库。

应用程序类加载器(系统类加载器):加载用户类路径Class_Path指定的类库。(使用第三方jar包添加到ClassPath后就是使用了这个加载器)

自定义加载器:如果需要自定义加载时的规则(比如:指定类的字节流来源、动态加载时性能优化等),可以自己实现类加载器。

 

总结:先加载jre下lib目录的类库,然后加载lib下ext的扩展的类库,然后加载第三方jar包,最后是加载自定义的规则

 

四、类加载的方式

方式一:
Class.forName
方式二:
ClassLoader.loadClass

 

五、双亲委派模型

工作流程是:当一个类加载器收到类加载请求时,不会直接加载这个类,而是把这个加载请求委派给自己父加载器去完成。如果父加载器无法加载时,子加载器才会去尝试加载。

使用双亲委派模型的原因:避免同一个类被多个类加载器重复加载。

例如object类,它存放在 rt.jar中,无论哪个类加载器要加载这个类,最终都是委派给处于模型顶端的启动类加载器加载这 个类,因此object类在程序的各种加载 类环境中都是同一个类。

 

六:对象的生命周期

对象是由类创建出来的,所以对象的生命周期就是包含在类的生命周期中:

类加载(5步,到类初始化)——创建类的实例对象——使用对象——对象回收——类卸载

 

参考:https://blog.csdn.net/fgets/article/details/52934178

 

Java垃圾收集器

1.HotSpot虚拟机所有垃圾收集器

注:连线代表可以搭配使用

新生代收集器:Serial、ParNew、Parallel Scavenge;

1)serial 收集器:单线程,进行垃圾收集时,必须暂停所有工作线程,直到完成。对于运行在 client 模式下的虚拟机来说是个很好的选择。

2)parNew 收集器:serial 收集器的多线程版本,是许多运行在 server 模式下的虚拟机首 选的新生代收集器。

3)parallel scaverge:也叫吞吐量收集器(Throughput Collector),目标 达到一个可控制的吞吐量,适合在后台运算,没有太多的交互。适合以高吞吐量为目标,减少垃圾收集时间的模式,比如不需要与用户进行太多交互,只是需要能快速的在后台进行垃圾回收。

上面都是执行拷贝算法。

 

老年代收集器:Serial Old、Parallel Old、CMS;

4)serial old:serial 的老年代版本,单线程

5)parallel old:parallel scaverge 老年代的版本,多线程

上面两个都是标记-整理算法。

6)cms(Concurrent Mark Sweep) 收集器:也称为并发低停顿收集器或低延迟垃圾收集器,适合用户交互较多,希望系统停顿时间最短,注重服务的响应速度的场景。

使用标记-清除算法(会产生内存碎片),可以与用户线程同时工作。

 

整堆收集器:G1(Garbage-First

可以并发让垃圾收集与用户程序同时进行,不需要与其他收集器搭配,没有了新生代和老年代的区分,而是将整个堆划分为多个大小相等的独立区域(Region),整个范围内执行标记-整理算法,局部是从一个region到另外一个region执行拷贝算法。

总结:只有cms和G1收集器才可以和用户线程并发进行。

 

2.GC

a.Minor GC (young GC),新生代的gc,因为Java对象大多是朝生夕灭,所以Minor GC非常频繁,一般回收速度也比较快;

当Eden区没有足够内存分配时,会发生一次MinorGC,结果15次(默认可修改)后还未被回收的会被放到年老代。

b.Full GC (Major GC),年老代gc,Full GC速度一般比Minor GC慢10倍以上;

 

3.GC用的引用可达性分析算法中,哪些对象可作为GC Roots对象?

• Java虚拟机栈中的对象
• 方法区中的静态成员
• 方法区中的常量引用对象
• 本地方法区中的JNI(Java Native Interface)引用对象。

 

原文地址:https://blog.csdn.net/tjiyu/article/details/53983650

BIO、NIO和AIO

 

一、tcp缓冲区:

  • BIO(Blocking IO) : 同步阻塞。如上,当发送端缓冲器满的时候,这时再发送将会被阻塞。接收端同理。
  • NIO(New IO/Non-Blocking IO) : 同步非阻塞。引入事件机制(IO的多路复用),使用一个线程不断扫描缓冲区,当发现写缓冲区为空时会生成一个socket可写事件,通知一个线程去进行写数据。
  • AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,

二、几个概念

  • 同步:发出一个请求,在没有得到调用结果前该调用不返回。
  • 异步:调用发出后就直接返回。
  • 阻塞:在调用结果返回之前,该线程会被挂起(阻塞),调用线程只有得到结果才会返回。
  • 非阻塞:没有得到返回结果之前,不会阻塞该线程。
  • 同步阻塞(BIO):一个连接一个线程
  • 同步非阻塞(NIO):一个请求一个线程(如下图对每个请求分配一个线程进行处理)
  • 异步非阻塞(AIO):一个有效的请求才启动一个线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间长的应用。

三、NIO细节

Selector选择器:监听客户端连接,每当有客户端连接过来,就把它注册到其中

Buffer缓冲区:提高读写性能。类似于一个数组,哪一部分正在写,哪一部分正在读,可以实现读写一起操作。

Channel多路复用:传输数据,避免了不断创建连接、断开连接的消耗

 

以上基于事件驱动,采用了Reactor模式,它使用一个线程管理所有的socket通道(Channel),即客户端发送的连接请求都会注册到多路复用器(Selector)上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。它的特点是要不断主动地去询问数据有没有处理完,一般只适用于连接数目较大但连接时间短的应用,如聊天应用等。

分布式协调服务-zookeeper

一、分布式环境

1.特点

a.分布性

b.并发性

程序运行过程中,并发性操作是很常见的。比如同一个分布式系统中的多个节点,同时访问一个共享资源。数据库、分布式存储

c.无序性

进程之间的消息通信,会出现顺序不一致问题

2.分布式环境下面临的问题

a.网络通信

网络本身的不可靠性,因此会涉及到一些网络通信问题

b.网络分区(脑裂)

当网络发生异常导致分布式系统中部分节点之间的网络延时不断增大,最终导致组成分布式架构的所有节点,只有部分节点能够正常通信

c.三态

在分布式架构里面,除了成功、失败、超时

3.分布式事务

ACID(原子性、一致性、隔离性、持久性)

4.中心化和去中心化

冷备或者热备

分布式架构里面,很多的架构思想采用的是:当集群发生故障的时候,集群中的人群会自动“选举”出一个新的领导。

最典型的是: zookeeper / etcd

 

5.经典的CAP/BASE理论

a.CAP理论

一致性(Consistency): 所有节点上的数据,时刻保持一致

可用性(Availability):每个请求都能够收到一个响应,无论响应成功或者失败

分区容错 (Partition-tolerance):表示系统出现脑裂以后,可能导致某些server与集群中的其他机器失去联系

CP  / AP

 

CAP理论仅适用于原子读写的Nosql场景,不适用于数据库系统

b.BASE理论:

基于CAP理论,CAP理论并不适用于数据库事务(因为更新一些错误的数据而导致数据出现紊乱,无论什么样的数据库高可用方案都是

徒劳) ,虽然XA事务可以保证数据库在分布式系统下的ACID特性,但是会带来性能方面的影响;

eBay尝试了一种完全不同的套路,放宽了对事务ACID的要求。提出了BASE理论

 

Basically available  : 数据库采用分片模式, 把100W的用户数据分布在5个实例上。如果破坏了其中一个实例,仍然可以保证80%的用户可用。

 

soft-state:  在基于client-server模式的系统中,server端是否有状态,决定了系统是否具备良好的水平扩展、负载均衡、故障恢复等特性。

Server端承诺会维护client端状态数据,这个状态仅仅维持一小段时间, 这段时间以后,server端就会丢弃这个状态,恢复正常状态。比如用户支付,成功或失败,中间会有一个处理中的状态。

 

Eventually consistent:数据的最终一致性

二、初步认识zookeeper

zookeeper是一个开源的分布式协调服务,是由雅虎创建的,基于google chubby。

1.zookeeper是什么

分布式数据一致性的解决方案

作用:zookeeper并不是用来存储数据的,通过监控数据状态的变化,达到基于数据的集群管理。

2.zookeeper能做什么

数据的发布/订阅(配置中心:disconf)  、 负载均衡(dubbo利用了zookeeper机制实现负载均衡) 、命名服务、

master选举(kafka、hadoop、hbase)、分布式队列、分布式锁

3.zookeeper的特性

a.顺序一致性

从同一个客户端发起的事务请求,最终会严格按照顺序被应用到zookeeper中

b.原子性

所有的事务请求的处理结果在整个集群中的所有机器上的应用情况是一致的,也就是说,要么整个集群中的所有机器都成功应用了某一事务,要么全都不应用

c.可靠性

一旦服务器成功应用了某一个事务数据,并且对客户端做了响应,那么这个数据在整个集群中一定是同步并且保留下来的

d.实时性

一旦一个事务被成功应用,客户端就能够立即从服务器端读取到事务变更后的最新数据状态;(zookeeper仅仅保证在一定时间内,近实时)

e.单一视图

无论客户端连接到哪个服务器,所看到的模型都是一样

 

三、zookeeper安装

单机环境安装

1.下载zookeeper的安装包

http://apache.fayea.com/zookeeper/stable/zookeeper-3.4.10.tar.gz

2.解压zookeeper

tar -zxvf zookeeper-3.4.10.tar.gz

3.cd 到 ZK_HOME/conf , copy一份cfg

cp  zoo_sample.cfg  zoo.cfg

4.sh zkServer.sh

{start|start-foreground|stop|restart|status|upgrade|print-cmd}

5.sh zkCli.sh -server ip:port

四、集群环境

1.zookeeper集群, 包含三种角色: leader / follower /observer

leader角色:

a. 事务请求的唯一调度和处理者,保证集群事务处理的顺
序性
b. 集群内部各服务器的调度者

follower

1. 处理客户端非事务请求、转发事务请求给 leader 服务器
2. 参与事务请求 Proposal 的投票(需要半数以上服务器
通过才能通知 leader commit 数据; Leader 发起的提案,
要求 Follower 投票)
3. 参与 Leader 选举的投票

observer

observer 是一种特殊的zookeeper节点。可以帮助解决zookeeper的扩展性(如果大量客户端访问我们zookeeper集群,需要增加zookeeper集群机器数量。从而增加zookeeper集群的性能。 导致zookeeper写性能下降, zookeeper的数据变更需要半数以上服务器投票通过。造成网络消耗增加投票成本)。observer服务器只提供非事物请求服务,通常在于不影响集群事务处理能力的前提下提升集群非事务处理的能力

  1. observer不参与投票。 只接收投票结果。
  2. 不属于zookeeper的关键部位。

 

2.集群的搭建

a.修改cfg

129/135/136

server.id=ip:port:port

server.1=192.168.11.129:2888:3181   2888表示follower节点与leader节点交换信息的端口号 3181  如果leader节点挂掉了, 需要一个端口来重新选举。

server.2=192.168.11.135:2888:3181

server.3=192.168.111.136:2888:3181

b.cfg中有一个dataDir = /tmp/zookeeper

$dataDir/myid 添加一个myid文件。

c.启动服务

 

注:

如果需要增加observer节点

zoo.cfg中 增加 :peerType=observer

同时,对应的配置后面添加对应的observer服务:server.3=192.168.111.136:2888:3181:observer

 

3.zoo.cfg配置文件分析

tickTime=2000  zookeeper中最小的时间单位长度 (ms)

initLimit=10  follower节点启动后与leader节点完成数据同步的时间

syncLimit=5 leader节点和follower节点进行心跳检测的最大延时时间

dataDir=/tmp/zookeeper  表示zookeeper服务器存储快照文件的目录

dataLogDir 表示配置 zookeeper事务日志的存储路径,默认指定在dataDir目录下

clientPort 表示客户端和服务端建立连接的端口号: 2181

 

五、zookeeper中的一些概念

1.数据模型

zookeeper的数据模型(树形结构)和文件系统类似,每一个节点称为:znode.  是zookeeper中的最小数据单元。每一个znode上都可以保存数据和挂载子节点。 从而构成一个层次化的属性结构。

节点特性:

持久化节点  : 节点创建后会一直存在zookeeper服务器上,直到主动删除

持久化有序节点 :每个节点都会为它的一级子节点维护一个顺序

临时节点 : 临时节点的生命周期和客户端的会话保持一致。当客户端会话失效,该节点自动清理

临时有序节点 : 在临时节点上多勒一个顺序性特性

2.会话

四种会话状态:没有连接、连接中、已连接、关闭

3.Watcher

zookeeper提供了分布式数据发布/订阅,zookeeper允许客户端向服务器注册一个watcher监听。当服务器端的节点触发指定事件的时候

会触发watcher。服务端会向客户端发送一个事件通知
watcher的通知是一次性,一旦触发一次通知后,该watcher就失效。如果需要永久监听,则需要反复注册。

4.ACL

zookeeper提供控制节点访问权限的功能,用于有效的保证zookeeper中数据的安全性。避免误操作而导致系统出现重大事故。

CREATE /READ/WRITE/DELETE/ADMIN

 

六、zookeeper的命令操作

1. create [-s] [-e] path data acl

-s 表示节点是否有序

-e 表示是否为临时节点

默认情况下,是持久化节点

2. get path [watch]

获得指定 path的信息

 

3.set path data [version]

修改节点 path对应的data

version的作用有一个乐观锁的概念:数据库里面有一个 dataversion 字段去控制数据行的版本号,每次修改对应节点,version会加1

4.delete path [version]

删除节点

七、stat信息

cversion = 0       子节点的版本号

aclVersion = 0     表示acl的版本号,修改节点权限

dataVersion = 1    表示的是当前节点数据的版本号

 

czxid    节点被创建时的事务ID

mzxid   节点最后一次被更新的事务ID

pzxid    当前节点下的子节点最后一次被修改时的事务ID

 

ctime    创建时间

mtime   修改时间

 

 

cZxid = 0x500000015

ctime = Sat Aug 05 20:48:26 CST 2017

mZxid = 0x500000016

mtime = Sat Aug 05 20:48:50 CST 2017

pZxid = 0x500000015

cversion = 0

dataVersion = 1

aclVersion = 0

ephemeralOwner = 0x0   创建临时节点的时候,会有一个sessionId 。 该值存储的就是这个sessionid

dataLength = 3    数据值长度

numChildren = 0  子节点数

 

八、Zookeeper的使用:

java API的使用:

1.jar包依赖

<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.8</version>
</dependency>

 

2.权限控制模式

schema:授权对象

ip     : 192.168.1.1

Digest  : username:password

world  : 开放式的权限控制模式,数据节点的访问权限对所有用户开放。 world:anyone

super  :超级用户,可以对zookeeper上的数据节点进行操作

 

3.连接状态

KeeperStat.Expired  在一定时间内客户端没有收到服务器的通知, 则认为当前的会话已经过期了。

KeeperStat.Disconnected  断开连接的状态

KeeperStat.SyncConnected  客户端和服务器端在某一个节点上建立连接,并且完成一次version、zxid同步

KeeperStat.authFailed  授权失败

4.事件类型

NodeCreated  当节点被创建的时候,触发

NodeChildrenChanged  表示子节点被创建、被删除、子节点数据发生变化

NodeDataChanged    节点数据发生变化

NodeDeleted        节点被删除

None   客户端和服务器端连接状态发生变化的时候,事件类型就是None

 

zkclient

curator

1.概述:

Curator本身是Netflix公司开源的zookeeper客户端;

curator提供了各种应用场景的实现封装

curator-framework  提供了fluent(流式)风格api

curator-replice     提供了实现封装

 

2.curator连接的重试策略

ExponentialBackoffRetry()  衰减重试

RetryNTimes 指定最大重试次数

RetryOneTime 仅重试一次

RetryUnitilElapsed 一直重试知道规定的时间

 

九、zookeeper数据存储

1.概述:

zookeeper中数据分为:内存数据和磁盘数据,zookeeper会定时把数据存储在磁盘上(配置文件中的配置)。

2.两个路径

DataDir路径下存储的是数据的快照

快照: 存储某一个时刻全量的内存数据内容

DataLogDir 存储事务日志

事务日志的文件格式:log.zxid

查看事务日志的命令

java -cp :/mic/data/program/zookeeper-3.4.10/lib/slf4j-api-1.6.1.jar:/mic/data/program/zookeeper-3.4.10/zookeeper-3.4.10.jar org.apache.zookeeper.server.LogFormatter log.200000001

3.zookeeper的三种日志

zookeeper.out  //运行日志

快照     存储某一时刻的全量数据

事务日志 事务操作的日志记录