【并发错误】

  1. 什么是并发错误?
    1. 根本原因:多个线程共享操作同一份数据
    2. 直接原因:线程当中连续的多行语句未必能够连续执行,很可能操作只完成了一部分,时间片突然耗尽,而此时另一个线程抢到了时间片,直接访问了操作不完整的数据...
    3. 导火索:时间片突然耗尽
    4. 多个线程共享操作同一份数据,线程体当中连续的多行语句,未必能够连续执行,很可能操作只完成了一部分,时间片耗尽,而另外一个线程抢到时间片之后,直接访问了操作不完整的错误数据
  2. 并发错误和并发修改异常哪个更严重,它们什么关系?并发错误更严重,它编译不报错,运行没异常, 数据全是错误的。而并发修改异常是官方程序员为了避免程序出现并发错误,而主动校验主动抛出的一个运行时异常,它的目的是防止程序出现并发错误
  3. 如何解决并发错误?可以使用synchronized结合对象的互斥锁进行加锁,也可以使用ReentrantLock(它是面向对象思想实现的加锁方式)可重入锁进行加锁
  4. synchronized能够修饰什么?互斥锁=互斥锁标记=锁标记=锁旗标=监视器=Monitor
    /**
        1.修饰代码块
        synchronized(临界资源){
            连续执行的操作1;
            连续执行的操作2;
            ....
    }
        2.修饰整个方法
        public synchronized void add(Object obj){
            连续执行的操作1;
            连续执行的操作2;
            ....
    }
    
        *:Vector、Hashtable、StringBuffer 它们之所以线程安全,是因为底层大量的方法都使用了               synchronized修饰
        
        *:单例模式的懒汉式需要synchronized修饰那个getter方法
            
        *:synchronized有一个所有修饰符都不具备的特性,它隔代丢失
        
        *:即便synchronized修饰符加在方法上 也依然是对 对象 进行加锁
    	   Java当中只有每个对象才有锁标记 只能对 对象 进行加锁!
    
    */
  5. synchronized的特性:隔代丢失 -- 父类的synchronized方法能够被继承,但是synchronized修饰符就消失了
  6. java.util.concurrent.locks.ReentrantLock=java包的工具包的并发包的锁包的可重入锁,
    1. 他提供了更好的可扩展性!!!
    2. since jdk5.0;
    3. lock() - 加锁;unlock() - 释放锁;
    4. 它可还可以指定公平锁或非公平锁;new ReentrantLock(true);
  7. 什么是公平锁、非公平锁?如何创建公平锁?
    1. 公平:按照对锁资源申请的先后顺序,依次获得资源;new ReentrantLock(true)
    2. 非公平:一但锁标记可用,就开始抢; new ReentrantLock() ;默认是false
  8. CountDownLatch是什么?它的核心方法叫什么?有什么作用?
    1. CountDownLatch = 倒计时门栓
    2. countDown():打开一个门闩
    3. await():主动制造阻塞,等待门闩都被打开
  9. 什么是死锁? 互斥锁标记使用过多,或者使用不当,就会造成多个线程 互相持有对方想要申请的资源,不释放的情况下,又去申请对方已经持有的资源,从而双双进入对方已经持有资源的锁池当中,产生永久的阻塞
  10. 如何解决死锁:一块空间:等待池 + 三个方法:wait()、notify()、notifyAll()
    1. wait():让当前线程放弃已经持有的锁标记,并且进入调用方法的那个对象的等待池当中。需要异常处理:try catch
    2. notify():从调用方法的那个对象的等待池当中,随机唤醒一个线程
    3. notifyAll():从调用方法的那个对象的等待池当中,唤醒所有阻塞的线程
    4. 使用上面三个方法的时候需要注意:它们必须出现在synchronized的作用范围内,否则不但操作失败,还会触发运行时异常,IllegalMonitorStateException
    5. *:这三个方法都是Object类的方法,而不是线程类的方法。因为Java当中每个对象都有等待池,每个对象都可能要操作等待池,所以这三个方法被定义在Object类当中
    6. *:这三个方法都必须在已经持有锁标记的前提下才能使用,否则不但操作失败还会触发IllegalMonitorStateException
  11. IllegalMonitorStateException是什么异常?怎么触发的?非法锁标记异常;在没有拿到对象锁标记的情况下,直接使用了操作等待池的方法。wait()、notify()没有出现在synchronized的作用范围当中
  12. 锁池、等待池的概念和区别
    1. 都是java当中每个对象都有一份的空间,都是存放线程任务的
    2. 锁池:存放的是想要获得对象的锁标记,但是还没有成功的线程(鱼塘、备胎池)
    3. 等待池:存放的是原本已经获得对象的锁标记,但是怕跟其他线程永久阻塞,而是主动释放掉资源的线程
    4. 区别:
      1. 进入的时候是否需要释放资源:锁池不需要,等待池需要
      2. 离开的时候是否需要调用方法:锁池不需要,等待池需要notify()或notifyAll()
      3. 离开之后去往什么状态:离开锁池,返回就绪;离开等待池,直奔锁池
  13. 线程交替 左右两部分
    1、张师傅开车工作
    2、张师傅休息wait
    3、王师傅开车工作
    4、王师傅 叫醒 张师傅 notify
    5、王师傅休息 wait
    6、张师傅 叫醒 王师傅 notify
    1. synchronized        等待池        wait()、notify()、notify()
    2. ReentrantLock      Condition    await()、signal()、signalAll()
  14. Java当中每个对象都有一份的 都是什么?属性、方法、锁、锁池、等待池
  15. 补充:强化理解synchronized
    1. 已知Vector类的add()和remove()都是synchronized修饰的,我们现在有一个Vector对象,名叫v(wc的天字一号房),有两个线程对象,名叫t1和t2。当t1线程调用v对象的add()方法已经开始执行了,但是还没有执行结束呢,此时时间片突然耗尽了,而t2线程抢到了时间片。问:t2能不能调用v对象的add()?t2能不能调用v对象的remove()?
      1. 答 false;false。因为add()方法被synchronized修饰,t1调用add()方法相当于t1进入了天字一号房后上锁了,中断后锁上门拿着钥匙走了,t2当然就不能调用add()方法进入天字一号房
    2. 已知:Vector类的add() 和 remove() 都是synchronized修饰的,我们现在有两个Vector对象 名叫 v1(wc的天字一号房) 和 v2(wc的天字二号房),有两个线程对象 名叫 t1 和 t2。当t1线程调用v1对象的add() 方法已经开始自行了,但是还没执行结束呢 此时时间片突然耗尽,而t2线程抢到了时间片。问:t2线程能不能调用v1对象的add()?t2线程能不能调用v1对象的remove()?t2线程能不能调用v2对象的add()?t2线程能不能调用v2对象的remove()?
      1. 答 false;false;true;true
    3. 综上所述,几遍synchronized修饰符加在方法上,也依然是对对象进行加锁。java当中只有每个对象才有锁标记,只能对对象进行加锁!
  16. 完善懒汉式
    class Sun{
    	private Sun(){}
    	private static Sun only;
    	public static synchronized Sun getOnly(){
    		if(only == null)
    			only = new Sun();
    		return only;
    	}
    }
  17. 总结:Object类的方法
    1. clone() :创建并返回此对象的副本       
    2. finalize():当垃圾收集确定不再有该对象的引用时,垃圾收集器在对象上调用该对象
    3. equals():知识一些其他对象是否等于此
    4. getClass():返回此Object的运行时类
    5. hashCode():返回对象的哈希码值
    6. toString():返回对象的字符串表示形式
    7. wait():导致当线程等待,直到另一个线程调用notify()方法或该对象的notifyAll()方法
    8. notify():唤醒正在等待对象监视器的单个线程
    9. notifyAll():唤醒正在等待对象监视器的所有线程
