Java 中是如何实现线程同步的?
-
使用synchronized 关键字实现的同步代码块、同步方法(悲观锁)
-
使用特殊域变量(volatile)实现线程同步(保证 可见性、有序性。比如说使用volatile实现 线程安全的双检查锁单例模式)
-
ThreadLocal(每个线程获取的都是该变量的副本)
-
使用 可重入锁 实现线程同步(相对 synchronized 锁粒度更细了,效率更高)
-
在java.util.concurrent 包,即常说的JUC,提供了很多并发安全类,可以支持同步。
-
如:ReentrantLock 类是可重入、可中断、可设置超时时间、可设置公平锁、可支持多个条件变量的锁,是 Lock 接口实现类
-
ReentrantLock() : 创建一个 ReentrantLock 实例
-
ReentrantLock 实例.lock() : 获得锁
-
ReentrantLock 实例.unlock() : 释放锁
-
-
-
java.util.concurrent.atomic 包 (乐观锁)
- 方便程序员在多线程环境下,无锁的进行原子操作,原理是CAS(可以从这里引入到CAS上面)
-
使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,
副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。变量局部化。
阻塞线程的方式有哪些?
当发生如下情况时,线程将会进入阻塞状态:
- 线程调用sleep()方法主动放弃所占用的处理器资源;
- 线程调用了一个阻塞式IO方法,在该方法返回之前,该线程被阻塞;
- 线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有;
- 线程在等待某个通知(notify);
- 程序调用了线程的suspend()方法将该线程挂起,但这个方法容易导致死锁,所以应该尽量避免使用该方法。
终止线程的方式有哪些?
stop 方法终止线程(线程不安全,不推荐)
直接使用thread.stop()来强行终止线程,线程会立即终止执行,可能会产生不可预料的结果。在调用thread.stop()后会导致该线程所持有的所有锁突然释放(不可控制),那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。因此,并不推荐使用 stop 方法来终止线程。
使用退出标志退出线程
public volatile boolean exit = false;
public void run() {
while (!exit){
//do something
}
}
通过使用volatile修饰的boolean类型的共享变量,在设置exit等于true时,该运行中的线程会退出while循环,执行完run方法结束
Interrupt方法结束线程
使用interrupt()方法来中断线程有两种情况:
- 线程处于阻塞状态:如使用了sleep,wait,await,socket中的receiver,accept等方法时,会使线程处于阻塞状态。当调用线程的interrupt()方法时,会抛出InterruptException异常,通过代码捕获该异常,然后break跳出循环状态,结束这个线程的执行。通常很多人认为只要调用interrupt方法线程就会结束,实际上是错的,一定要先捕获InterruptedException异常之后通过break来跳出循环,才能正常结束run方法。
public void run () {
//非阻塞过程中通过判断中断标志来退出
while (!Thread.interrupted()) {
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
//捕获到异常之后,执行 break 跳出循环
break;
}
}
}
- 线程处于非阻塞状态:使用isInterrupted()判断线程的中断标志来退出循环。当使用interrupt()方法时,中断标志就会置true,和使用自定义的标志来控制循环是一样的道理。
Thread.currentThread().isInterrupted()和Thread.interrupted()的区别?
两者都是判断当前线程是否中断,isInterrupted()不会清除中断标志,interrupted()会清除掉中断标志。