重构读书笔记

重构第一步,构造可靠的测试环境。

What

任何可以立即查阅的东西,都故意不去记忆。

什么是重构

重构,对软件内部结构调整,在不改变外部可观察行为的前提下,提高其可理解性,减低修改成本。

重构难题

对于修改接口,能获取所有调用者,那么可以安心修改。如果无法修改全部调用者,如果是公开已经发布的接口,就需要同时维护新旧两个接口,直到所有用户将该变化做出反应。

How

Duplicated Code

重复代码提炼。兄弟类,则推到 super class,如果是不完全相同的,则分解方法提炼统一的方法。

Long method

小函数准确的命名,积极地分解长函数。每当需要注释来说明,就应该把需要说明的内容写入独立函数,以其用途命名。

Large Class

如果类内部变量有相同的前缀和后缀,可能可以将其提炼到某个组件。

Long Parameter List

参数列表不应该过长,对象传递。

Divergent Change

类在不同方向上发生变化,针对外界发生的变化所有的修改都只发生在单一类中,需要找出特定原因造成的修改,将其提炼到另一个类中。

Shotgun Surgery

每次遇到某种变化,需要在不同的类内部做修改,这时候需要用 Move Method 和 Move Field 将所需要修改的代码放到同一个类中,如果没有合适的类,就创造一个。

Divergent Change 是“一个类受多种变化影响”,Shotgun Surgery 是“一种变化引发多个类修改”。

Feature Envy

面向对象的技术要点在于,这是一种“将数据和对数据的操作行为包装在一起”的技术。函数对某个类的兴趣超过了对自己所处类的兴趣,这种问题的焦点便是数据。比如某个函数为了计算某个值,从另一个对象调用半打取值函数,解法,将这个函数移动到另外的地方。

Data Clumps

数据泥团,数据项成群结队在一起。

Primitive Obsession

对象打破了基本数据和体积较大的类的界限。

Switch Statements

switch 用多态来代替

Parallel Inheritance Hierarchies 平行继承

在为某个类增加子杯,也必须为另一个类增加子类。

Lazy Class


2018-12-16 java , refactor , coding , programming

JProfile 简单使用

在学习 ThreadLocal 的时候有人推荐了 JProfiler ,可以用来对 Java 内存泄露分析,JProfile 其实是一个 Java 的性能分析工具,不仅可以用来排查 OutOfMemoryError 的错误,对于查找系统瓶颈,查看 Java 堆信息等等都有很强大的功能支持。

搜索一番之后发现也有很多 Java Profiler 的工具,JDK 自带也有 Java VisualVM 这样的工具。

JProfiler

JProfiler’s intuitive UI helps you resolve performance bottlenecks, pin down memory leaks and understand threading issues.

安装注册

官网地址:

JProfiler 是商业软件,有 10 天试用期,到期注册码自行解决。

特性

  • profile a demo session and a saved session
  • attach a running jvm
  • profile an application server, locally or remotely
  • open a snapshot, can open HPROF and PHD snapshots

数据采集方式

两种采集方式

  • Sampling 采样,间隔时间将每个线程栈中方法栈中的信息统计出来,对应用影响小,数据统计可能不精确
  • Instrumentation, 在 class 加载之前,JProfier 把相关功能代码写入到需要分析的 class 中,对正在运行的 jvm 有一定影响。

启动方式

  • Attach mode ,将本机正在运行的 jvm 加载 JProfiler Agent
  • Profile at startup,将指定的 JProfiler Agent 手动加载到该 jvm
  • Prepare for profiling
  • Offline profiling

jprofiler

Java VisualVM

reference


2018-12-14 java , jprofiler , visualvm

英语搭配语字典整理

最近用 [[GoldenDict]] 查词非常频繁且非常有效的提高了阅读的效率,但是在日常有的时候听一些英语口语的广播节目的时候,经常会有一些短语搭配,而我们平时如果写作的话,其实短语较于单词更为重要,口语亦然。所以产生了一个念头,如果有一本英语搭配语字典就能够提高不少效率,没想到都不用去刻意搜索,就出来了好几个推荐。

  • Oxford Collocation Dictionary 牛津英语搭配语词典
  • The BBI dictionary of English word combinations BBI 英语搭配词典
  • Longman Collocations Dictionary and Thesaurus 朗文搭配
  • Longman Language Activator 朗文英语联想活用词典,更准确来说这一本更像是一部同义词解析词典,但也有涉及短语搭配
  • Oxford Learner’s Thesaurus

