(笔记九)多线程、Lock、停止线程

1.线程间通信
多线程在处理同一资源,但任务却不同

2.obj.wait(),obj.notify(),obj.notifyAll()
1)必须存在synchronized(Obj)语句块中

2)锁可以是任何对象,这些方法定在在Object中

3)wait:线程在获取对象锁后,通过wait方法主动释放对象锁,同时本线程阻塞。直到有其它线程调用该对象锁的notify()唤醒该线程,才能继续获取对象锁,并继续执行。(线程存入线程池)

4)notify:相当于对象锁的唤醒操作。调用后,并不是马上就释放对象锁,而是在相应的synchronized(){}语句块执行结束后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程(★★★继续执行,而不是重新执行)。

2.wait()与sleep()区别
1)前者释放cpu执行权并释放锁,后者释放cpu执行权 ,不释放锁

2)wait可以指定时间也可以不指定,sleep必须指定

3.例子
两个线程同时操作对象,先输入然后输出,循环显示
Person.java
package mypackage;
public class Person {
	String name;
	String sex;
	boolean flag;
}
input.java
package mypackage;
public class Input implements Runnable {
	Person p;

	public Input(Person p) {
		this.p = p;
	}
	public void run() {
		int x = 0;
		while (true) {
			synchronized (p) {
				if(p.flag){
					try {
						p.wait();
					} catch (InterruptedException e) {						
						e.printStackTrace();
					}
				}
					
				if (x % 2 == 0) {
					p.name = "Tom";
					p.sex = "male";
				} else {
					p.name = "Lily";
					p.sex = "female";
				}
				p.flag=true;
				p.notify();
			}
			x++;
		}
	}
}
Out.java
package mypackage;
public class Out implements Runnable {
	Person p;
	public Out(Person p) {
		this.p = p;
	}
	public void run() {
		while (true) {
			synchronized (p) {
				if (!p.flag) {
					try {
						p.wait();
					} catch (InterruptedException e) {						
						e.printStackTrace();
					}
				}
				System.out.println(p.name + "..." + p.sex);
				p.flag = false;
				p.notify();
			}
		}
	}
}
Demo.java
import mypackage.*;
public class Demo {
	public static void main(String[] args) 
	{
		Person p=new Person();			
		Input in=new Input(p);
		Out out=new Out(p);
		
		Thread t1=new Thread(in);
		Thread t2=new Thread(out);
		
		t1.start();
		t2.start();		
	}
}
4.优化Person类
public class Person {
	private String name;
	private String sex;
	private	boolean flag;
	
	synchronized void input(String name,String sex){
		if(flag)
			try {
				this.wait();
			} catch (InterruptedException e) {				
				e.printStackTrace();
			}
		this.name=name;
		this.sex=sex;
		flag=true;
		notify();
	}
	
	synchronized void out(){	
		if(!flag)
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		System.out.println("name:"+name+",sex:"+sex);
		flag=false;
		notify();
	}
}

5.多个线程同时输入,多个线程同时输出情况下,引发异常(★★★)

在main函数中,创建两个线程t0与t1,执行输入操作;创建两个线程t2与t3,执行输出操作。
分析:t0执行后阻塞-->t1阻塞-->t2执行后阻塞,唤醒t0-->t3阻塞(仅t0运行)-->
t0输入数据后,并没有输出(异常!!!),唤醒t1,再次输入。

原因:if判断只执行一次,在t1阻塞后重新得到执行权,不会再次判断是否要运行,导致不该运行的线程运行,导致数据异常。

解决:将if换成while,解决判断是否运行,但会导致死锁,可将notify换成notifyAll解决

6.Lock(JDK1.5特性)
1)提供了比使用synchronized方法和语句更广泛的锁定操作,允许更灵活的结构,支持多个Condition对象(支持多个监视器对象),Condition代替Object中的wait,notify,notifyAll。

2)将synchronized隐式实现转变为Lock对象的显示实现

3)使用
Lock l = 实现;
 l.lock();
 try

 {
 }
finally {
      l.unlock();
 } 

 4)演示
import java.util.concurrent.locks.*;
public class Product {
	String name;
	int Id;
	boolean flag;
	int no = 1;
	// 创建锁对象
	Lock l = new ReentrantLock();
	// 通过已有锁对象创建该锁上的监视器对象,一个监视生产者,一个监视销售者
	Condition in = l.newCondition();
	Condition out = l.newCondition();
	
	void input(String name) {
		l.lock();
		try {
			while (flag)
				try {
					in.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			this.name = name;
			this.Id = no++;
			System.out.println("生产:" + name + "," + Id);
			flag = true;
			out.signal();
		} finally {
			l.unlock();
		}
	}
	void out() {
		l.lock();
		try {
			while (!flag)
				try {
					out.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			System.out.println("销售:" + name + ",id:" + Id + ",继续加油");
			flag = false;
			in.signal();
		} finally {
			l.unlock();
		}
	}
}

7.停止线程
1)stop方法,涉及安全问题,已过时
2)run方法结束,通常任务中会有循环结构,控制循环标记,但如果线程处于堵塞状态,无法读取标记,则无法结束
3)interrupt方法,将线程从阻塞状态强制恢复到运行状态来,让线程具备cpu执行资格,但会发生InterruptException,可以在catch中处理循环标记

8.守护线程
setDaemon方法必须在启动线程前调用,当正在运行的线程都是守护线程时(),Java 虚拟机退出。
非守护线程可以理解为前台线程,守护线程守护前台线程,前台线程都结束,守护线程也结束。

9.join
当前线程等待该线程终止,后运行

10.经典面试题
3个线程顺序打印10次ABC

11.获取线程状态方法getState() 

对应枚举 Thread.State(6种状态)

12.获取当前程序所有线程
ThreadGroup topGroup = null;
ThreadGroup group = Thread.currentThread().getThreadGroup();
// 遍历线程组树,获取根线程组
while (group != null) {
	topGroup = group;
	group = group.getParent();
}
// 此线程组中活动线程的估计数
int estimatedSize = topGroup.activeCount();
System.out.println("active:" + estimatedSize);
Thread[] slackList = new Thread[estimatedSize];
// 获取根线程组的所有线程
int actualSize = topGroup.enumerate(slackList);
//遍历
for (Thread t : slackList) {
	System.out.println("ID:" + t.getId() + ",Name:" + t.getName()
			+ ",isAlive:" + t.isAlive());
}	

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值