线程的创建方法
Thread
从Thread类派生子类。
public class ThreadExample extends Thread {
public void run() {
System.out.println("I am a example of Thread.");
}
}
Runnable
从Runnable接口构造Thread对象。
public class RunnableExample extends Runnable {
public void run() {
System.out.println("I am a example of Runnable.");
}
}
可以在Thread中添加方法,获取来自外部的变量,或者给构造方法添加参数来获取来自外部的变量,见如下代码。
public class ThreadExample extends Thread {
private int varFromCreator;
private int varFromMethod;
//在构造方法中获取来自外部的变量
public ThreadExample(String name, int a) {
super(name);
this.varFromCreator = a;
}
//在自定义方法中获取来自外部的变量
public void addVar(int b) {
this.varFromMethod = b;
}
public void run() {
System.out.println("I am a example of Thread."+a);
}
}
线程的调度
Thread.sleep(time)
让当前线程休眠一段时间,进入休眠的线程不会失去对现有monitor或锁的所有权。
time是一个整型值,单位为毫秒。
Thread.interrupt()
向线程发出中断信号,一般来说,线程收到中断信号时应该中断,直接终止。
但是线程收到其他线程发来的中断信号, 并不意味着一定要“停止”,线程正常运行期间, 即使接收到中断信号,也不理会,只有在sleep()时检测是否收到别人的中断信号,若是,则抛出异常并终止线程。
Thread.yield()
使用该方法,线程告知调度器:我可以放弃CPU的占用权,从而可能引起调度器唤醒其他线程。
Thread.stop()
不管当前线程的运行情况,强行终止线程,有可能引起各种不可预知的错误。所以,不要使用stop()!
Thread.join()
让当前线程保持执行,直到其执行结束。可以确定线程的执行顺序。
线程安全(Thread Safety)的四种策略
线程之间的“竞争条件”:作用于同一个mutable数据上的多个线程, 彼此之间存在对该数据的访问竞争并导致interleaving,导致postcondition可能被违反,这是不安全的。
线程安全:ADT或方法在多线程中要执行正确。
Confinement
将可变数据限制在单一线程内部,避免竞争。不允许任何线程直接读写该数据。
Immutability
使用不可变数据类型和不可变引用,避免多线程之间的race condition。不可变数据通常是线程安全的。
Using Threadsafe Data Types
如果必须要用mutable的数据类型在多线程之间共享数据,要使用线程安全的数据类型。
一般来说, JDK同时提供两个相同功能的类,一个是threadsafe,另一个不是。 原因:threadsafe的类一般性能上受影响。
Locks and Synchronization
前三种策略的核心思想:避免共享,即使共享,也只能读/不可写(immutable),即使可写 (mutable),共享的可写数据应自己具备在多线程之间协调的能力,即“使用线程安全的mutable ADT。
很多时候,我们并没有办法满足上述条件。
这里引入一种新机制:同步和锁。程序员来负责多线程之间对mutable数据的共享操作,通过“同步”策略,避免多线程同时访问数据。
synchronized(lock)代码块保证拥有lock的锁才能访问代码块中的内容,如果没有获得锁,则需要等待锁被释放。
lock可以是一个Object,也可以是自定义的一个对象。
class newLock {
boolean condition = false; //自定义对象可以通过内部定义一些变量,来影响线程的具体操作
}
public class Example extends Thread {
private newLock lock = new newLock();
private Object obj = new Object();
public setLock(newLock lock) {
this.lock = lock;
}
public void run() {
......
synchronized(lock) { //这个代码块只有获得lock锁的线程可以访问
......
if(lock.condition) { //这里,如果condition为true,就让这个线程进入阻塞状态,等待被唤醒
try { //可以用Object.notify()和Object.notifyAll()来唤醒
lock.wait();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized(obj) { //这个代码块只有获得obj锁的线程可以访问
......
}
}
}
同步机制会给性能带来极大影响,因为如果线程很多,有可能导致很多线程阻塞在一个位置等待获取锁,失去了多线程的意义,还有可能造成死锁。Java中很多mutable的类型都不是threadsafe就是这个原因。
除非必要,否则不要用synchronized,如果要用则尽可能减少lock的范围。
wait()、notify()、notifyAll()
注意,这三个方法是Object的方法,并不是Thread的方法。
Object.wait()让object所处的当前线程进入阻塞状态,直到其他线程调用该对象的notify()操作。
Object.notify()随机唤醒一个在该对象上调用wait()方法的线程。
Object.notifyAll()唤醒所有在该对象上调用wait()方法的线程。