【Android 系统开发】_“进程线程”篇 -- “同步” 和 “死锁”

Thread - 同步

问题引出

咱们如今来经过Runnable接口实现多线程,产生3个线程对象,模拟卖票的场景!java

class MyThread implements Runnable {
    private int ticket = 5;
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (ticket > 0) {
                try {
                    Thread.sleep(300);     // 加入延迟
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("卖票:ticket = " + ticket--);
           }
        }
    }
};

public class SyncDemo01 {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        Thread t1 = new Thread(mt);
        Thread t2 = new Thread(mt);
        Thread t3 = new Thread(mt);    
        t1.start();
        t2.start();
        t3.start();
    }
}

咱们执行下这段代码,结果以下:多线程

卖票:ticket = 5
卖票:ticket = 4
卖票:ticket = 5
卖票:ticket = 3
卖票:ticket = 2
卖票:ticket = 2
卖票:ticket = 1
卖票:ticket = 0
卖票:ticket = -1    // 票数居然还能负数?

为何会出现“负数”的状况:在上面的操做中,咱们能够发现,由于加入了“延迟操做”一个线程颇有可能在还没对票数进行减操做以前,其余线程就已经将票数减小了,这样就会出现票数为负的状况。并发

有没有方法解决?确定是有的!想解决这样的问题,就必须使用同步!所谓同步,就是指多个操做在同一个时间段内只能有一个线程进行,其余线程要等待此线程完成以后才能够继续执行。this

解决问题

解决资源共享的同步操做,有两种方法:同步代码块同步方法线程

同步代码块

所谓代码块就是指使用“{}”括起来的一段代码,若是在代码块上加上synchronized关键字,则此代码块就成为同步代码块。code

【同步代码块 - 格式】对象

synchronized(同步对象) {
    须要同步的代码 ;
}

咱们对代码进行修改:接口

class MyThread implements Runnable {
    private int ticket = 5;
    public void run() {
        for (int i = 0; i < 100; i++) {
            synchronized (this) {              // 加入同步操做
                if (ticket > 0) {
                    try {
                        Thread.sleep(300);     // 加入延迟
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("卖票:ticket = " + ticket--);
               }
            }
        }
    }
};

public class SyncDemo01 {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        Thread t1 = new Thread(mt);
        Thread t2 = new Thread(mt);
        Thread t3 = new Thread(mt);    
        t1.start();
        t2.start();
        t3.start();
    }
}

咱们从新执行下这段代码,结果以下:进程

卖票:ticket = 5
卖票:ticket = 4
卖票:ticket = 3
卖票:ticket = 2
卖票:ticket = 1

同步方法

除了能够将须要的代码设置成同步代码块外,也可使用synchronized关键字将一个方法声明成同步方法。资源

【同步方法 - 格式】

synchronized 方法返回值 方法名称(参数列表) {
}

咱们采用同步方法对代码进行修改:

class MyThread implements Runnable {
    private int ticket = 5;
    public void run() {
        for (int i = 0; i < 100; i++) {
            this.sale();                // 调用同步方法
        }
    }
    public synchronized void sale() {   // 声明同步方法
        if (ticket > 0) {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("卖票:ticket = " + ticket--);
        }
    }
};

public class SyncDemo01 {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        Thread t1 = new Thread(mt);
        Thread t2 = new Thread(mt);
        Thread t3 = new Thread(mt);    
        t1.start();
        t2.start();
        t3.start();
    }
}

咱们从新执行下这段代码,结果以下:

卖票:ticket = 5
卖票:ticket = 4
卖票:ticket = 3
卖票:ticket = 2
卖票:ticket = 1

从以上程序的运行结果能够发现,此代码完成了与以前同步代码块一样的功能。

总结

多个线程共享同一资源时须要进行同步,以保证资源操做的完整性。

Thread - 死锁

经过上面的例子,咱们发现,同步仍是颇有好处的,它能够保证资源共享操做的正确性,可是过多的同步也会产生问题,这就是咱们接下来要讨论“死锁”问题!

