ThreadLocal简介

2016 Dec 3

在单线程的程序里,可以通过static变量或者singleton对象的方式定义全局的、共享的变量。 在多线程的程序里,如果想定义在一个线程内部共享的变量,可以考虑使用ThreadLocal

ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread

一般来说,ThreadLocal对象会被声明成类的私有静态成员,比如下面这个例子来自ThreadLocal的javadoc

 public class ThreadId {
     // Atomic integer containing the next thread ID to be assigned
     private static final AtomicInteger nextId = new AtomicInteger(0);

     // Thread local variable containing each thread's ID
     private static final ThreadLocal<Integer> threadId =
         new ThreadLocal<Integer>() {
             @Override protected Integer initialValue() {
                 // 让不同的线程有不同的初始值
                 return nextId.getAndIncrement();
         }
     };

     // 不同的线程调用threadId.get()会返回不同的值,每个线程都有其各种的拷贝
     public static int get() {
         return threadId.get();
     }
 }
ThreadLocal的实现

每个线程对应的Thread对象里存在着一个map(所以不同线程的map相互隔离,没有竞争),

// 摘自Thread类的定义
ThreadLocal.ThreadLocalMap threadLocals = null;

ThreadLocalMap的实现是一个hash表,定义如下,

static class ThreadLocalMap {
	static class Entry extends WeakReference<ThreadLocal<?>> {
		/** The value associated with this ThreadLocal. */
		// ThreadLocal对象关联的对象
		Object value;
		
		Entry(ThreadLocal<?> k, Object v) {
			super(k);
			value = v;
		}		
	}
	
	private Entry[] table;
}      

map中的key由ThreadLocal对象们担当,value则是ThreadLocal对象所关联的对象。

下面是ThreadLocal::get()的伪代码(真实的实现考虑了threadLocals的lazy initialization),

public class ThreadLocal<T> {
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap.Entry e = t.threadLocals.get(this);
        if (e == null) {
            t.threadLocals.set(this, this.initialValue());
        }
        return (T)e.value;
    }
} 
ThreadLocal和内存泄漏

Java生态圈工具和技术的2016年度报告

2016 Nov 29

关于Java生态圈中的工具和技术的2016年度调查报告。 报告基于大约两千多份问卷,问卷时间为2016年三、四月份。 报告一共分为三个部分,除了上面的链接,还有Pivoting dataTrends and Historical data

一些有意思的点:

  • 三分之一的受调查者采用了微服务架构。
  • 采用了微服务架构的受调查者中,60%的人觉得微服务架构并没有让开发变得更简单🤔。
  • IntelliJ超过了Eclipse成为了最受欢迎的IDE。
  • MongoDB是最受Java开发者欢迎的NoSQL数据库。
  • SVN仍然占据着23%的份额😨。
  • VisualVM是最受欢迎的profiler(38%),其次是JProfiler(16%);超过三分之一不做profiling🤔。
  • 采用了微服务架构的开发者中接近二分之一使用Spring Boot做为web框架。

要不要使用ORM框架

2016 Nov 26

到底应不应该在项目中使用ORM(Object-relational mapping)框架?

来自Martin Fowler的一篇文章,OrmHate。文章的观点是,

关系型数据库和内存对象的相互映射本来就是一个很难的问题: 因为两者属于两种不同的建模方式,而且还要考虑两者的数据一致性(特别是在并发访问/修改的情况下)。 一些开发者对ORM框架有着不实际的期待——他们只想处理(灵活的)内存对象,期待ORM框架能(完美)处理对数据库的操作。 实际上,如果使用了关系型数据库,那么就应该在开发中考虑关系型数据库带来的影响。 如果ORM框架能解决80%的问题,那么使用它就是值得的。 另外,如果不需要双向的映射(bi-directional mapping),或者NoSQL数据库适用于业务场景,可以考虑不用ORM框架。

另一篇文章,ORM Haters Don’t Get It,更细节一些。文章的观点是,

