最新消息:文章中包含代码时,请遵守代码高亮规范!

深入理解Java 02 内置锁与死锁【原创】

原创文章 Joash 3105浏览 0评论

一、并发问题分析
如果一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,此时线程是安全的。

而实际中存在这样的情况:
1.存在两个或者两个以上的线程对象,而且线程之间共享着一个资源。
2.有多个语句操作了共享资源。

二、解决方案
用一个案例来说明:张刘亮和郑旭共用一包面纸,两个人相当于两个线程,每一张纸不可以两人同时抽取
方式一:同步代码块
线程类:

class PaperThread extends Thread{
		
     static int count= 100;//纸的数量  静态,在方法区中的数据共享区中维护一份。
	 
     static Object o = new Object();
	
	 public PaperThread (String name) {
		super(name);
	 }
	
	@Override
	public void run() {
		while(true){
			//同步代码块
			synchronized (o) {				
				if(count>0){			          
                    System.out.println(Thread.currentThread().getName()
                    +"取走了1张纸,还剩"+(count-1)+"张"");
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					count--;
				}else{
					System.out.println("用光了..");
					break;
				}
			}		
		}
	}		
} 

测试类

	
public class Test{
	public static void main(String[] args) {
		//创建两个线程
		PaperThread thread1 = new PaperThread("张刘亮");
		PaperThread thread2 = new PaperThread("郑旭");
		//开启线程
		thread1.start();
		thread2.start();		
	}	
}

同步代码块要注意事项:
1. 任意的一个对象都可以做为锁对象。
2. 在同步代码块中调用了sleep方法并不是释放锁对象的。
3. 只有真正存在线程安全问题的时候才使用同步代码块,否则会降低效率的。
4. 多线程操作的锁对象必须是唯一共享的,否则无效。

方式二:同步函数:同步函数就是使用synchronized修饰一个函数。
线程类:

class PaperThread extends Thread{
	
	static int count = 100;
	
	public currentThread(String name){
		super(name);
	}
	
	//@Override  
	public void run() {
	   getPaper();
	}	
	
	//静态的函数---->函数所属的类的字节码文件对象--->PaperThread.class 唯一的。
	public static synchronized void getPaper(){
		while(true){			
		    if(count>0){
		         System.out.println(Thread.currentThread().getName()
                         +"取走了1张纸,还剩"+ (count-1)+"张");
			 count= count - 1;
		    }else{
			   System.out.println("纸用光了...");
			   break;
		    }	
		}
	}	
}

测试类:

public class Test {

	public static void main(String[] args) {
		//创建两个线程对象
		PaperThread thread1 = new PaperThread("张刘亮");
		PaperThread thread2 = new PaperThread("郑旭");
		//调用start方法开启线程取钱
		thread1.start();
		thread2.start();		
	}	
}

同步函数要注意的事项 :
1. 如果是一个非静态的同步函数的锁对象是this对象,如果是静态的同步函数的锁 对象是当前函数所属的类的字节码文件(class对象)。
2. 同步函数的锁对象是固定的,不能由你来指定的。

推荐使用:同步代码块。
原因:
1. 同步代码块的锁对象可以由我们随意指定,方便控制。同步函数的锁对象是固定的,不能由我们来指定。
2. 同步代码块可以很方便控制需要被同步代码的范围,同步函数必须是整个函数的所有代码都被同步了。

三、死锁
同步机制解决了线程安全问题,但是也同时引发死锁现象。
案例:张刘亮和郑旭打LOL,只有一个鼠标和一个键盘,需要同时获取鼠标和键盘才能打LOL

class DeadLock extends Thread{
	
	public DeadLock(String name){
		super(name);
	}
	
	public void run() {
		if("张刘亮".equals(Thread.currentThread().getName())){
			synchronized ("键盘") {
				System.out.println("张刘亮拿到了键盘,准备去拿鼠标");
				synchronized ("鼠标") {
					System.out.println("张刘亮拿到了键盘和鼠标,撸了起来");
				}
			}
		}else if("郑旭".equals(Thread.currentThread().getName())){
			synchronized ("鼠标") { 
				System.out.println("郑旭拿到了鼠标,准备去拿键盘");
				synchronized ("键盘") {
					System.out.println("郑旭拿到了键盘和鼠标,撸了起来");
				}
			}		
		}	
	}
}
public class TestDeadLock {

	public static void main(String[] args) {
		DeadLock thread1 = new DeadLock("张刘亮");
		DeadLock thread2 = new DeadLock("郑旭");
		//开启线程
		thread1.start();
		thread2.start();		
		
	}
	
}

死锁现象出现 的根本原因:
1. 存在两个或者两个以上的线程。
2. 存在两个或者两个以上的共享资源。
死锁现象的解决方案:死锁没有解决方案,只能尽量避免。

转载时请注明出处及相应链接,本文永久地址:https://blog.yayuanzi.com/14407.html


pay_weixin
pay_weixin
微信打赏
pay_weixin
支付宝打赏
感谢您对作者joash的打赏,我们会更加努力!    如果您想成为作者,请点我

您必须 登录 才能发表评论!