public class TestConcurrentError{
	public static void main(String[] args){
		Student stu = new Student("张曼玉","女士");
		printThread p = new printThread(stu);
		changeThread c = new changeThread(stu);
		p.start();
		c.start();
	}
}
class Student{
	String name;
	String gender;

	public Student(String name,String gender){
		this.name = name;
		this.gender = gender;
	}

	public String toString(){
		return name + ":" + gender;
	}
}
class changeThread extends Thread{
	Student stu;
	public changeThread(Student stu){
		this.stu = stu;
	}

	@Override
	public void run(){
		boolean isOkay = true;
		while(true){
			synchronized(stu){//不加锁的话就会出现数据错误,可能为姓名和性别不匹配
				if(isOkay){
					stu.name = "梁朝伟";
					stu.gender = "先生";
				}else{
					stu.name = "张曼玉";
					stu.gender = "女士";
				}
				isOkay = !isOkay;
			}
		}
	}
}
class printThread extends Thread{
	Student stu;
	public printThread(Student stu){
		this.stu = stu;
	}

	@Override
	public void run(){
		while(true){
			synchronized(stu){//不加锁的话就会出现数据错误,可能为姓名和性别不匹配
				System.out.println(stu);
			}
		}
	}
}
import java.util.concurrent.locks.*;
public class TestConcurrentErrorWithLock{
	public static void main(String[] args){
		Student stu = new Student("张曼玉","女士");
		Lock lock = new ReentrantLock();
		PrintThread pt = new PrintThread(stu,lock);
		ChangeThread ct = new ChangeThread(stu,lock);
		pt.start();
		ct.start();
	}
}
class ChangeThread extends Thread{
	Student stu;
	Lock lock;
	public ChangeThread(Student stu,Lock lock){
		this.stu = stu;
		this.lock = lock;
	}
	@Override
	public void run(){
		boolean isOkay = true;
		while(true){
			//synchronized(stu){
				lock.lock();
				try{
					if(isOkay){
						stu.name = "梁朝伟";//"梁朝伟","女士"
						stu.gender = "先生";//"梁朝伟","先生"
					}else{
						stu.name = "张曼玉";//"张曼玉","先生"
						stu.gender = "女士";//"张曼玉","女士"
					}
					isOkay = !isOkay;
				}finally{
					lock.unlock();
				}
			//}
		}
	}
}

