JavaEE学习笔记-(4)线程-2

前一篇讲了线程的两种创建方式以及常用的一些方法

请参考:JavaEE学习笔记-(4)线程-1

但是上一篇都是单线程,本篇文章学习多线程

**

1.并发错误

**

​ 多个线程共享同一组数据时可能会出现

​ 线程体重连续的多行语句未必能够连续执行,很有可能只进行了一半,时间片就用完了,这时候另一个线程拿走了操作并不完整的“错误”数据

**

2.解决并发错误的方法

**

​ 1)加锁

​ *修饰代码块

​ synchronized(临界资源){

​ 需要连续执行的操作1;

​ 需要连续执行的操作2;

​ }

​ 多个线程共享的那个对象 = 临界资源

​ 锁标记 = 互斥锁标记

​ *修饰整个方法

​ public synchronized void add(Object obj){

​ …;

​ }

​ 等价于从方法的第一行到方法的最后一行统统加锁

​ 对调用方法的那个对象加锁!

​ * synchronized特性不会被子类继承得到,想要该特性,需要方法覆盖

​ 2)java.util.concurrent.locks.ReentrantLock JUC

​ 两个方法:lock(); unlock();

public class TestConcurrentError{
	public static void main(String[] args){
		Student stu = new Student("张朝伟","先生");//对stu的名字和性别进行赋值
		PrintThread pt = new PrintThread(stu);//创建打印线程
		ChangeThread ct = new ChangeThread(stu);//创建修改线程
		pt.start();
		ct.start();
	}
}
//1st.用于打印显示数据的线程 
class PrintThread extends Thread{
	Student stu;
	public PrintThread(Student stu){
		this.stu = stu;
	}
	@Override
	public void run(){ 
		while(true){  // 被pt.start();  执行之后 如果不加锁 ,资源也许会被ct.start()抢走
            //就会出现 梁朝伟女士  张曼玉先生的错误
			synchronized(stu){//我们要对一组连续的操作加锁 不要对所有操作加锁
							  //我们去厕所 只是锁一次上厕所的过程 不要一辈子死在厕所里
				System.out.println(stu);
			}
		}
	}
}
class ChangeThread extends Thread{
	Student stu;
	public ChangeThread(Student stu){
		this.stu = stu;
	}
	@Override
	public void run(){   //被ct.start();
		boolean isOkay = true;
		while(true){
			synchronized(stu){
				if(isOkay){ //梁朝伟 先生
					stu.name = "张曼玉";//张曼玉 先生 
					stu.gender = "女士";//张曼玉 女士 
				}else{ //张曼玉 女士
					stu.name = "梁朝伟";//梁朝伟 女士 
					stu.gender = "先生";//梁朝伟 先生
				}
				isOkay = !isOkay;
			}
		}
	}
}

class Student{
	String name;
	String gender;//性别
	public Student(String name,String gender){
		this.name = name;
		this.gender = gender;
	}
	@Override
	public String toString(){
		return name + " : " + gender;
	}
}

**

3.互斥锁标记使用过多 或者使用不当

**

​ 就会造成多个线程 相互持有对方想要申请的资源
​ 不释放的情况下 又去主动申请对方已经持有的资源
​ 从而双双进入对方持有资源的锁池当中 产生永久的阻塞

​ 就是 死锁 DeadLock: 马路堵车例子

如何解决死锁:
一块空间 和 三个方法:

一块空间: 对象的等待池

三个方法:
1) wait() : 让当前线程放弃已经持有的锁标记 并且进入调用方法的那个对象的等待池当中
east.wait(); 勋勋放弃已经持有的east的锁标记 并且进入east对象的等待池当中
2) notify() : 从调用方法的那个对象的等待池当中随机的唤醒一个线程
3) notifyAll() : 从调用方法的那个对象的等待池当中唤醒所有阻塞的线程

*: 这三个方法都是Object类的方法 所以 任何一个对象 都可以使用
因为任何一个对象都有等待池

*: 这三个方法都必须在已经持有锁标记的前提下才能使用 否则 不但是调用失败
还会触发运行时异常~

Object: equals() clone() finalize()
toString() hashCode()
wait() notify() notifyAll()

*: 我们可以使用wait() notify() 使两个线程相互配合 交替执行~

public class TestDeadLock{
	public static void main(String[] args){
		QCRoad r = new QCRoad();
		QCRoad.Benz s900 = r.new Benz();
		QCRoad.Bmw x9 = r.new Bmw();
		s900.start();
		x9.start();
	}
}
class QCRoad{//一条马路
	Object east = new Object();//路东资源   
	Object west = new Object();//路西资源	

	class Benz extends Thread{//中国人
		@Override
		public void run(){
			System.out.println("中国人驾驶奔驰驶出家门 ");
			synchronized(east){
				System.out.println("中国人和他的奔驰已经占领了马路东侧~");
				try{sleep(1000);}catch(Exception e){e.printStackTrace();}
				try{
                    east.wait();//在此处等待
                }catch(Exception e){
                    e.printStackTrace();}
				synchronized(west){
					System.out.println("中国人和他的奔驰又占领了马路西侧~");
				}
			}

			System.out.println("中国人顺利的通过了马路");
		}
	}
	class Bmw extends Thread{//美国人
		@Override
		public void run(){
			System.out.println("美国人驾驶宝马驶出家门 去上课~");
			synchronized(west){
				System.out.println("美国人和他的宝马已经占领了马路西侧~");
				try{sleep(1000);}catch(Exception e){e.printStackTrace();}
				synchronized(east){
					System.out.println("美国人和他的宝马又占领了马路东侧~");
					east.notify();//用east唤醒
				}
			}
			System.out.println("美国人顺利的通过了马路");
		}
	}

}

Java当中的锁池和等待池有什么区别?
锁池和等待池都是Java当中每个对象都有一份的 用来放置线程的空间

1st.进入的时候是否需要释放资源
锁池:不需要释放资源就能直接进入 (所以才会形成死锁)
等待池:必须要先释放资源 才能进入

2nd.离开的时候是否需要调用方法
锁池:不需要 一旦锁标记归还再度可用 锁池就可以解除
等待池:必须要另外的线程主动唤醒 notify() / notifyAll()

3rd.离开之后去到什么状态
离开锁池:前往就绪状态
离开等待池:前往锁池! [等着唤醒我的线程释放锁标记]

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值