一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

深入理解Java 02 内置锁与死锁

时间:2015-11-03 编辑:简简单单 来源:一聚教程网

一、并发问题分析

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

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

二、解决方案

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


class PaperThread extends Thread{
        
     static int num = 100;//纸的数量  非静态的成员变量,非静态的成员变量数据是在每个对象中都会维护一份数据的。
     
     static Object o = new Object();
    
     public PaperThread (String name) {
        super(name);
     }
    
    @Override
    public void run() {
        while(true){
            //同步代码块
            synchronized ("锁") {              
                if(num>0){                   
                    System.out.println(Thread.currentThread().getName()
                    +"取走了1张纸,还剩"+(count-1)+"张"");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    num--;
                }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 synchronized  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 - 1000;
            }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. 存在两个或者两个以上的共享资源。
死锁现象的解决方案:死锁没有解决方案,只能尽量避免。

热门栏目