1.不要调用Thread类或者Runnable对象的run方法。
- 直接调用run方法只会在同一个线程中执行任务,而没有启动新的线程;
- 应当调用Thread.start方法,这会创建一个新线程来执行run方法;
2.现在线程优先级高度依赖系统。
- Java有10个线程优先级(1~10),10最高,默认为5;
- Windows有7个优先级别;面向Linux的Oracle JVM,会完全忽略线程优先级;
- Java线程的优先级会映射到主机平台优先级;
- 现在的Java线程优先级设定不是很有用,尽量不要使用了;
3. ReentrantLock(可重入)锁可以反复获取已拥有的锁。
- 内部持有计数器来跟踪对lock方法的嵌套调用;每一层调用都需要unlock来释放;
4. 条件对象:Condition。
- 一个锁对象可以有一个或者多个关联的条件对象。
- 已获得锁的对象,当不满足条件对象的条件时,会暂停并释放锁;当条件满足后,又会从之前暂停的地方开始继续执行;
public void transfer(int from, int to, int amount) {
bankLock.lock();
try {
while (accounts[from] < amount) { // 要用while,因为唤醒后还得再次检查条件
// sufficientFunds = bankLock.newCondition();
sufficientFunds.await();
// transfer funds ...
sufficientFunds.signalAll(); // 会激活等待这个条件的所有线程
}
}
}
- 只有当线程拥有一个条件的锁时,才能在这个条件上调用await、signalAll或者signal方法;
5. synchronized 关键字(内部对象锁);
- 每个Java对象都有一个内部锁;如果在一个方法上使用synchronized关键字,调用这个方法时,线程必须获得内部对象锁;
- 每个内部对象锁只有一个关联条件;
- 最好既不使用Lock/Condition,也不使用synchronized关键字,可以使用java.util.concurrent包的某些机制(比如阻塞队列)来更好的完成并发处理;
6. volatile字段
如果为了能并发读写一两个实例字段,可以不使用锁,而是使用volatile字段实现;
- 标记为volatile的字段,编译器和虚拟机就会考虑到该字段可能被另一个线程更新;
- 标记为volatile的字段,每次被某一线程修改时,最新值都会对所有线程可见;
- volatile变量不能提供原子性;
// 无法保证读取、取反和写入操作不被中断;
public void flipDone() { done = !done; } // not atomic
7. stop和suspend方法已经废弃,不要使用;
- stop方法可能会让对象被破坏,即未完成完整操作就被终止了;
- suspend方法会挂起持有锁的线程,导致线程恢复前锁是不可用的;如果调用suspend方法的线程试图获取同一个锁,程序就会死锁;
8. 非线程安全的类可以通过ThreadLocal包装,来让各个线程独立访问;
public static final ThreadLocal<SimpleDateFormat> dateFormat =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
String dateStamp = dateFormat.get().format(new Date);
网友评论