目录
停止线程
终止正在运行的线程:
- 使用退出标记,程序正常退出
- 使用stop方法强行退出(暴力停止法),过时方法,强行停止可能导致数据不一致问题
- 使用interrupt中断线程(通常是异常法,sleep法)
interrupt()方法仅仅是在当前线程中打了一个停止的标签,不会马上就停下来。如果线程中包含sleep()或wait()会被停止。
interrupted():测试当前线程是否已经中断,执行后具有将状态标志置清除为false的功能。 isInterrupted():测试线程是否已经中断,但不清除状态标志。
无论是thread.interrupted()还是Thread.interrupted(),都指调用这个方法的当前线程。
停止线程异常法
/**
* 停止线程,异常法
* */
@Test
public void interruptedTest() throws Exception {
class MyThread extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 500000; ++i) {
if (this.isInterrupted()) {
throw new InterruptedException();
}
System.out.println(i);
}
} catch (InterruptedException ex) {
System.out.println("线程被终止!");
}
}
}
MyThread myThread = new MyThread();
myThread.start();
Thread.sleep(2000);
myThread.interrupt();
/**
* output
...
...
73186
73187
73188
73189
73190
73191
73192
线程被终止!
*/
}
暂停恢复线程
suspend()暂停线程 resume()恢复线程
多线程同步技术
synchronized
synchronized可锁重入,线程在执行带有synchronized的方法时,可以再调用对象的其他synchronized方法。
- 如果不可锁重入,就会造成死锁。
- 可重入锁支持在继承的环境中。
- 出现异常锁自动释放。
- synchronized修饰的方法继承的子类不具有该属性。
- synchronized方法用在静态方法上,是对这个类对应的Class类进行加锁。而在非静态方法上,是对这个类的对象加锁。
volatile
强制从公共内存中读取变量的值,保证线程对变量的可见性。 对单个volatile变量的读写具有原子性(指实际底层指令原子性),而对于多个volatile变量复核操作不具有原子性。
所谓volatile不支持原子性,指的是不支持对多条源代码事务的原子性,诶,毕竟volatile只修饰变量又不修饰代码块,当然不能保证上层源代码事务的原子性了,但是一条源代码可能被拆分为多条汇编指令,volatile就能保证这多条汇编指令事务是原子性的。哪些直接说volatile不支持原子性是不是有点太笼统了?!毕竟volatile只修饰变量又不修饰代码块。
volatile与synchronized的区别:
- volatile只能修饰变量;synchronized可以修饰方法,代码块。
- 多线程访问volatile不会阻塞;而synchronized会阻塞。
- volatile保证数据的可见性,但不能保证原子性;而synchronized保证可见性和原子性(因为他会将cache和内存的数据做同步)。
- volatile解决的是变量在多个线程之间的可见性;而synchronized是解决多个线程之间访问资源的同步性。
i++并不是一个原子操作,分为:
- 从内存取i
- +1计算
- 写回内存 int i,多个线程对i做自增操作(i++),不能保证原子性。
volatile int i,多个线程对i做自增操作(i++),能保证原子性。
volatile的使用场景是在多个线程中可以感知实例变量被更改了。
wait()/notify()
wait()和notify()方法必须用在synchronized的临界区内。 wait()方法会释放锁。wait()状态时,当对线程发起interrupt(),线程出现中断异常InterruptException终止线程。 notify()方法执行后,不会立即释放锁,而是退出synchronized块时释放。
线程出现阻塞的5中情况:
- 调用sleep()
- 调用了阻塞式的IO
- 调用wait()方法,等待notify通知
- 等待获取同步监视器(锁等待)
- 调用了suspend()挂起线程
wait()和sleep()的区别:
- 二者所属的类不同。wait()属于Object类;sleep()属于Thread类。
- 二者都会让出cpu;
- wait()会释放对象锁;sleep()不会释放对象锁。
join()
join()使当前线程阻塞,等待子线程返回。
join与synchronized的区别: join()在内部使用wait方法等待;而synchronized使用对象监视器做同步。
ThreadLocal类
使线程拥有一个私有变量,通常设计为public static工具类形式。
使用InheritableThreadLocal类可以让子线程从父线程中取得值。类似Log4j中的MDC。
ReentrantLock类
new ReentrantLock(true)指定为公平锁,缺省为非公平锁。 lock()/unlock() unlock()通常放在finally{}代码块中,发生异常时,释放锁。
synchronized与wait()、notify()、notifyAll()实现等待通知,ReentrantLock也可以借助Condition实现等待通知模式,而且可以实现选择性通知。
Condition.await()/signal()
与wait()/notify()的用法相同,condition.await()/singal()的也调用必须获得同步监视器,不同的是使用使用lock.lock()获得。
getHoldCount()
表示当前线程保持此锁的个数(锁重入数)。 getQueueLength()
返回等待此锁的估计数。 getWaitQueueLength(condition)
返回condition条件的await()等待线程的估计数。
hasQueueThread(threadA)
表示threadA是否在等待lock锁。 hasQueueThreads()表示是否有线程在等待此锁。 hasWaiters(condition)
表示是否有线程在等待此锁的condition的signal。
isFair()
是否公平锁。 isHeldByCurrentThread()
当前线程是否持有该锁。 isLocked()
该锁是否被持有
lockInterruptibly()
如果线程未被中断,则获取锁定,否则抛出异常。 tryLock()
尝试取锁 tryLock(long timeout, TimeUnit unit)
尝试取锁,超时返回
ReentrantReadWriteLock类
读读不互斥,读写互斥,写写互斥。即多个读线程不互斥,...
定时器Timer
Timer类用于设置计划任务,创建一个Timer就会创建一个线程。 TimerTask类用于封装新任务
Timer默认不是守护线程,任务执行完了不会退出。
timer.schedule(task, runDate)
在runDate时间点(Date)执行,task任务。 timer.schedule(task, runDate, period)
在runDate时间点执行task任务,周期为period执行一次,单位是ms。
对于周期性任务,如果task任务的执行时长比period长,称为延时,延时情况下,下一次任务的执行时间是上次任务结束时间作为参考;如果不延时,则是下一次任务的执行时间是上一次的开始时间加上period时间。
scheduleAtFixedRate与schedule的区别: scheduleAtFixedRate具有追赶性,如果runDate比现在时间早,那么它会按照比率将runDate到现在错过的任务补充执行。
task.cancel()
从Timer计划任务中移除task任务。 timer.cancel()
将Timer计划任务中的所有任务移除,并且Timer线程被销毁。
实现线程安全的单例模式
- 饿汉模式(包括在static变量上new,或在static代码块中),加载类时初始化,即可把实例创建在方法区。
public class MyObject {
// 在static变量上new
private static MyObject myObject = new MyObject();
/**
* 在static代码块中
*/
// static {
// myObject = new MyObject();
// }
private MyObject() {}
public static MyObject getInstance() {
return myObject;
}
}
- 懒汉模式,使用DCL(双检查机制)实现。
public class MyObject {
// 此处的volatile并不是用于可见性,而是为了防止指令重排序
private volatile static MyObject myObject;
private MyObject() {}
public static MyObject getInstance() {
try {
if (null == myObject) {
synchronized (MyObject.class) {
// 双重检查,保证创建对象并释放后,其他线程取得锁不会再次创建对象
if (null == myObject) {
// new不是原子操作,它包含了三条指令
// 如果变量不是volatile的,则其他线程可能会在第一个if出现myObject != null的情况
myObject = new MyObject;
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return myObject;
}
}
- 静态内置类实现,与饿汉类似。
public class MyObject {
// 静态内部类
private static class MyObjectHandler {
private static MyObject myObject = new MyObject;
}
private MyObject() {}
public static MyObject getInstance() {
return MyObjectHandler.myObject;
}
}
- 使用Enum实现单例
public Enum MyObject {
INSTANCE;
private OthObject othObject;
private MyObject() {
othObject = new OthObject();
}
public OthObject getOthObject() {
return othObject;
}
}
// 使用
MyObject.INSTANCE.getOthObject()即可;
线程的状态
此处非线程生命周期,而是使用thread.getstate()方法,返回的枚举类型。
- NEW
- RUNNABLE
- BLICKED 线程在等待锁的时候
- WAITING 无期限的等待,例如wait()、await()
- TIMED_WAITING 指定时间的等待
- TERMINATED
线程异常处理
通过设置**setUncaughtExceptionHandler()**重写异常的处理方式
thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
}
})
对于线程组异常,通常可以继承ThreadGroup类,重写uncaughtException(Thread t, Throwable e)方法,然后使用自定义的ThreadGroup线程组。
参考 《Java多线程编程核心技术》 高洪岩