一个socket小Demo
server
1 | public class ServiceServer { |
client
1 | public class ServiceClient { |
task
1 | public class ServiceServerTask implements Runnable { |
处理类
1 | public class GetDataServiceImpl { |
1 | public class ServiceServer { |
1 | public class ServiceClient { |
1 | public class ServiceServerTask implements Runnable { |
1 | public class GetDataServiceImpl { |
1 |
|
1 | public class ReflectTest { |
JMS即Java消息服务, Java Message Service,应用程序接口是一个Java平台中关于面向消息中间件的API,用于两个程序之间或分布式系统中发送消息,进行异步通信,它是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。
JMS由一下几个元素组成
JDK5.0以后的版本都引入了高级并发特性,大多数的特性在java.util.concurrent包中,是专门用于多线程编程的,充分利用了现代多处理器和多核心系统的功能以编写大规模并发应用程序。主要包含原子量,并发集合,同步器,可重入锁,并对线程池的构造提供了支持。
Fixed Thread Pool:拥有固定线程数的线程池,如果没有任务执行,那么线程会一直等待。Executors.newFixedThreadPool(4),构造函数中的4是线程池大小,可以根据需要配置。获取cpu数量:
1 | int cpuNums = Runtime.getRuntime().availableProcessors() |
Scheduled Thread Pool:用来调度即将执行的任务的线程池。Executors.newScheduledThreadPool()。
BlockingQueue是java.util.concurrent下的主要用来控制线程同步的工具。
主要的方法是: put, take 阻塞存取。add, poll 非阻塞存取。
区别
除了CachedThreadPool其他线程池都有饱和的可能,当饱和以后就需要相应的策略处理请求线程的任务,比如,达到上限时通过ThreadPoolExecutor.setRejectedExecutionHandler方法设置一个拒绝任务的策略JDK提供了AbortPolicy, CallerRunsPolicy, DiscardPolicy, DiscardOldestPolicy
多线程任务设计上尽量使得个任务是独立无依赖的,两个方面
1、线程之间的依赖性。如果线程有依赖可能会造成死锁或饥饿
2、调用者与线程的依赖性,调用者得监视线程的完成情况,影响并发量
不同的应用程序运行的过程中都需要在内存中分配自己独立的运行空间,彼此之间不会相互的影响,我们把每个独立应用程序在内存的独立空间成为当前应用程序运行的一个进程。
位于进程中,负责当前进程中的某个具备独立运行资格的空间,进程是负责整个程序的运行,而线程是程序中具体的某个独立功能的运行。一个进程中至少应该有一个线程。
1 | public class ThreadDemo { |
简单用法:
synchronzied(一个任意的对象[锁]){
doSomething();
}
synchonzied的缺陷:
如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
1、使用ls命令来查看当前zk中所包含的内容
1 | ls / |
2、创建一个新的znode。这个命令创建了一个新的znode节点 “zk”以及与它关联的字符串。
1 | create /zk |
3、运行get命令来确认znode是否包含我们所创建的字符串
1 | get /zk |
4、通过set命令来对zk所关联的字符串进行设置
1 | set /zk "test" |
5、删除znode
1 | delete /zk |
6、删除节点
1 | rmr /zk |
org.apache.zookeeper.Zookeeper时客户端入口类, 负责建立与server的会话, 提供了几类主要方法:
功能 | 描述 |
---|---|
create | 在本地目录树中创建一个节点 |
delete | 删除一个节点 |
exists | 是否存在目标节点 |
get/set data | 从目标姐弟啊读取/写数据 |
get/set ACL | 获取/设置目标节点访问控制列表信息 |
get children | 检索目标节点的子节点 |
sync | 等待要被传送的数据 |
1 | private static final String CONNECT_STRING = "bigdata1:2181,bigdata2:2181,bigdata3:2181"; |
zookeeper(zk)是一个分布式协调服务,zk本身就是一个分布式程序,在底层其实只提供了两个功能:
常用的应用场景有
zk集群有两个角色:leader和follower(observer)
只要集群中半数以上节点存活,集群就能提供服务。
zk虽然在配置文件并没有指定leader和follower, 但是, zk工作时, 是有一个节点为leader, 其他则为follower, leader是通过内部的选举机制临时产生的。
以一个简单的例子来说明整个选举的过程.
假设有五台服务器组成的zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的.假设这些服务器依序启动,来看看会发生什么.
1) 服务器1启动,此时只有它一台服务器启动了,它发出去的报没有任何响应,所以它的选举状态一直是LOOKING状态
2) 服务器2启动,它与最开始启动的服务器1进行通信,互相交换自己的选举结果,由于两者都没有历史数据,所以id值较大的服务器2胜出,但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是3),所以服务器1,2还是继续保持LOOKING状态.
3) 服务器3启动,根据前面的理论分析,服务器3成为服务器1,2,3中的老大,而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的leader.
4) 服务器4启动,根据前面的分析,理论上服务器4应该是服务器1,2,3,4中最大的,但是由于前面已经有半数以上的服务器选举了服务器3,所以它只能接收当小弟的命了.
5) 服务器5启动,同4一样,当小弟.
那么,初始化的时候,是按照上述的说明进行选举的,但是当zookeeper运行了一段时间之后,有机器down掉,重新选举时,选举过程就相对复杂了。
需要加入数据id、leader id和逻辑时钟。
数据id:数据新的id就大,数据每次更新都会更新id。
Leader id:就是我们配置的myid中的值,每个机器一个。
逻辑时钟:这个值从0开始递增,每次选举对应一个值,也就是说: 如果在同一次选举中,那么这个值应该是一致的 ; 逻辑时钟值越大,说明这一次选举leader的进程更新.
选举的标准就变成:
1、逻辑时钟小的选举结果被忽略,重新投票
2、统一逻辑时钟后,数据id大的胜出
3、数据id相同的情况下,leader id大的胜出
根据这个规则选出leader。
至少3台有jdk环境的机器(虚拟机也可)
两种方法,官网下载使用ftp工具传输到机器上或直接使用wget
1 | wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.13/zookeeper-3.4.13.tar.gz |
1 | tar -zxvf zookeeper-3.4.13.tar.gz |
添加内容
1 | export ZOOKEEPER_HOME=/home/hadoop/zookeeper |
重新加载文件
1 | source /etc/profile |
在zk文件夹中操作
1 | cd zookeeper/conf |
vim zoo.cfg 添加内容
1 | dataDir=/home/hadoop/zookeeper/data |
1 | cd /home/hadoop/zookeeper/ |
1 | echo 1 >> myid |
1 | scp -r /home/hadoop/zookeeper hadoop@slave2:/home/hadoop/ |
并且要记得修改myid文件:
slave2上内容修改为2
slave3上内容修改为3
zkServer.sh start
今天在更新博客的时候,突然报了异常,
1 | Error: The module '/usr/local/lib/node_modules/hexo-cli/node_modules/dtrace-provider/build/Release/DTraceProviderBindings.node' |
看样子是有一个模块因为被不同版本的node给build了,估计是前一阵子更新mac系统版本的时候更新了node版本。
关于node我也不是很懂,只能求助于谷歌- -
最后终于找到了一个暴力的解决方法:
1、cd 到 /usr/local/lib/node_modules/hexo-cli/
2、干掉node_modules文件夹
3、npm install
4、cd 到 hexo_project
5、干掉node_modules文件夹
6、npm install
搞定 收工~
先来看个栗子🌰:
1 |
|
输出为:
1 | sun.misc.Launcher$AppClassLoader@18b4aac2 |
可以看出,没有获取到ExtClassLoader的父Loader,原因是Bootstrap Loader(引导类加载器)是用C语言实现的,找不到一个确定的返回父Loader的方式,于是返回为null。
站在Java虚拟机的角度来说,只存在两种不同的类加载器:
站在开发人员的角度来说,大致可分为三类类加载器:
如果有必要,还可以假如自定义的类加载器。
ExtClassLoader和APPClassLoader并不是继承关系,它们都继承同一个类URLClassLoader。
JVM类加载机制:
- 全盘负责:当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
- 父类委托:先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径加载该类
- 缓存机制:缓存机制会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效
为什么需要双亲委派机制?
先说明一下,jvm判断两个类相同的前提是这两个类都是同一个加载器进行加载的,如果使用不同的类加载器进行加载同一个类,那在jvm中会认为它们是不同的。所以如果没有双亲委派机制,如果我们在rt.jar中随便找一个类,如java.util.HashMap,那么我们同样也可以写一个一样的类,也叫java.util.HashMap存放在我们自己的路径下,那么这两个类采用的是不同的类加载器,程序中就会出现两个不同的HashMap类。总结来说,1、系统类防止内存中出现多芬同样的字节码 2、保证Java程序安全稳定运行
双亲委派的工作流程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,所以所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
Java类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向外部提供了访问方法区内的数据结构的接口。
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段。其中验证、准备、解析统称为连接。
加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班的开始,而解析则不一定,它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java的运行时绑定。
加载阶段,虚拟机需要完成以下3件事情:
加载.class文件的方式
- 从本地系统中直接加载
- 通过网络下载
- 从zip,jar等归档文件中加载.class文件
- 从专有数据库中提取
- 将Java源文件动态编译为.class文件
加载和连接阶段的部分内容是交叉进行的,加载阶段尚未完成,连接阶段可能已经开始(如一部分字节码文件格式验证),但是这些动作仍然属于连接阶段的内容,这两个阶段的开始时间仍然保持着固定的先后顺序。
加载阶段既可以使用系统提供的类加载器来完成加载,也可以自定义自己的类加载器。
加载阶段完成后,虚拟机外部的二进制字节流就按照虚拟机所需的格式存储在方法区之中,而且在Java堆中也创建一个java.lang.Class对象,这样便可以通过该对象访问方法区中的这些数据。
验证是连接阶段的第一步,这一步的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
主要完成4个检验动作:
验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用-Xverifynone参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。
为类的静态变量分配内存,并将其初始化为默认值,正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配。
有几点需要注意:
假如一个类变量的定义为:
public static int value = 7
那么在准备阶段过后value的初始值为0,而不是7,这时候还未执行任何Java方法,把value赋值为7的动作将在初始化阶段才会执行。特殊情况:加载final关键词后,即当类字段的字段属性是ConstantValue时,会在准备阶段初始化为指定的值,所以标注final后,value的值在此阶段初始化为7而不是0。
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。符号引用就是一组符号来描述模板,可以是任何字面量。
直接引用就是直接指向模板的指针、相对偏移量或一个简洁定位到目标的句柄。
为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。对类变量进行初始值设定有两种方式:
JVM初始化步骤:
类初始化时机(只有当对类的主动使用的时候才会导致类的初始化):