线程
线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元,更多信息参见百度百度-线程。
与创建线程相关的两个类:Thread
、Runnable
。
public class Thread implements Runnable{
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
Runnable
接口定义执行行为。Thread
实现了Runnable
接口,当Thread
中指明了受委托的Runnable
对象,调用也会调用该Runnable
对象的run()
方法。Thread
封装了对线程管理的常用操作,诸如:线程(状态)调度、线程池、线程优先级等。
两者在功能上是父子集合的关系,通常离开Thread
的Runnable
对象是无意义的。
线程如何执行
线程是通过调用start()
方法正确执行的,很多书籍上指明了直接调用run()
是错误做法,那么为什么调用start()
是正确的呢?
// 摘选自:Thread.class
public synchronized void start() {
if (threadStatus != 0/*NEW*/)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
观察到线程被添加到ThreadGroup
中,并调用本地方法start0()
,有关JVM
的部分本文暂不涉及。
而直接调用run()
方法则是在当前线程中直接方法调用,与普通方法调用并无区别。
数据可见性
- 对象的私有实例数据仅在线程中可见
- 对象的公开实例数据可在不同的线程中可见
- 对象的公开静态示例数据可在不同的线程中可见
主要有以下手段,解决后两者引发的多线程问题:
* 使用synchronized
;
* 使用volatile
;
volatile
能保证数据的可见性,但不能保证原子性;而synchronized
可以保证原子性,也可以间接保证可见性,因为它会将私有内存与主存间数据做同步。
线程安全包括原子性和可见性两个方面,Java的同步机制就是围绕这两个方面来确保线程安全的。
volatile
的使用场景是在多个线程中可以感知实例变量被更改了,并且可以获得最新的值使用,也就是用多线程读取共享变量时可以获得最新值使用。
线程间通信
- 使用
Thread.wait()、Thread.notify()
; - 使用
ReentrantLock.lock()、ReentrantLock.unlock()
; - 使用
Thread.join()
; - 使用
ThreadLocal
类;
了解这章的知识,需要先了解观察者模式
的原理。
调用
wait()
立即释放锁,调用notify()
不释放锁。
假设当线程调用wait()
后,不释放锁则意味着独占该锁。那么原本调用notify()
的线程将没有执行机会,那就意味着死锁。
public abstract class AbsRunnable implements Runnable {
protected Object monitor;
public AbsRunnable(Object monitor) {
this.monitor = monitor;
}
protected void print(Object str) {
System.out.println(new SimpleDateFormat("HH:mm:ss").format(new Date()) + " : " + getClass().getSimpleName() + " : " + str);
}
}
public class ARunnable extends AbsRunnable {
public ARunnable(Object monitor) {
super(monitor);
}
@Override
public void run() {
synchronized (monitor) {
try {
print("wait()");
monitor.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
print(monitor);
}
}
}
public class BRunnable extends AbsRunnable {
public BRunnable(Object monitor) {
super(monitor);
}
@Override
public void run() {
synchronized (monitor) {
print(monitor);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
monitor.notify();
print("notify()");
}
}
}
public class ClientMain {
public static void main(String[] args) {
Object lock = new Object();
AbsRunnable a = new ARunnable(lock);
AbsRunnable b = new BRunnable(lock);
new Thread(a).start();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(b).start();
}
}
注意调用wait()
或notify()
或notifyAll()
的前提条件是当前线程已经获得锁,否则将会抛出监视器对象异常。实际中使用时候,需要避免先通知后等待通知的情况。
在某些场合下sleep()
和wait()
给人的感觉很相似,但它们之间是有区别的。最明显的区别是,wait()
执行完会立即释放锁,而sleep()
不会。
总结
标签:入门级读物
、工具手册
博主读此书的时间:2018.04.09
-2018.04.14
该书的阅读时间约两周,适合对多线程技术欠缺经验的小白用户。多以实例验证为主,告知读者“如何这样写,会引发什么问题”。但是欠缺原理上的解释,可以视为削减版(常用类)的工具手册。
购买地址
《Java多线程编程核心技术》拿起淘宝扫描二维码,即可获取该书的购买地址。