什么是死锁?

多线程以及多进程改善了系统资源的利用率并提升了系统 的处理能力。然而,并发执行也带来了新的问题 -- 死锁

在编写多线程的时候,必需要注意资源的使用问题,若是两个或多个线程分别拥有不一样的资源,而同时又须要对方释放资源才能继续运行时,就会发生死锁。

简单来讲:死锁就是当一个或多个进程都在等待系统资源,而资源自己又被占用时,所产生的一种状态。

形成死锁的缘由

多个线程竞争共享资源,因为资源被占用,资源不足或进程推动顺序不当等缘由形成线程处于永久阻塞状态,从而引起死锁。

形成死锁的四个条件

一、互斥条件:进程对于所分配到的资源具备排它性,即一个资源只能被一个进程占用,直到被该进程释放
二、请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已得到的资源保持不放。
三、不剥夺条件:任何一个资源在没被该进程释放以前,任何其余进程都没法对他剥夺占用
四、循环等待条件:当发生死锁时,所等待的进程一定会造成一个环路(相似于死循环),形成永久阻塞。

问题引出

如今张三想要李四的画,李四想要张三的书,因而产生了如下对话:

张三对李四说:“把你的画给我,我就给你书”
李四对张三说:“把你的书给我,我就给你画”

此时,张山在等着李四的答复,李四也在等着张三的答复,那么这样下去的结果就是,两我的都在等待,可是都没有结果,这就是“死锁”!

从线程角度来讲,所谓死锁就是指两个线程都在等待彼此先完成,形成了程序的停滞,通常程序的死锁都是在程序运行时出现,好比咱们经过一个代码范例来看看发生死锁的场景。

class Zhangsan {
    public void say() {
        System.out.println("Zhangsan say: give me your painting, i will give you my book!");
    }
    
    public void get() {
        System.out.println("Zhangsan got Lisi's painting!");
    }
}

class Lisi {
    public void say() {
        System.out.println("Lisi say: give me your book, i will give you my painting!");
    }
    
    public void get() {
        System.out.println("Lisi got Zhangsan's book!");
    }
}

public class ThreadDeadLock implements Runnable {
    private static Zhangsan zs = new Zhangsan();         // 实例化static型对象,数据共享
    
    private static Lisi ls = new Lisi();                 // 实例化static型对象,数据共享
    
    private boolean flag = false;                        // 声明标记,用于判断哪一个对象先执行
    
    public void run() {
        if (flag) {                                      // 判断标志位,flag为true,Zhangsan先执行
            synchronized (zs) {                          // 同步第一个对象
                zs.say();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (ls) {                      // 同步第二个对象
                    zs.get();
                }
            }
        } else {                                         // Lisi先执行
            synchronized (ls) {                          // 同步第二个对象
                ls.say();
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (zs) {                      // 同步第一个对象
                    ls.get();
                }
            }
        }
    }
    
    public static void main(String[] args) {
        ThreadDeadLock t1 = new ThreadDeadLock();
        ThreadDeadLock t2 = new ThreadDeadLock();
        t1.flag = true;
        t2.flag = false;
        Thread thA = new Thread(t1);
        Thread thB = new Thread(t2);
        thA.start();
        thB.start();
    }
}

咱们执行下这段代码,结果以下:

Zhangsan say: give me your painting, i will give you my book!
Lisi say: give me your book, i will give you my painting!

从程序的运行结果中能够看出,两个线程都在彼此等待着对方的执行完成,这样,程序就没法向下继续执行,从而形成了死锁的现象。

解决问题

要预防和避免死锁的发生,只需将上面所讲到的4个条件破坏掉其中之一便可。

如上面的代码当中,有四个同步代码块,只须要将其中一个同步代码块去掉,便可解决死锁问题,通常而言破坏“循环等待”这个条件是解决死锁最有效的方法。

相关文章
相关标签/搜索