比如在查阅 dictionary 时,将查阅字典这一类的词组 look up the dictionary 也能够显示出来。在别人分享的一篇文章中,作者举了一个例子 open up the possibility ,开启了什么的可能性,possibility 这个单词非常常见,但是却少有人意识到它可以和 open up 一起用。搭配字典在英语学习中,尤其是非母语人士的英语写作中起着很大的帮助作用。越普通,越常见的单词就越应该了解其常用的搭配。

牛津英语搭配语词典 Oxford Collocation Dictionary

一种全新的英文词典,提供词于词见常用并且地道搭配用法。可以有效帮组英语学生或成人 学习,写作和说地道的英文。 该词典对准备雅思、托福等英语考试也非常有益。

级别:Upper-Intermediate to Advanced

主要特点:

《牛津英语搭配词典》(英汉双解版)从崭新的角度探究了英语中词与词之间的组合关系,如 ‘bright idea’ or ‘talk freely’。这种组合不是任意的,而是受到语义、语法、语体和文化的制约。只有熟悉和掌握了英语搭配,才能真正做到让英语单词“为我所用”,地道自然地传达思想,与人高效沟通。本词典收录搭配丰富,逾 9,000 个常用英语词条, 达 150,000 个搭配词组;例句真实自然,50,000 条示例全部选自语料库;专辟 25 个不同主题的用法说明,加深对搭配词应用的理解;

oxford collocation

The BBI Dictionary of English Word Combinations

是一本非常轻薄的搭配语字典,词条详细,内容丰富,编排紧凑,同样也带来了一定阅读的不便。

bbi collocations

Longman Collocations Dictionary and Thesaurus

朗文搭配语词典,虽然没有牛津搭配语词典有名,但一样是一本非常地道的搭配语词典,排版甚至较牛津的更加清晰易读。

longman collocations

Longman Language Activator

朗文英语写作活用词典,这是一本如何活用英语的词典,不仅会告诉你单词的意思,还会告诉你这个单词的使用情景。比如查询 future,它会告诉你 future 有这么多含义:

  • the time after now
    • 在表示现在之后的时间时,有 the car of the future may run on solar-powered batteries. 等等这样的搭配
    • 也会告诉你,future 相同含义的表达也可以是 lie ahead, ahead of
  • at some time in the future
    • 同样你也可以看到词典里面会有 in the future, some time, one day 等等的组合
  • likely to happen in the future
    • 表达相似的意思可以使用 be on the horizon, be in store 等等

Oxford Learner’s Thesaurus

更加准确来说这是一本 Thesaurus 词典,也就是同义词词典,会给出查阅单词的相近含义的单词,以便于联想学习。

总结

前三本字典是真正的搭配语词典,有着非常丰富的搭配语用法和解释,所以推荐可以日常对比使用。而后两本更加偏向于同义词活用,有时间再整理一下同义词词典吧。

reference


2018-12-14 english , learning , dictionary , goldendict , mdx , mdd , oxford , collocation , longman , oxford

Bash Script

Shell Syntax - what is your input means to the shell

Shell Operations - what can shell do

  • read input from file
  • breaks the input into words and operations
  • parse the token into simple and compound commands
  • performs the various shell expansions
  • redirections
  • execute the commands
  • wait for command to complete and collect the exit status

Quoting - remove the special meaning from characters

Escape Character

A non-quoted backslash \ is the Base escape character. It preserves the literal value of the next character that follows.

除了 \newline 有特殊意义。其他都表示转义。

Single Quotes

