JavaCore学习 Thread

什么是并发错误

        多个线程共享操作同一份数据的时候 线程体当中连续的多行操作未必能够连续执行 很可能操作只完成了一部分 时间片突然耗尽 而此时另一个线程刚好抢到时间片 直接访问了操作不完整的数据

        根本原因:多个线程共享访问同一份数据

        直接原因:线程体当中连续多行语句 未必能够连续执行

        导火线:时间片突然耗尽

//并发修改异常(CME)和 并发错误是什么关系?

        并发修改异常时Sun公司官方程序员为了避免程序运行时出现并发错误而主动抛出的运行时异常

        并发错误 则编译不报错 运行没异常 只是数据全是逻辑错误的

如何解决并发错误

        1.互斥锁

        *:什么是临界资源

                多个线程共享的那个对象 称为 临界资源

        *:什么是锁标记

                Java当中每个对象都有一份的标识

                锁标记 = 互斥锁 = 互斥锁标记 = 锁旗标 = 监视器 = Moniter

        synchronized 修饰符 形容词(同步的)

                1.修饰代码块

                        synchronized(临界资源){

                                需要连续执行的操作1;

                                需要连续执行的操作2;

                                ...        

                        }

                 2.修饰整个方法

                        public synchronizied void add(Object obj){

                                需要连续执行的操作1;

                                需要连续执行的操作2;

                                ...

                        }

                        这等价于 从方法的第一行到最后一行统统加锁  对调用方法的当前对象枷锁

                        等价于

                        public void add(Object obj){

                                synchronized(this){

                                }      

                        }

//已知:Vector类的 add() 和 remove() 都是synchronized修饰的
    现在有一个Vector对象 名叫v
    有两个线程对象 名叫 t1 和 t2

    当t1线程调用v对象 add() 方法已经开始执行了
        但是还没执行结束呢 此时时间片突然耗尽
        而t2线程抢到了时间片

问:
    t2能不能调用v对象的add()?    false
    t2能不能调用v对象的remove()?    false


已知:Hashtable的put() 和 remove() 都是synchronized修饰的
    现在有两个Hashtable对象 名叫 h1 和 h2
    有两个线程对象 名叫 t1 和 t2
    当t1线程调用h1的put() 方法已经开始执行了
    但是还没执行结束呢 此时时间片突然耗尽
    而t2线程抢到了时间片

问:
    t2线程能不能调用h1对象的put()?        false
    t2线程能不能调用h1对象的remove()?    false
    t2线程能不能调用h2对象的put()?        true
    t2线程能不能调用h2对象的remove()?    true


综上所述 哪怕synchronized修饰符加在方法上
    也不是对方法进行加锁 而是对调用方法的对象进行加锁

    *:Java当中只有每个对象才有锁标记 一定是对对象进行加锁

      *:Vector HashTable StringBuffer 之所以线程安全 是因为底层大量方法使用了synchronized修饰

          *:单例模式的懒汉式 需要synchronized

          *:synchronized隔代丢失...  父类的synchronized方法   能够被子类继承得到    但是方法的synchronized就没了

        2.可重入锁(ReentrantLock)

                java.util.concurrent.locks.ReentrantLock

                java包的工具包的并发包的锁包的可重入锁                

                                                                since JDK5.0  by Doug Lea

                                lock()                                  unlock()

                                加锁                                     释放锁

                ReentrantLock构造方法可以传参指定公平锁或非公平锁

                        new ReentrantLock(true);

import java.util.concurrent.locks.*;
public class TestConcurrentErrorWithLock{
	public static void main(String[] args){
		Lock lock = new ReentrantLock();
		Student stu = new Student("张曼玉","女士");
		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;
	}
}

DeadLock  死锁

互斥锁标记食用过多或者使用不当  就会出现多个线程相互持有对方想要申请的资源 不是放的情况下 又去申请对方已经持有的资源  从而双双进入对方已经持有 资源的锁池当中  产生永久的阻塞

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{Thread.sleep(10);}catch(Exception e){e.printStackTrace();}
				//当前线程Benz放弃east对象的锁标记 进入east对象的等待池
				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{Thread.sleep(10);}catch(Exception e){e.printStackTrace();}

				synchronized(east){
					System.out.println("李总和他的宝马又占领了泉城路东侧");
					east.notify();
				}
			}
			System.out.println("李总驾驶宝马驶过泉城路");

		}
	}
}

