1.死锁:
死锁很容易理解,通俗的说就是所有的线程都因为条件没有达到而都被阻塞了,这里就不多说了。
2.线程局部变量:
前面说了线程间共享变量的风险,有时候可能要避免共享变量,使用ThreadLocal辅助类为各个线程提供各自的实例。例如:
public static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
//这个时候有两个线程操作下面的步骤:
String dateStamp = dateFormat.format(new Date());
结果可能很混乱,因为dateFormat使用内部数据结构可能会被并发的访问破坏。可以使用如下方式:
public static final ThreadLocal<SimpleDateFormat> dateFormat =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
//访问时:
String dateStamp = dateFormat.get().format(new Date());
在一个给定线程中首次调用get()时,会调用initialValue方法。
在多个线程中生成随机数也存在类似的问题。java.util.Random类是线程安全的。但是如果多个线程需要等待一个共享的随机数生成器,这会是很低效的。同样可以使用ThreadLocal辅助类:
int random = ThreadLocalRandom.current().nextInt(upperBound);
ThreadLocalRandom.current()调用会返回特定于当前线程的Random类实例。
2.1java.lang.ThreadLocal<T> 1.2:
T get()
得到这个线程的当前值。如果首次调用get,会调用initialize来得到这个值。
protected initialize()
应覆盖这个方法来提供一个初始值。默认情况下,这个方法返回null。
void set(T t)
为这个线程设置一个新值。
void remove()
删除对应这个线程的值。
static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) 8
创建一个线程局部变量,其初始值通过调用给定的supplier生成。
2.2java.util.concurrent.ThreadLocalRandom 7:
static ThreadLocalRandom curent()
返回特定于当前线程的Random类实例。
3.锁测试和超时:
线程在调用Lock方法获得另一个线程所持有的锁的时候,很可能发生阻塞。应该更加谨慎地申请锁。tryLock方法试图申请一个锁,在成功获得锁之后返回true,否则,立即返回false,而且线程可以立即离开去做其他事情。
在调用tryLock时,还可以使用超时参数:
if(myLock.tryLock(100,TimeUnit.MILLSECONEDS))...
设置超时参数主要为了防止线程在等待获得锁时如果被中断可以抛出InterruptedException异常,防止程序死锁导致Lock方法无法被终止。也可以调用lockInterruptibly方法,它就相当于一个超时设置为无限的tryLcok方法。
同样,在等待一个条件时,也可以设置超时:
myCondition.await(100,TimeUnit.MILLSECONEDS)
如果一个线程被另一个线程通过调用signalAll或signal激活,或者超时时限已经达到,或者线程被中断,那么await方法将别返回。
如果等待的线程被中断,await方法将抛出InterruptedException异常。如果希望继续等待,可以使用awaitUninterruptibly方法替代await。