class PrintThread extends Thread{
	Student stu;
	Lock lock;
	public PrintThread(Student stu,Lock lock){
		this.stu = stu;
		this.lock = lock;
	}
	@Override
	public void run(){

		while(true){
			//synchronized(stu){
				lock.lock();
				try{
					System.out.println(stu);
				}finally{
					lock.unlock();
				}
			//}
		}
	}
}
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;
	}
}
public class ExecDeadLock{
	public static void main(String[] args){
		Restaurant kfc = new Restaurant();
		Restaurant.Chinese xdd = kfc.new Chinese();
		Restaurant.American bd = kfc.new American();
		xdd.start();
		bd.start();
	}
}
class Restaurant{
	Object forks = new Object();
	Object chops = new Object();

	class Chinese extends Thread{
		@Override
		public void run(){
			System.out.println("中国人走进餐厅");
			synchronized(forks){
				System.out.println("中国人拿到刀叉");
				synchronized(chops){
					System.out.println("中国人又拿到筷子");
					chops.notify();
				}
			}
			System.out.println("中国人就餐完毕");
		}
	}

	class American extends Thread{
		@Override
		public void run(){
			System.out.println("美国人走进餐厅");
			synchronized(chops){
				System.out.println("美国人拿到筷子");
				try{chops.wait();}catch(Exception e){e.printStackTrace();}
				synchronized(forks){
					System.out.println("美国人又拿到刀叉");
				}
			}
			System.out.println("美国人就餐完毕");
		}
	}
}
import java.util.concurrent.locks.*;
public class TestSwitchThreadWithLock{
	public static void main(String[] args){
		右脚 r = new 右脚();
		左脚 l = new 左脚(r);
		l.start();
		//r.start();
	}
}
class X{
	static Object obj = new Object();
	static Lock lock = new ReentrantLock();
	static Condition c = lock.newCondition();//!!!
}
class 左脚 extends Thread{
	右脚 r;

	public 左脚(右脚 r){
		this.r = r;
	}

	@Override
	public void run(){
		X.lock.lock();
		//synchronized(X.obj){
			r.start();//start()的作用是使线程从新生到就绪,但是此时左脚已经拿到锁标记了,右脚肯定抢不过
			for(int x = 0; x < 8888;x++){
				//try{Thread.sleep(5);}catch(Exception e){}

				System.out.println("左脚");//1.
				try{X.c.await();}catch(Exception e){}
				//try{X.obj.wait();}catch(Exception e){}//2.

				X.c.signal();
				//X.obj.notify();//6.
			}
		//}
		X.lock.unlock();
	}
}
class 右脚 extends Thread{
	@Override
	public void run(){
		X.lock.lock();
		//synchronized(X.obj){
			for(int x = 0; x < 8888;x++){
				//try{Thread.sleep(5);}catch(Exception e){}

				System.out.println("		右脚");//3.
				X.c.signal();
				//X.obj.notify();//4.
				try{X.c.await();}catch(Exception e){}
				//try{X.obj.wait();}catch(Exception e){}//5.


			}
		//}
		X.lock.unlock();
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值