Enclosing characters in single quotes(') perserves the literal value of each character within the quotes. A single quote may not occur between single quotes, even when proceded by a backslash.

Double Quotes

Enclosing characters in double quotes (") perserves the literal value of all characters within the quotes, with the exception of $, ```, \.

ANSI-C Quoting

\a      alert
\e \E   an escape character
\n      newline

$(command) is the modern synonym for command , $() will evaluate this command result and then evaluate the reset of line.

echo $(pwd)/file

with be

echo /path/to/file

Curly braces (${}) are also unconditionally required when

  • expanding array elements, as in ${array[2]}

Shell Commands - the types of commands you can use

Shell Functions - the way you group commands by name

Two ways to define a function:

function functname {

}

or

functionName() {

}

Shell Parameters - how shell stores values

Shell Expansions - how bash expands parameters and the various expansions available

Redirections - a way to control where input and output go

Executing Commands - what happens when you run a command

Shell Scripts - executing files of shell commands

Double parentheses

Double parentheses are used for arithmetic operations:

((a++))

((meaning = 42))

for ((i=0; i<10; i++))

echo $((a + b + (14 * c)))

Bash built-in variables

if we have file test.sh

#! /bin/sh
echo '$#' $#
echo '$@' $@
echo '$?' $?

then run:

> ./test.sh 1 2 3

We will get:

$#  3
$@  1 2 3
$?  0

Explain:

$# = number of arguments. Answer is 3
$@ = what parameters were passed. Answer is 1 2 3
$? = was last command successful. Answer is 0 which means 'yes'

Tips

看一些 shell 脚本的时候发现了如下的写法

VAR1=${VAR1:-VAR2}

这个语句允许当 VAR1 为空时用 VAR2 来赋值。

${parameter:-word}
    If parameter is unset or null, the expansion of word is substituted.
    Otherwise, the value of parameter is substituted.

这个在 Bash 中叫做 parameter expansion ,更多的内容可以参考 Bash Hacker’s Wiki

使用举例

当 variable 不存在时,会默认使用后者

$ echo "$VAR1"

$ VAR1="${VAR1:-default value}"
$ echo "$VAR1"
default value

当 variable 存在时,则使用前者

$ VAR1="has value"
$ echo "$VAR1"
has value

$ VAR1="${VAR1:-default value}"
$ echo "$VAR1"
has value

reference


2018-12-14 linux , shell , bash , assignment

Linux 下安装 openssh server

一般服务器中会默认已经安装 SSH server,而个人版本的发行版,比如桌面版的 Ubuntu 或者我用的 Mint 可能默认没有安装,那么就需要自己安装配置。

SSH is program and protocol for securely logging into remote machines across a network. It allows you to run programs, and do a variety of tasks as if you were sitting at the machine. SSH is very similar to telnet except for it is with encryption to protect the transferred information and authentication.

安装

可以通过下面命令查看安装包

    sudo apt search ssh

或者 

    sudo apt search openssh

通过下面命令安装

    sudo apt install openssh-server

配置

    sudo vim /etc/ssh/sshd_config

来修改 ssh 默认的配置,可以通过修改 Port 来指定 ssh 默认监听端口 22

然后使用如下命令重启 ssh 服务

    sudo /etc/init.d/ssh restart

在其他电脑上可以使用 ssh name@address -p port 来连接。

openssh 和 ssh 包的区别

使用 apt search 的时候会发现,有 openssh 和 ssh 两个不同的 package

i A openssh-client                  - secure shell (SSH) client, for secure acce
p   openssh-server                  - secure shell (SSH) server, for secure acce
p   ssh                             - secure shell client and server (metapackag
v   ssh-client                      -                          
v   ssh-server                      -      

openssh 的包中又分为 server 和 client ,对于只需要安装 server 的机器来说只需要安装 server,客户端也单独安装就可以。而如果直接安装 ssh 的话,client 和 server 都会安装。使用 aptitude show ssh  可以查看 package 的具体信息。

reference


2018-12-12 linux , openssh , ssh

每天学习一个命令:gzip 压缩文件

gunzip 是个使用广泛的解压缩程序,它用于解开被 gzip 压缩过的文件,这些压缩文件预设最后的扩展名为”.gz”。 事实上 gunzip 就是 gzip 的硬连接,因此不论是压缩或解压缩,都可通过 gzip 指令单独完成。

命令格式

unzip  [-Z]  [-cflptTuvz[abjnoqsCDKLMUVWX$/:^]]  file[.zip] [file(s) ...]
   [-x xfile(s) ...] [-d exdir]

常用参数:

  • -c 将输出写到标准输出上,并保留原有文件
  • -d 把 .gz 文件解压出来
  • -l 查看当前压缩文件的信息。
  • -r 递归式地查找指定目录并压缩其中的所有文件或者是解压缩
  • -t 测试,检查压缩文件是否完整
  • -num 用指定的数字 num 调整压缩的速度,-1 或 –fast 表示最快压缩方法(低压缩比),-9 或 –best 表示最慢压缩方法(高压缩比)系统缺省值为 6
  • -v 对每一个压缩和解压的文件,显示文件名和压缩比。

使用实例

压缩

gzip file1

压缩直接使用 gzip 加上文件名,还可以指定压缩率默认是 6 ,最高是 9 最低是 1。

解压缩

-d 表示解压缩, 等同于 --decompress-v 表示 verbose,打印更多的日志:

gzip -dv file.gz

显示压缩包内容

gzip -l file.gz

可以显示压缩之后的大小 解压缩之后的大小 压缩率是多少,大概像下图这样。

compressed       uncompressed         ratio          uncompressed_name
  23124234          110229            60.4%          file.gz

合并多个 gzip 文件

可以使用 cat 来合并多个 gzip :

cat 1.gz 2.gz 3.gz > total.gz

2018-12-11 linux , gzip , tar , command

Set up Drools Workbench with tomcat

在本机使用 Tomcat 启动 Drools Workbench,通常情况下直接使用 Docker 起就行了,但是为了学习 KIE Drools Workbench,这边就补习一下 Tomcat 启动方式。

基本的准备工作包括:

  • JDK, JAVA_HOME
  • Tomcat
  • Drools 相关工具
  • sudo 使用权限

installation

Make sure you have Tomcat installed.

Follow this link to download the drools workbench. And choose the Tomcat version to download.

You will get kie-drools-wb-7.11.0.Final-tomcat8.war file. And you can just rename it to kie-drools-wb.war .

And copy(deploy) the .war to TOMCAT/webapps directory. 这个名字决定了在 URL 中的路径,需要注意下。

dependency jars

Download following related jars and copy them to TOMCAT/lib:

  • btm-2.1.4.jar
  • btm-tomcat55-lifecycle-2.1.4.jar
  • h2-1.4.193.jar
  • jta-1.1.jar
  • slf4j-api-1.7.2.jar
  • slf4j-jdk14-1.7.2.jar
  • kie-tomcat-integration-7.11.0.Final.jar kie-tomcat-integration
  • javax.security.jacc-api-1.6.jar JACC (javax.security.jacc:artifactId=javax.security.jacc-api)

These jars can be found at https://mvnrepository.com/

conf

Firstly, modify the vim tomcat\conf\tomcat_user.xml file, and add following between <tomcat-users></tomcat-users>. This file control the privilege of tomcat which is used by KIE:

<role rolename="admin"/>
<role rolename="analyst"/>
<role rolename="user"/>
<role rolename="kie-server"/>
<role rolename="manager"/>
<role rolename="manager-gui"/>
<role rolename="manager-status"/>
<user username="workbench" password="workbench" roles="admin,kie-server"/>
<user username="kieserver" password="kieserver" roles="kie-server"/>
<user username="admin" password="admin" roles="admin,manager-gui,manager-status,manager, user"/>

Note, analyst or admin roles is required to be authorized to use kie-wb.

Secondly, create setenv.sh (or setenv.bat) under tomcat/bin/

TOMCAT_HOME="/opt/tomcat/"
DATA_PATH="/home/einverne/data/kie-wb"
CATALINA_OPTS="-Xmx512M  \
            -XX:MaxPermSize=512m  \
            -Dbtm.root=$TOMCAT_HOME \
            -Dbitronix.tm.configuration=$TOMCAT_HOME\conf\btm-config.properties \
            -Djbpm.tsr.jndi.lookup=java:comp/env/TransactionSynchronizationRegistry \
            -Djava.security.auth.login.config=$TOMCAT_HOME\webapps\kie-drools-wb\WEB-INF\classes\login.config \
            -Dorg.jboss.logging.provider=jdk \
            -Dorg.uberfire.nio.git.dir=$DATA_PATH \
            -Dorg.uberfire.nio.git.ssh.cert.dir=$DATA_PATH \
            -Dorg.guvnor.m2repo.dir=$DATA_PATH/repo \
            -Dorg.uberfire.metadata.index.dir=$DATA_PATH"

NOTE: On Debian based systems $CATALINA_HOME needs to be replaced with $CATALINA_BASE. ($CATALINA_HOME defaults to /usr/share/tomcat8 and $CATALINA_BASE defaults to /var/lib/tomcat8/)

NOTE: this is an example for unix like systems for Windows $CATALINA_HOME needs to be replaced with windows env variable or absolute path

NOTE: java.security.auth.login.config value includes name of the folder in which application is deployed by default it assumes kie-drools-wb so ensure that matches real installation. login.config file can be externalized as well meaning be placed outside of war file.

还有一个需要注意的是,如果想要在 Drools Workbench 中使用 git clone ssh://admin@localhost:8001/ 这样的工具,有两点需要特别注意,一个就是这个配种中的

-Djava.security.auth.login.config=$TOMCAT_HOME\webapps\kie-drools-wb\WEB-INF\classes\login.config \

这个 login.config 地址一定要配置正确,尤其是 webapps 后面的路径,不同的环境可能配置的路径不一样。第二点就是在上面的角色配置中需要启用一个叫做 user 的角色,并且将自己的用户名配置到 user 角色下。1 2

Then add valve configuration into TOMCAT_HOME/conf/server.xml inside Host element as last valve definition:

<Valve className="org.kie.integration.tomcat.JACCValve" />

Create btm-config.properties file under tomcat/conf and add this:

bitronix.tm.serverId=tomcat-btm-node0
bitronix.tm.journal.disk.logPart1Filename=%{btm.root}%\work\btm1.tlog
bitronix.tm.journal.disk.logPart2Filename=%{btm.root}%\work\btm2.tlog
bitronix.tm.resource.configuration=%{btm.root}%\conf\resources.properties

Create resources.properties file under tomcat/conf:

resource.ds1.className=bitronix.tm.resource.jdbc.lrc.LrcXADataSource
resource.ds1.uniqueName=jdbc/jbpm
resource.ds1.minPoolSize=10
resource.ds1.maxPoolSize=20
resource.ds1.driverProperties.driverClassName=org.h2.Driver
resource.ds1.driverProperties.url=jdbc:h2:mem:jbpm
resource.ds1.driverProperties.user=sa
resource.ds1.driverProperties.password=
resource.ds1.allowLocalTransactions=true

打开 KIE

打开 http://localhost:8080 端口应该能看到 Tomcat 页面,在 Tomcat 管理 app 页面中 (http://localhost:8080/manager/html) 启动 KIE

在列表中会看到 kie-wb 这个项目,初始状态应该是 stop 状态,点击启动,观察 Tomcat 下 logs/catalina.out 应该能够看到启动日志,如果出现错误,需要处理一下。通常情况下可能会出现创建目录失败的问题,手动创建目录并给予写入权限即可。

等待启动后,点击左边的 path 进入应用,使用 Tomcat 中配置的权限登录。

reference


2018-12-02 drools , kie , drools-workbench , tomcat , linux , rule-engine

Tomcat 安装及使用

DescriptionApache Tomcat implements several Java EE specifications including Java Servlet, JavaServer Pages, Java EL, and WebSocket, and provides a “pure Java” HTTP web server environment in which Java code can run.

Apache License 2.0

installation

Firstly, make sure jdk is installed.

Download the latest binary release of Tomcat from the official site

wget http://apache.website-solution.net/tomcat/tomcat-8/v8.5.41/bin/apache-tomcat-8.5.41.tar.gz
sudo tar zxvf apache-tomcat-8.5.41.tar.gz -C /opt/
sudo ln -s /opt/apache-tomcat-8.5.41 /opt/tomcat

Then, for security purposes, Tomcat should be run as an unprivileged user( not root), so we have to create necessary group and users.

sudo groupadd tomcat

And create a tomcat user. And we make this user a member of tomcat group, with a shell of /bin/false( so nobody can log into the account ), and with a home directory of /opt/tomcat (where we install Tomcat) sudo useradd -s /bin/false -g tomcat -d /opt/tomcat tomcat

Now, tomcat user is set up.

Give the tomcat group ownership over the entire installation directory:

sudo chgrp -R tomcat /opt/tomcat

And give the tomcat group read access to the conf directory and all of its contents.

sudo chmod -R g+r conf
sudo chmod g+x conf
sudo chown -R tomcat webapps/ work/ temp/ logs/

Create a systemd Service file

sudo vim /etc/systemd/system/tomcat.service

And paste this:

[Unit]
Description=Apache Tomcat Web Application Container
After=network.target

[Service]
Type=forking

Environment=JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre
Environment=CATALINA_PID=/opt/tomcat/temp/tomcat.pid
Environment=CATALINA_HOME=/opt/tomcat
Environment=CATALINA_BASE=/opt/tomcat
Environment='CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC'
Environment='JAVA_OPTS=-Djava.awt.headless=true -Djava.security.egd=file:/dev/./urandom'

ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh

User=tomcat
Group=tomcat
UMask=0007
RestartSec=10
Restart=always

[Install]
WantedBy=multi-user.target

And remember to replace your own java home.

Next, reload the systemd daemon

sudo systemctl daemon-reload
sudo systemctl start tomcat
sudo systemctl status tomcat

How you can visit http://localhost:8080 to check Tomcat page. Oh if you have ufw installed, don’t forget to allow traffic by:

sudo ufw allow 8080

And finally, if you want to go with Tomcat when start up, you can enable Tomcat automatically starts at boot:

sudo systemctl enable tomcat

management

In order to use the manager web app that comes with Tomcat, we must add a login to our Tomcat server.

sudo vim /opt/tomcat/conf/tomcat-users.xml

and edit:

<tomcat-users . . .>
  <user username="admin" password="password" roles="manager-gui,admin-gui"/>
</tomcat-users>

Restart your server

sudo systemctl restart tomcat

you can get what you want:

http://localhost:8080/manager/html
http://localhost:8080/host-manager/html

questions

修改 Tomcat 端口

修改 conf 目录下 server.xml 文件,修改 http 访问端口,默认为 8080

<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />

搜索 Connector port 修改这里的端口号。

reference


2018-12-01 tomcat , java-ee , java-web

MySQL 中的日志配置和管理

MySQL 中默认是没有开启日志记录的,所以需要手动修改配置文件开启日志。而在 MySQL 中我们需要关心的有三类日志:

  • The error log 错误日志,这一类日志包括了服务器运行时,包括启动和停止时的错误等信息
  • The General Query Log 查询日志,通常是 mysqld 进行连接,断开连接,查询等等操作的日志
  • The Slow Query Log 慢查询日志,包含了慢查询的 SQL 语句

在配置中没有开启任何日志记录时,MySQL 相关的日志在 /var/log/syslog 中。之所以关注到 MySQL 的日志是因为发现服务器上 MySQL 间隔一定时间就自动重启,想要找出原因,开启日志之后观察了一段时间,初步排查可能是 clamav 执行时占用内存导致 MySQL 内存不足挂掉。现在把所有的日志都开启观察一段时间再看看。

开启日志

修改 /etc/mysql/my.cnf 中的配置,开启 MySQL 服务器日志,不同发行版的配置地址可能不相同:

[mysqld_safe]
log_error=/var/log/mysql/error.log

[mysqld]
log_error=/var/log/mysql/error.log

开启查询日志

general_log_file        = /var/log/mysql/mysql.log
general_log             = 1

开启慢查询日志

log_slow_queries       = /var/log/mysql/mysql-slow.log
long_query_time = 2
log-queries-not-using-indexes

修改保存配置之后需要重新启动 mysql

sudo service mysql restart

在运行时开启日志

如果不想重启服务器,可以在运行时,通过 mysql client 登录之后 mysql -u root -p 然后执行命令来开启:

SET GLOBAL general_log = 'ON';
SET GLOBAL slow_query_log = 'ON';

reference


2018-11-29 mysql , log , database , linux

Java 查漏补缺之:ThreadLocal 使用

ThreadLocal 线程本地变量,变量为线程独有,每个线程保存变量的副本,对副本的改动,对其他的线程而言是透明的。

特性

  • The object is not shared between threads, so it can be used without the need for synchronization
  • it is available throughout the life of the thread, meaning you don’t have to pass it all over the place through method calls

适用场景

  • 每个线程都需要自己的数据存储对象
  • 实例被多个方法共享,但是不希望多线程共享
  • 空间换时间的场景

一个典型的例子就是用 ThreadLocal 来保存一次请求的 Session 数据,程序的不同地方可能需要读取 Session 的内容,也要往 Session 中写入数据。

construct an instance

Create instance with new:

ThreadLocal<Integer> threadLocalValue = new ThreadLocal<>();

Or using the withInitial() static method:

ThreadLocal<Integer> threadLocal = Threadlocal.withInitial(() -> 1);

常用方法

// 设置当前线程的线程局部变量的值
void set(Object value);

// 该方法返回当前线程所对应的线程局部变量
public Object get();

// 将当前线程局部变量的值删除
public void remove();

ThreadLocal 类允许我们创建只能被同一个线程读写的变量,通常的用法是当有一些 Object 不是线程安全,但是又想避免使用同步 访问机制时。比如 SimpleDateFormat,因此可以使用 ThreadLocal 来给每一个线程提供一个线程自己的对象。

public class Foo
{
    // SimpleDateFormat is not thread-safe, so give one to each thread
    private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
        @Override
        protected SimpleDateFormat initialValue()
        {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        }
    };

    public String formatIt(Date date)
    {
        return formatter.get().format(date);
    }
}

ThreadLocal 对象是给定线程中对象的引用,因此在服务端使用线程池时极有可能造成 classloading leaks 内存泄露 ,在使用时需要特别注意清理。ThreadLocal 持有的任何对象空间在 Java 永久堆上,即使重新部署 webapp 也不回收这部分内存,可能造成 java.lang.OutOfMemoryError 异常。

原理

利用 HashMap,在 map 中保存了 Thread 名 -> 线程变量的关系,因此在多线程之间是隔离的,但同时也耗费了内存空间。

reference


2018-11-28 java , thread

电子书

本站提供服务

最近文章

  • 2024 年台北之行 去年的时候就得知了海外的大陆人可以通过官方网站申请入台证,从而可以在海外直接入境台湾,所以 4 月份女朋友过来日本之后就通过线上系统申请了入台证,入台证申请通过并付费之后是只有 3 个月有效期的,因为我们申请的比较晚,所以有效期的三个月正好落在了最热的 7,8,9 月份,但考虑到暑假有假期,我们还是决定硬着头皮买了机票。
  • macOS 上的多栏文件管理器 QSpace QSpace 是一个 macOS 上的多窗口平铺的文件管理器,可以作为 Finder 的代替,在 Windows 上曾经用过很长时间的 [[Total Commander]],后来更换到 Linux Mint 之后默认的文件管理器自带多面板,反而是用了很多年 macOS ,才意识到原来我缺一个多窗口,多面板的文件管理器。
  • Dinox 又一款 AI 语音转录笔记 前两天介绍过 [[Voicenotes]],也是一款 AI 转录文字的笔记软件,之前在调查 Voicenotes 的时候就留意到了 Dinox,因为是在小红书留意到的,所以猜测应该是国内的某位独立开发者的作品,整个应用使用起来也比较舒服,但相较于 Voicenotes,Dinox 更偏向于一个手机端的笔记软件,因为他整体的设计中没有将语音作为首选,用户也可以添加文字的笔记,反而在 Voicenotes 中,语音作为了所有笔记的首选,当然 Voicenotes 也可以自己编辑笔记,但是语音是它的核心。
  • Emote 又一款 AI 语音笔记应用 继发现了 Voicenotes 以及 Dinox 之后,又发现一款语音笔记 Emote,相较于前两款应用,Emote 吸引我的就是其实时转录的功能,在用 Voicenotes 的时候时长担心如果应用出现故障,没有把我要录下来的话录制进去,后期怎么办,而 Emote 就解决了这个问题,实时转录的功能恰好作为了一个声音录制的监听。
  • 音流:一款支持 Navidrome 兼容 Subsonic 的跨平台音乐播放器 之前一篇文章介绍了Navidrome,搭建了一个自己在线音乐流媒体库,把我本地通过 [[Syncthing]] 同步的 80 G 音乐导入了。自己也尝试了 Navidrome 官网列出的 Subsonic 兼容客户端 [[substreamer]],以及 macOS 上面的 [[Sonixd]],体验都还不错。但是在了解的过程中又发现了一款中文名叫做「音流」(英文 Stream Music)的应用,初步体验了一下感觉还不错,所以分享出来。