- 每创建一个线程都要消耗内存,每个线程都有自己的栈空间(大约1 M),也就是说每创建一个线程,操作系统就会给该线程分配1 M的空间。而且当线程执行完时间片后需要把数据保存至内存或硬盘中,下一个线程执行时又要从内存或硬盘中取出对应自己的数据,这么一存一取也很耗时间。这就是为什么线程开太多的话,会导致执行任务所花费的时间更多的原因。
- Thread和Runnable不是一对一的关系,而是一对多的关系。比如线程池里的线程就总是有限个,但是Runnable却可以有多个,丢给Thread执行。
- Thread的isInterrupted与static的interrupted的区别在于static方法调用后如果返回true,那么内部会将该boolean值又改回false。而isInterrupted不会。
- Join方法可以让两个线程顺序执行
- 当一个Thread调用sleep()或wait()方法后,若其它线程调用该线程的interrupt()的话,这两个方法就会抛出InterruptedException。但是抛出异常后,它们又会把这个中断标志位重新置为false。
- 线程A调用yield后,会从运行状态转为就绪状态。让操作系统去调用其它的线程,当然操作系统可能又会再调起线程A
- 如果想在Runnable的run方法中中断线程,可以如下:
class TestRunnable implements Runnable {
public void run() {
while(!Thread.currentThread().isInterrupted()) {
...
}
}
}
- 对象锁与类锁的区别
private synchronized void print() {//同步监视器是当前对象
...
}
private static synchronized void print() {//同步监视器是当前类的class对象
...
}
- Thread的yield和sleep不会释放锁。wait会释放锁
- wait()被唤醒后线程必须重新获得锁才能继续执行wait()后面的逻辑,而notify()/notifyAll()虽然会唤醒wait()但是并不会释放锁,所以只能等到notify所在方法执行完毕后才会释放锁,wait()所在线程才会有机会获得锁。
- synchronized锁在等待锁时不能中断,不具有尝试获取锁的机制,只存在2中情况:1、获得锁了,执行业务代码;2、没有获得锁,等待锁。
- Lock是显示锁,synchronized是隐式锁。之所以这么称呼,是因为synchronized的获取锁和释放锁都有JDK自动完成,而Lock的获取锁与释放锁需要程序员手动操作
- lock.unlock()一定要在finally中调用,这样才能确保锁一定会被释放
public void test() {
lock.lock();
try {
...
} finally {
lock.unlock();
}
}
-
synchronized支持可重入锁
-
公平锁:先申请的线程一定先拿到锁;非公平锁:后申请锁的线程可能先拿到锁。synchronized和ReentrantLock默认都是非公平锁。非公平锁的性能更好。
-
线程从运行状态转成阻塞状态以及从阻塞状态转成就绪状态都是花费一定时间。考虑下这么一种情况:线程B处于阻塞状态,等待线程A释放锁,当A释放锁的时候,线程C处于运行状态恰好也需要锁。如果是公平锁的话,那么就必须严格按照先是B获得锁,B释放锁,C获得锁的顺序执行。这时需要经历以下阶段:
1、B从阻塞转成就绪再转成运行
2、C从运行转成阻塞
3、B执行完毕,释放锁
4、C从阻塞转成就绪再转成运行
5、C执行完毕,释放锁
如果是非公平锁的话,那么就有可能是C先获得锁,所需经历的阶段就变成如下:
1、C执行完毕,释放锁
2、B从阻塞转成就绪再转成运行
3、B执行完毕,释放锁
减少了线程C的状态转换,所以非公平锁的性能更好。 -
ReentrantLock可在构造函数中传入true变成公平锁,默认是非公平锁。而synchronized只能是非公平锁,无法改变。
-
读写锁,写锁排斥读锁和其他写锁,而读锁只排斥写锁。读写锁以外的其它类型的锁都是排斥其它所有的锁的。
-
使用synchronized时可以通过wait、notify/notifyAll进行线程间的协作。使用lock时可以通过Condition接口进行线程间的协作。
-
Callable、AutoInteger、Lock、读写锁、Condition接口要学习下