如何解决死锁

                一块空间:对象的等待池

                三个方法:wait() / notify() /notifyAll()

                wait() : 让当前线程放弃已经持有的锁标记  并且进入调用方法的那个对象的等待池

                notify():当前线程从调用方法的那个对象的等待池当中 随机唤醒的一个线程

                notifyAll : 唤醒所有阻塞的线程

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("美国人就餐完毕");
		}
	}
}

                *:这三个方法都不是Thread类的方法  而是Object类的方法  因为Java 当中每个对象都有等待池  都需要操作等待池  所以这三个方法被定义在Object类当中

                *: 这三个方法都必须在已经持有锁标记的前提下才能使用  也就是说 他们都一定出现在synchronizedz的大括号当中    abc.wait()就一定在synchronized(abc){这里写}     如果没有拿到对象的锁标记就尝试操作对象的等待池  不但会操作失败  还会出发运行时异常    IllegalMonitorStateException

                借助wait()  /  notify()  实现两个线程交替运行

//synchronized    等待池         wait() notify() notifyAll()
ReentranLock    Condition   await() signal() signalAll()

锁池和等待池 都是Java当中每个对象都有一份的空间
    而且都是用于存放线程任务的空间

    锁池:存放的是想要拿到对象的锁标记 但是还没成功的线程

    等待池:存放的是原本已经拿到对象的锁标记
            为了避免跟另外的线程永久阻塞
            又主动释放锁标记的线程...

    它们的区别主要在如下三个层面

    1.进入的时候是否需要释放资源:
        锁池不需要
        等待池需要先释放资源
    2.离开的时候是否需要调用方法
        锁池不需要
        等待池需要notify()/notifyAll()
    3.离开之后去到什么状态
        离开锁池返回就绪
        离开等待池返回锁池

//Object类的一个对象  有属性 有方法 有互斥锁Monitor 有锁池 有等待队列

public class TestSwitchThread{
	public static void main(String[] args){
		RightThread rt = new RightThread();
		LeftThread lt = new LeftThread(rt);

		lt.start();
		//
	}
}
class X{
	static Object obj = new Object();			//X.obj
}
class LeftThread extends Thread{
	RightThread rt;
	public LeftThread(RightThread rt){
		this.rt = rt;
	}
	@Override
	public void run(){
		synchronized(X.obj){
			rt.start();
			for(int i = 0;i<666;i++){
				System.out.println("左jio");//1
				try{X.obj.wait();}catch(Exception e){e.printStackTrace();}//2
				X.obj.notify();//6
			}
		}
	}
}
class RightThread extends Thread{
	@Override
	public void run(){
		synchronized(X.obj){
			for(int i = 0;i<666;i++){
				System.out.println("			右jio");//3
				X.obj.notify();
				try{X.obj.wait();}catch(Exception e){e.printStackTrace();}
			}
		}
	}
}

import java.util.concurrent.locks.*;
public class TestSwitchThreadWithLock{
	public static void main(String[] args){
		RightThread rt = new RightThread();
		LeftThread lt = new LeftThread(rt);

		lt.start();
		//
	}
}
class X{
	//static Object obj = new Object();			//X.obj
	static Lock lock = new ReentrantLock();
	static Condition c = lock.newCondition();//新建一个阻塞条件
}
class LeftThread extends Thread{
	RightThread rt;
	public LeftThread(RightThread rt){
		this.rt = rt;
	}
	@Override
	public void run(){
		//synchronized(X.obj){
			X.lock.lock();
			rt.start();
			for(int i = 0;i<666;i++){
				System.out.println("左jio");//1
				try{X.c.await();}catch(Exception e){e.printStackTrace();}//try{X.obj.wait();}catch(Exception e){e.printStackTrace();}//2
				X.c.signal();//X.obj.notify();//6
			}
			X.lock.unlock();
		//}
	}
}
class RightThread extends Thread{
	@Override
	public void run(){
		//synchronized(X.obj){
			X.lock.lock();
			for(int i = 0;i<666;i++){
				System.out.println("			右jio");//3
				X.c.signal();//X.obj.notify();
				try{X.c.await();}catch(Exception e){e.printStackTrace();}//try{X.obj.wait();}catch(Exception e){e.printStackTrace();}
			}
			X.lock.unlock();
		//}
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jerry鹿17

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值