java多线程之死锁和等待唤醒机制

lock锁:接口

使用其子类对象

为保证被同步的代码块不出现异常,通常需要将解锁的过程放在final中,所以被同步的代码块也要放在try中

线程死锁:

当多个锁出现相互等待都不执行时,就出现线程死锁

死锁一般出现在锁的嵌套中,相互等着对方执行完毕,也就是双方都要执行对方已经加锁的代码

代码演示:

<span style="font-size:12px;">public class DeadLock extends Thread {
//  定义一个标记用来指定要执行的代码
	boolean flag;
//	创建两个不同的锁对象
	static Object o1 = new Object();   // 静态修饰的原因是保证锁的同步性!重要!
	static Object o2 = new Object();	
	/**
	 * @param flag
	 */
	public DeadLock(boolean flag) {
		super();
		this.flag = flag;
	}
	@Override
	public void run() {

		super.run();
		if (flag) {    //创建线程对象时,将flag赋值为true时,执行的代码

			synchronized (o1) {
				System.out.println("if语句中o1的执行代码");

				synchronized (o2) {
					System.out.println("if语句中o2的执行代码");
				}
			} 
		}else {         //创建线程对象时,将flag赋值为false时,执行的代码

			synchronized (o2) {
				System.out.println("else语句中o2的执行代码");

				synchronized (o1) {
					System.out.println("else语句中o1的 执行代码");
				}
			}
		}
	}
}</span>

结合如下死锁测试代码,死锁过程为,当线程一进入之后判断条件语句,执行判断结果为true的同步锁代码,打印if中的语句,同时线程二进入判断条件语句,执行判断结果为false的同步锁代码,执行if中的语句,当两个线程继续向下执行时,各自的线程都没有出锁,而接下来的代码就是要执行对方线程的解锁后的代码,所以两个线程就互相 等待对方 执行完解锁,所以就形成了相互等待的死锁状态

死锁测试:

<span style="font-size:12px;">public class Test {

	public static void main(String[] args) {
//		创建多线程对象
		DeadLock lock1 = new DeadLock(true);
		DeadLock lock2 = new DeadLock(false);
//		开启线程
		lock1.start();
		lock2.start();
	}
}</span>
解决方案:

不出现锁的嵌套

满足条件后自己解锁,比如当线程等待时间超过一定时间,就自动杀掉线程

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

生产者消费者:

生产者与消费者共享同一个资源

满足生产者生产条件时,生产者生产

当满足消费者消费条件时,消费者消费

产者与消费者可以通过不同的线程完成相应的动作

等待唤醒机制:

要执行的线程由于某种需求需要别的线程对共享数据先进行操作时,这个线程就需要等待

当其他的线程操作完之后,应该唤醒刚才那个等待的线程

等待唤醒机制涉及到的类:

Thread:多个线程才会出现呢等待唤醒机制

锁:等待唤醒机制需要同步代码,既然有同步代码就需要锁(synchronize)

锁是Object类型:是线程等待,但是是线程通过锁等待

代码实现:

1.创建共享数据类

<span style="font-size:12px;">public class Person  {
	
//	封装成员属性
	private String name;
	private int age;
	private boolean flag;

//	提供判断条件用于区分生产者和消费者的执行路线
	public boolean isFlag() {
		return flag;
	}
	public void setFlag(boolean flag) {    //  对外提供公共的 修改标志条件的入口,是保证生产和消费一一对应的前提
		this.flag = flag;
	}
	public Person() {            //   创建空参的 构造函数用于创建共享对象的数据
		super();
		// TODO Auto-generated constructor stub
	}
	/**
	 * @param name
	 * @param age
	 */
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
//	对外提供公共的访问入口
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}

}</span>

2.创建生产者线程类

<span style="font-size:12px;">public class Set implements Runnable {

	int i=0;
	private  Person p;	
	/**
	 * @param p
	 */
	public Set(Person p) {   // 为什么呢? 因为创建线程的时候保证了生产者和消费者使用的是同一个对象
		super();
		this.p = p;
	}
//	重写run方法
	public void run() {
		while (true) {
			synchronized (p) {         //  保证所共享的数据是同步的
				if (p.isFlag()) {
				try {
					p.wait();          // 先进行判断,如果满足条件则生产,否则等待消费者消费
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
//				判断生产的元素
				if (i%2==0) {
					p.setName("龙女");
					p.setAge(16);
					System.out.println(Thread.currentThread().getName()+p);
				}else {
					p.setName("过儿");
					p.setAge(18);
					System.out.println(Thread.currentThread().getName()+p);
				}
				i++;       //制造不同的循环生产条件
				p.setFlag(true);    // 改变标志,让消费者消费
				
				p.notify();        // 唤醒消费者
			}
		}
		
	}
}</span>
3.创建消费者线程类

public class Get implements Runnable {

	private Person p;
	
	/**
	 * @param p
	 */
	public Get(Person p) {        //  利用出入的对象作为共享数据源
		super();
		this.p = p;
	}
//  重写run方法
	public void run() {
			while (true) {
				synchronized (p) {
//					判断标志
					if (!p.isFlag()) {
					try {
						p.wait();       //  判断,如果满足条件则消费,否则等待生产者生产
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					
					System.out.println(Thread.currentThread().getName()+p);
				}
				p.setFlag(false);   //  改变标志,让生产线程执行
				
				p.notify();     // 唤醒生产线程
			}
			
		}
		
	}
	
}
4.测试生产者和消费者一一对应的运行结果

public class Test {

	public static void main(String[] args) {
		
//		创建共享对象数据
		Person p = new Person();
		
		Set set = new Set(p);
		Get get = new Get(p);
		
//		创建线程对象
		Thread setThread = new Thread(set,"生产者");
		Thread getThread = new Thread(get,"消费者");
		
//		同时开启生产者和消费者线程
		setThread.start();
		getThread.start();
	}

}


总结:等待唤醒机制是Object类中的提供的方法,即所有类都继承了此方法,当使用共享数据时,可以把其放在属性的位置,或者创建构造方法当参数传入,这样可以保证共享数

据的同步性,当生产者和消费者执行各自的线程时首先要进行判断,如果不满足条件则等待对方对共享数据的操作,然后改变判断条件,唤醒等待,如此反复执行下去


线程组:

用来组织线程

线程池:

程序在开辟新的线程时,要消耗大量的系统资源,频繁的创建及销毁线程会大大降低程序的效率

在程序开始时,只要创建一个线程池,将指定数量的线程开启,如果使用,就使用线程池中的线程,如果不使用,线程也不销毁,而是出于空闲状态,等待被再次使用

线程池可以线程的某些行为,比如销毁动作





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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值