wait()和notifyAll()
synchronized
解决了多线程竞争的问题,但是synchronized
并没有解决多线程协调的问题。
多线程协调运行的原则就是:当条件不满足时,线程进入等待状态;当条件满足时,线程被唤醒,继续执行任务。
我们可以使用wait()
和notifyAll()
解决多线程协调运行:
wait()
使线程进入等待状态;notifyAll()
唤醒当前锁对象的所有等待线程;
wait()
和notifyAll()
需要使用在锁对象上。在synchronized
修饰的方法中,就是this
对象。
当然,已唤醒的线程还需要重新获得锁后才能继续执行。
这是一个生产者消费者问题使用wait()
和notifyAll()
的例子:
package product;
/**
*仓库
*/
class Storage {
// 仓库容量为10
private Product[] products = new Product[10];
private int top = 0;
// 生产者往仓库中放入产品
public synchronized void push(Product product) {
while (top == products.length) {
try {
wait();//仓库已满,等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//把产品放入仓库
products[top++] = product;
notifyAll();//唤醒等待线程
}
// 消费者从仓库中取出产品
public synchronized Product pop() {
while (top == 0) {
try {
wait();//仓库空,等待
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//从仓库中取产品
--top;
Product p = new Product(products[top].getId(), products[top].getName());
products[top] = null;
notifyAll();//唤醒等待线程
return p;
}
}
线程的被动中断和主动中断
- 线程被动地中断
interrupt()
– 依靠别的线程来拯救自己
– 没有及时释放资源 - 线程主动中断(定期监测共享变量)
– 如果需要中断线程,可先释放资源,再主动中断线程。
– 暂停:Thread.sleep(),休眠
– 终止:run方法结束,线程终止
public class InterruptTest {
public static void main(String[] args) throws InterruptedException {
TestThread1 t1 = new TestThread1();
TestThread2 t2 = new TestThread2();
t1.start();
t2.start();
// 让线程运行一会儿后中断
Thread.sleep(2000);
t1.interrupt();
t2.flag = false;
System.out.println("main thread is exiting");
}
}
class TestThread1 extends Thread {
public void run() {
// 当程序睡眠时,中断线程,interrupted()会报出异常,直接中断程序。这期间可能还没来得及适放资源。
while (!interrupted()) {
System.out.println("test thread1 is running");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
System.out.println("test thread1 is exiting");
}
}
class TestThread2 extends Thread {
public volatile boolean flag = true;
public void run() {
// 使用flag来中断程序
while (flag) {
System.out.println("test thread2 is running");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("test thread2 is exiting");
}
}
多线程死锁
每个线程互相持有别人需要的锁。
预防死锁:对资源进行等级排序。(让每个线程获取资源的顺序一致)
import java.util.concurrent.TimeUnit;
public class ThreadDemo5
{
public static Integer r1 = 1;
public static Integer r2 = 2;
public static void main(String args[]) throws InterruptedException
{
TestThread51 t1 = new TestThread51();
t1.start();
TestThread52 t2 = new TestThread52();
t2.start();
}
}
class TestThread51 extends Thread
{
public void run()
{
//先要r1,再要r2
synchronized(ThreadDemo5.r1)
{
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(ThreadDemo5.r2)
{
System.out.println("TestThread51 is running");
}
}
}
}
class TestThread52 extends Thread
{
public void run()
{
//先要r2,再要r1 //如果这里更改为先要r1,再要r2,就不会发生死锁
synchronized(ThreadDemo5.r1)
{
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(ThreadDemo5.r2)
{
System.out.println("TestThread52 is running");
}
}
}
}
守护线程
当main函数(主线程)结束时,守护线程也会结束。
所以,守护线程永远不要访问资源,如文件或数据库等。
package daemon;
public class ThreadDemo4
{
public static void main(String args[]) throws InterruptedException
{
TestThread4 t = new TestThread4();
t.setDaemon(true); //这一句话将该线程设置为了守护线程。
t.start();
Thread.sleep(2000);
System.out.println("main thread is exiting");
}
}
class TestThread4 extends Thread
{
public void run()
{
while(true)
{
System.out.println("TestThread4" +
" is running");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}