即使不使用现成的ORM框架,也会在项目中创造出一个功能类似ORM框架的东西。 使用ORM还有一些额外的好处,比如方便切换数据库、现成的缓存实现等。 需要防止错误地使用ORM框架(需要熟悉ORM框架,ORM不是一个傻瓜式框架)。 另外ORM框架的session管理一般比较复杂,想要配置好需要一定的经验。

一些想法,

  • 考虑一下NoSQL是否能满足业务需求。
  • 考虑一下轻量级的ORM框架,比如MyBatis,能否满足要求。
  • 使用ORM框架时,考虑显式地使用native query(有时候ORM框架生成的SQL可能并不理想)
  • 开发过程中打印出ORM框架所执行的SQL语句,确认ORM框架的行为符合性能要求和预期。比如通过执行的SQL语句可以很容易地发现N+1问题

使用ORM框架(比如Ruby on Rails的ActiveRecord)的另外一个好处是可以快速地进行原型开发。

SAM type和Lambda

2016 Nov 23

这是一份Java 8 Lambda的早期设计文档。 文档里Lambda的语法和最终的实现有一些细微的区别,但是不影响阅读。 最终的Lambda设计文档见这里

Lambda也叫“闭包(closure)”。 在Java 8之前,anonymous inner class可以认为是一种闭包——它可以capture enclosing class里定义的变量,封装一段或者几段代码以便稍后执行。 anonymous inner classes的缺点是语法太啰嗦、this不是lexically scoped、不能capture非final变量等。 Java 8引入Lambda是为了解决这些问题。

Java 8在引入Lambda时,并没有引入新的function type;而是借助SAM type来实现Lambda。 SAM(single abstract method) type是指只有一个抽象方法的接口。

Lambda expressions can only appear in context where it will be converted to a variable of SAM type.

Lambda表达式都会转化为一个SAM类型的变量。 凡是能接收SAM类型的地方,比如方法的参数,都可以接收一个Lambda表达式。

Comparator<String> c = (String s1, String s2) -> s1.compareToIgnoreCase(s2);

Java 8的Lambda表达式和anonymous inner class的一个不同之处是它们对this的解读。 Lambda表达式中的this指的是enclosing class中的this。 anonymous inner class中的this指的是inner class自己的this

Kotlin,又一种JVM语言

2016 Nov 23

Kotlin是JetBrains开发的的一种JVM语言。

  • 比Java简洁
  • 没有烦人的NullPointerExceptions
  • 多用途,for the JVM, Android and the browser
  • 可以和Java代码混着写,100% Java Interoperability

相比Java,没有raw types,没有checked exceptions,类型系统保证的null safety,……。详细对比。 Java和Kotlin的语法比较

Kotlin很像Swift,或者说Swift很像Kotlin。

Comments and Rate Limit

2016 Nov 17

Now OneFeed supports commenting. Since post pages are cached for quick access, a tiny modification is needed to accept POST request initiated from the cached pages. authenticity_tokens in comment creation form of cached pages are usually out-of-date.

<form action="/posts/post-x-link/comments" method="post">
  <input type="hidden" name="authenticity_token" value="zzcjgpp55...+yxjDy4LmydrMVqsAfQ/+igqKDcIAg==">

Rails uses these authenticity_tokens to prevent CSRF attack. Skip this protection for comment :create method.

class CommentsController < ApplicationController
  protect_from_forgery except: :create
Rate Limit

And a rate limit is set for POST request of comment creation. It’s simply implemented on top of Redis.

def exceed_limit?
  key = request.remote_ip
  if EXISTS key
    return true # the ip has been submitted a comment lately
  else
    INCR key
    EXPIRE key, CREATION_RATE_LIMIT
    return false
  end
end

安全地存储用户密码

2016 Nov 16

