前言
由于TreeMap的实现原理就是以红黑树为基础数据结构的,所以基本也是红黑树的原理解读。
红黑树
红黑树是一种自平衡的二叉查找树。是一种复杂但高效的数据结构,它可以在O(log n)时间内做查找,插入和删除。
红黑树的规定:
1.一个节点只能是红色或者黑色
2.根节点是黑色
3.每个叶节点(null节点/空节点)为黑色
4.如果一个节点为红色,则他们的2个子节点都为黑色
5.从任意节点到其每个叶节点
红黑树结构java代码示例:(TreeMap中的内部类Entry)
|
|
由于TreeMap的实现原理就是以红黑树为基础数据结构的,所以基本也是红黑树的原理解读。
红黑树是一种自平衡的二叉查找树。是一种复杂但高效的数据结构,它可以在O(log n)时间内做查找,插入和删除。
红黑树的规定:
1.一个节点只能是红色或者黑色
2.根节点是黑色
3.每个叶节点(null节点/空节点)为黑色
4.如果一个节点为红色,则他们的2个子节点都为黑色
5.从任意节点到其每个叶节点
红黑树结构java代码示例:(TreeMap中的内部类Entry)
|
|
很多Linux新手发现某个文件没有相关权限,一言不合就是chmod 777
。首先说一句,chmod 777
应该杜绝使用,尤其生产环境。
那chmod 777
的背后原理到底是什么呢?
首先Unix系统的权限分为三种,分别为拥有者(owner)、用户组(group)、其他用户(other)。用ll
命令可以查看具体的权限设置,如下图所示:
每个项目前面那一串字母和横杠就是权限。第一位指的是文件类型:-代表普通文件,d代表文件夹。后面9位分为三组,每组代表了对应用户的权限:
Docker hub官方已经维护了一套比较完善的jetty镜像,但是依赖的是openjdk。所以这里只是把jdk换成之前学习系列中构建过的oracle-jdk8。
|
|
“Java manipulates objects ‘by reference,’ but it passes object references to methods ‘by value.’” –David Flanagan
大神解释了:java操作对象都是通过引用传递,而给方法传参都是通过值传递
首先,操作对象通过引用传递这个大家应该没有什么争议。但是方法传参都是通过值传递,估计大家还有些疑虑。
我们知道,如果是值传递,那么实际上传的是一份拷贝。引用传递的话一般传递的是内存地址(看具体的jvm实现)。
所以,值传递是无法修改原值的,而引用传递是可以修改原值。
请看以下代码例子:
|
|
输出:
c1 after change1:[a, b]
c1 after change2:[e, b]
我们知道,数组是个对象。传递给2个不同的方法,确出现了不一致的行为,既有点像值传递,有好像是引用传递。我们来看看这2个方法在内存中到底做了什么:
zookeeper 是一个分布式的,开源的协调服务框架,服务于分布式应用程序。
可以将分布式应用从处理协调服务的泥潭中解救出来。且性能优越,设计简洁优雅。
|
|
注意:这里依赖的镜像oraclejdk8
请查看之前的文章“Docker学习系列五:构建oracle-jdk8镜像”
mybatis如何批量插入呢?常用的做法如下代码所示:
|
|
但是,这样有个影藏的bug:当user集合超过一定数量,会导致动态拼接的sql过长而导致执行报错。并且,批量更新就不能使用这种形式了。
spring默认bean的注册形式是单例模式。那spring是如何解决并发安全问题的呢?就是通过ThreadLocal。到底ThreadLocal有和“魔力”能让普通类变成线程安全的类呢?
先来看看ThreadLocal.java的源码注释:
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its {@code get} or {@code set} method) has its own, independently initialized copy of the variable. {@code ThreadLocal} instances are typically private static fields in classes that wish to associate state with a thread (e.g.,a user ID or Transaction ID).
大致意思:
这个类提供线程局部变量。这种在多线程环境下访问(通过get或set方法)时,能保证各个线程里的变量相对独立于其他线程内的变量。ThreadLocal实例通常是
private static
类型的,用于管理线程上下文。
也就是说,Threadlocal提供了作用范围为线程的局部变量,这种变量只在线程生命周期内起作用。减少了线程内多个方法之间公共变量传递的复杂度。
这里关于线程安全的类有一个普遍适用的原则:如果一个类没有实例私有属性,或者实例私有属性也是无状态的类,那么这个类就是无状态的类。而一个无状态的类肯定是线程安全的类。
而用ThreadLocal包装类的所有实例私有属性后,这个类就没有实例私有属性了,那么这个类就是一个无状态类,因此也是一个线程安全的类。这也是spring使用ThreeadLocal处理bean的默认方式。
由于docker官方的jdk镜像都是openjdk,与我们实际开发上线的jdk环境不符。因此需要构建一个oracle-jdk8的基础镜像。
参考的是dockerhub中一个oracle-jdk高star的配置:传送门
# AlpineLinux with a glibc-2.21 and Oracle Java 8
FROM alpine:3.3
MAINTAINER zacard <[email protected]>
# Java Version and other ENV
ENV JAVA_VERSION_MAJOR=8 \
JAVA_VERSION_MINOR=92 \
JAVA_VERSION_BUILD=14 \
JAVA_PACKAGE=jdk \
JAVA_HOME=/opt/jdk \
PATH=${PATH}:/opt/jdk/bin \
LANG=C.UTF-8
# do all in one step
RUN apk upgrade --update && \
apk add --update curl ca-certificates bash && \
for pkg in glibc-2.23-r1 glibc-bin-2.23-r1 glibc-i18n-2.23-r1; do curl -sSL https://github.com/andyshinn/alpine-pkg-glibc/releases/download/2.23-r1/${pkg}.apk -o /tmp/${pkg}.apk; done && \
apk add --allow-untrusted /tmp/*.apk && \
rm -v /tmp/*.apk && \
( /usr/glibc-compat/bin/localedef --force --inputfile POSIX --charmap UTF-8 C.UTF-8 || true ) && \
echo "export LANG=C.UTF-8" > /etc/profile.d/locale.sh && \
/usr/glibc-compat/sbin/ldconfig /lib /usr/glibc-compat/lib && \
mkdir /opt && curl -jksSLH "Cookie: oraclelicense=accept-securebackup-cookie" -o /tmp/java.tar.gz \
http://download.oracle.com/otn-pub/java/jdk/${JAVA_VERSION_MAJOR}u${JAVA_VERSION_MINOR}-b${JAVA_VERSION_BUILD}/${JAVA_PACKAGE}-${JAVA_VERSION_MAJOR}u${JAVA_VERSION_MINOR}-linux-x64.tar.gz && \
gunzip /tmp/java.tar.gz && \
tar -C /opt -xf /tmp/java.tar && \
apk del curl glibc-i18n && \
ln -s /opt/jdk1.${JAVA_VERSION_MAJOR}.0_${JAVA_VERSION_MINOR} /opt/jdk && \
rm -rf /opt/jdk/*src.zip \
/opt/jdk/lib/missioncontrol \
/opt/jdk/lib/visualvm \
/opt/jdk/lib/*javafx* \
/opt/jdk/jre/plugin \
/opt/jdk/jre/bin/javaws \
/opt/jdk/jre/bin/jjs \
/opt/jdk/jre/bin/keytool \
/opt/jdk/jre/bin/orbd \
/opt/jdk/jre/bin/pack200 \
/opt/jdk/jre/bin/policytool \
/opt/jdk/jre/bin/rmid \
/opt/jdk/jre/bin/rmiregistry \
/opt/jdk/jre/bin/servertool \
/opt/jdk/jre/bin/tnameserv \
/opt/jdk/jre/bin/unpack200 \
/opt/jdk/jre/lib/javaws.jar \
/opt/jdk/jre/lib/deploy* \
/opt/jdk/jre/lib/desktop \
/opt/jdk/jre/lib/*javafx* \
/opt/jdk/jre/lib/*jfx* \
/opt/jdk/jre/lib/amd64/libdecora_sse.so \
/opt/jdk/jre/lib/amd64/libprism_*.so \
/opt/jdk/jre/lib/amd64/libfxplugins.so \
/opt/jdk/jre/lib/amd64/libglass.so \
/opt/jdk/jre/lib/amd64/libgstreamer-lite.so \
/opt/jdk/jre/lib/amd64/libjavafx*.so \
/opt/jdk/jre/lib/amd64/libjfx*.so \
/opt/jdk/jre/lib/ext/jfxrt.jar \
/opt/jdk/jre/lib/ext/nashorn.jar \
/opt/jdk/jre/lib/oblique-fonts \
/opt/jdk/jre/lib/plugin.jar \
/tmp/* /var/cache/apk/* && \
echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf
# EOF
看考官方java镜像中有关java:alpine
的说明:传送门
摘录其中一段:
This variant is highly recommended when final image size being as small as possible is desired
官方推荐使用基于alpine制作的镜像。
docker build -t oraclejdk8 .
p.s:由于此镜像需要下载一些基础包和jdk,可能需要比较长的build时间,同时可能需要翻墙~
docker run -ti --rm oraclejdk8 java -version
docker save -o jdk8.tar oraclejdk8
docker load -i jdk8.tar
Docker Compose是一个用来定义和运行docker应用服务(一个或者多个dacker容器应用)的工具。使用Compose file来配置管理docker应用服务。
先来看下一下例子:
docker run --name jetty -p 8881:8080 -p 22 -d jetty;
docker run --name jenkins -p 8882:8080 --link -d jetty:jetty jenkins;
这段运行的命令是要先运行一个jetty容器,然后启用一个jenkins容器,同时链接到之前启动的jetty容器。命令显的冗长且极容易敲错或者漏敲。而且不容易记忆,迁移复杂。
如果使用Docker Compose的形式,只需配置一个docker-compose.yml文件即可。如下所示:
services:
jetty:
images:jetty
container_name:jetty
ports:
- "8881:8080"
- "22"
jenkins:
imagesLjenkins
container_name:jenkins
ports:
- "8882:8080"
links:
jetty
然后运行命令:
docker-compose up -d
即可做到和之前的命令一样的效果。
如同Dockerfile之于镜像构建,Compose file使从Docker image运行Docker contain的过程结构化、透明化。并使之保留运行的记录。
Docker Registry是一个docker仓库,用来存储和分享docker镜像。类似于Github。
这里应该说为什么需要配置一个私有的docker registry。我们知道,已经有dockerhub了,而且官方也提供私有仓库的功能。
因为访问官方的dockerhub,是要良好的网络。而且当我们部署发布的时候,肯定不希望dockeruhb在维护或者故障。而我们配置私有的docker registry可以很好的避免此类问题。
docker pull registry:2.4
命令:
docker run -d -p 5000:5000 --restart=always --name registry -v /data/registry:/var/lib/registry registry:2.4
使用Compose file编排(创建docker-compose.yml):
registry:
restart: always
image: registry:2.4
container_name: registry
ports:
- 5000:5000
volumes:
- /data/registry:/var/lib/registry
然后使用命令:
docker-compose up -d