JAVA核心技术总结(十四章)多线程
2017-03-10 本文已影响0人
五月的约修亚
- 例子
class ThreadTask implements Runnable //在1.5以后加入了callable接口
{
public void run()
{
//.....
}
}
ThreadTask task = new ThreadTask();
Thread t = new Thread(task);
t.start();
- 锁对象
假定第一个线程调用transfer方法,在执行结束前被剥夺了运行权。假定第二个线程也调用transfer,由于第二个线程不能获得锁,将在调用lock方法时被阻塞,必须等第一个线程unlock后才能被激活。
每个Bank对象都有自己的ReentrantLock,如果两个线程访问同一个Bank对象,锁以串行的方式提供服务,但是如果每个线程访问不同的Bank对象将得到不同的锁对象,不会发生阻塞
public class Bank
Lock bankLock = new ReentrantLock();//定义锁
Condition sufficientFunds = bankLock.newCondition(); //定义条件
public void transfer(int from, int to, int amount)
{
bankLock.lock(); //加锁
try{
while(! (ok to proceed)) //条件变量不满足
Con.await(); //该线程被阻塞并且放弃了锁,等待其他线程通过signalAll来激活自己重新进入该while循环继续执行
//do something
Con.signalAll(); //当该线程做了某些事情后,其他线程的条件变量可能被满足,所以通过signalAll来激活等待该条件的所有线程,让它们继续尝试运行
}
finally{
bankLock.unlock();//解锁
}
}
}
-
synchronized
关键字用于保护整个方法(能用synchronized
就不要自己使用Lock/Condition
)
public synchronized void transfer(int from, int to, int amount) throws InterruptedException
{
while(!(ok to proceed))
wait(); //同条件变量的wait
//do something
notifyAll(); //同条件变量的signalAall
}
}
-
volatile
域
声明为volatile
的变量每次使用时都要重新从内存中读取(因为其有可能被其他线程改变,所以不能信任预取在寄存器中的值) -
线程局部变量
有时为了避免共享变量,使用ThreadLocal
辅助类为各个线程提供各自的实例。 -
读写锁
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private Lock readLock = rwl.readLock()
private Lock writeLock = rwl.writeLock()
对所有读方法加锁
void read()
{
readLock.lock();
//do something
readLock.unlock();
}
对所有写方法加锁
void write()
{
writeLock.lock();
//do something
writeLock.unlock();
}
- 线程中断
首先来看Thread类三个和中断有关的方法:
==interrupt
方法不会真的终止线程运行,而只是设定中断的标志位,然后由线程本身决定如何处理这个中断==
理解Java线程的中断
《Java并发编程》之线程中断与终止线程运行
每个线程都有一个与之相关联的 Boolean 属性,用于表示线程的中断状态(interrupted status)。中断状态初始时为 false;当另一个线程通过调用 Thread.interrupt() 中断一个线程时,会出现以下两种情况之一。
- 如果那个线程在执行一个低级可中断阻塞方法,例如 Thread.sleep()、 Thread.join() 或 Object.wait(),那么这些方法将取消阻塞并抛出 InterruptedException(注意是线程内部的这些方法抛出的)。
- 否则, interrupt() 只是设置线程的中断状态。 在被中断线程中运行的代码以后可以轮询中断状态,看看它是否被请求停止正在做的事情。中断状态可以通过 Thread.isInterrupted() 来读取,并且可以通过一个名为 Thread.interrupted() 的操作读取和清除。
中断是一种协作机制。当一个线程中断另一个线程时,被中断的线程不一定要立即停止正在做的事情。相反,中断是礼貌地请求另一个线程在它愿意并且方便的时候停止它正在做的事情。有些方法,例如 Thread.sleep(),很认真地对待这样的请求,但每个方法不是一定要对中断作出响应。对于中断请求,不阻塞但是仍然要花较长时间执行的方法可以轮询中断状态,并在被中断的时候提前返回。 您可以随意忽略中断请求,但是这样做的话会影响响应。
中断的协作特性所带来的一个好处是,它为安全地构造可取消活动提供更大的灵活性。我们很少希望一个活动立即停止;如果活动在正在进行更新的时候被取消,那么程序数据结构可能处于不一致状态。中断允许一个可取消活动来清理正在进行的工作,恢复不变量,通知其他活动它要被取消,然后才终止。
参考资料
public class Thread {
// 发出一个中断请求,把标志位设定为中断状态,不会终止线程运行。
// 其他线程试图调用该方法,会检测是否有权限中断该线程(正常情况
// 下不会存在权限问题,这里可以忽略)
public void interrupt() { ... }
// 检测标志位是否为中断的状态
public boolean isInterrupted() { ... }
// 清除当前线程的标志位的中断状态,返回是否为中断状态
public static boolean interrupted() { ... }
}
- java编程语言中没有任何东西可以避免或打破死锁现象,必须由程序员仔细设计程序确保不会出现死锁。