在存储密码的数据库被攻破的前提下,破解密码的成本越高则密码的安全性就越高。 一般来说,密码在存储到数据库前都经过hash加密。 需要考虑hash函数的性能对密码安全性的影响。 比如对于SHA256来说,当前的普通计算机能在一秒内计算数百万的hash值,定制的计算机则可以提供每秒数十亿次的hash运算。 在这种情况下,很多密码安全的设计都变得不再安全。

首先,最不安全的方式是明文存储密码。 其次,对密码明文进行sha1加密、对密码明文进行sha1加密时加上固定的salt值、为每个用户分配不同的salt值都是不安全的。

推荐使用bcrypt加密密码。 bcrypt相比sha1等来说更慢,这意味着破解密码的cpu成本更高。 比如log_rounds值设为10时,大概需要100毫秒来计算hash值。 还可以通过增加log_rounds值来抵消未来计算机运算能力的提高。 一些bcrypt实现还原生支持per-user-salt。

Dropbox也主要采用了per-user-salt加bcrypt来存储用户密码。 Dropbox在用bcrypt hash之前,会先用SHA512对密码做预处理——把密码hash成512 bit的定长字符串。 预处理有两个作用:一是对于很短的密码,提高它的entropy(熵);二是防止输入密码过长造成潜在的DoS attacks风险(bcrypt很“慢”)。 Dropbox还引入了global pepper(既有“盐”又有“胡椒粉”)——对bcrypt的hash值再用AES256做进一步的加密。 pepper存储在数据库之外的地方。AES256是对称加密,方便未来更新pepper值。

Ruby on Rails框架的历史

2016 Nov 15

Rails从0.9.3版本开始到当前版本的所有Changelogs。 自“底”向“上”过一遍这些Changelogs,可以了解一个web框架(该有)的功能和演化。

Changelog(总)是了解一个软件(新)功能的最佳文档。

Weak References in Java

2016 Nov 12

一篇非常好的关于Java的“弱引用”的文章。

WeakReference和SoftReference

Java里的引用有四种级别:strong、soft、weak、phantom。Strong reference最常见。 但是strong reference并不适用所有的场景。比如,用strong reference来实现cache时,要么自己动手 决定什么时候应该evict缓存的对象(这在某种意义上是重复了JVM的工作——进行垃圾回收),要么承担内存 泄漏的风险(缓存的对象做为strong references一直被cache自己引用着,使得这些对象一直不能被回收)。

这种情形下,可以考虑使用较弱的引用,比如WeakReference或者SoftReference, 来实现缓存(把什么时候应该evict缓存对象的决定权交给JVM)。

A weak reference, simply put, is a reference that isn’t strong enough to force an object to remain in memory.

SoftReference在使用上和WeakReference类似,不过SoftReference指向的对象相比于WeakReference要存活得更久一些。 一般来说,只要可用内存充足,soft reference指向的对象就不会被回收。所以在实现cache时,可能使用SoftReference更好一些, 使用WeakReference会使得缓存失效的概率大大增加。

An object which is only weakly reachable (the strongest references to it are WeakReferences) will be discarded at the next garbage collection cycle, but an object which is softly reachable will generally stick around for a while.

PhantomReference、ReferenceQueue和Finalization

a fundamental problem with finalization: finalize() methods can “resurrect” objects by creating new strong references to them

Well, the problem is that an object which overrides finalize() must now be determined to be garbage in at least two separate garbage collection cycles in order to be collected.

Tag Cloud, Responsive UI and Performance Tuning

2016 Nov 12

Now OneFeed has a tag cloud, more responsive UI across desktop and phone browsers. Also performance of this site is improved a bit.

Tag Cloud

Both Javascript and CSS can implement tag cloud. A JQuery plugin called jQCloud can produce very beautiful tag cloud. But considering Javascript may be disabled, a CSS implementation is more reliable. My implementation is largely based on this CSS tag cloud.

tag cloud

Responsive UI

responsive ui

First of first to implement a responsive UI,

<meta name="viewport" content="width=device-width, initial-scale=1.0">