Java学习笔记45(多线程二:安全问题以及解决原理)

2018-01-20 19:53:03来源:cnblogs.com作者:一清人点击

分享

线程安全问题以及解决原理:

多个线程用一个共享数据时候出现安全问题

一个经典案例:

电影院卖票,共有100座位,最多卖100张票,买票方式有多种,网上购买、自主售票机、排队购买

三种方式操作同一个共享数据,这时候会出现安全问题:

示例:

package demo1;public class Tickets implements Runnable {    private int ticket = 100;        public void run(){        while(true){            if (ticket>0) {                System.out.println(Thread.currentThread().getName()+"出售第"+ticket--+"张票");            }        }    }}
package demo1;public class ThreadDemo {    public static void main(String[] args) {        Tickets t = new Tickets();        Thread t0 = new Thread(t);        Thread t1 = new Thread(t);        Thread t2 = new Thread(t);        t0.start();        t1.start();        t2.start();    }}

一般不会出现问题,但是要想到这种问题

但是,假设只剩下最后最后一张票,一个线程抢到CPU资源执行,在判断结束时候,CPU资源被其他线程抢到,其他线程判断然后执行,

这时候轮到开始时候的线程,由于已经判断完,继续执行,这时候票数就会变成负数,这里就出现了问题

解决方法:

同步代码块

原理:一个线程进入数据操作的时候,阻止其他线程执行

package demo1;public class Tickets implements Runnable {    private int ticket = 100;    private Object obj1 = new Object();    public void run() {        while (true) {            synchronized (obj1) {                if (ticket > 0) {                    System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");                }            }        }    }}

不过,虽然安全了,但是运行速度下降

但是,我们为了安全性可以不顾及速度,无论如何都要保证安全性

这里传入的对象参数简称作:同步锁,专业名称:对象监视器

原理:

没有锁的线程不能执行,只能等待

线程遇到同步代码块后判断是否有同步锁,如果有,拿走锁,进入同步中执行,执行完毕后将锁对象还回去

另一个线程遇到代码块后没有锁,无法进入,原来的线程把锁还回去之后新线程再获取锁,循环下去

这里明显可以看出,这么多的过程,速度自然就慢下来了

采用同步方法解决问题:

优点:代码量更低

package demo1;public class Tickets implements Runnable {    private int ticket = 100;        public void run() {        while (true) {            payTicket();        }    }    public synchronized void payTicket() {        //同步方法的对象锁是本类对象引用:即为this        //静态方法的锁是本类类名.class        if (ticket > 0) {            System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");        }    }}

 缺点:如果出现了异常,方法的锁对象没有释放,不出同步,锁不会释放

这里就需要用到一个Lock接口:

提供了更广泛的锁定操作

改进之前的售票案例:

package demo1;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class Tickets implements Runnable {    private int ticket = 100;    private Lock lock = new ReentrantLock();    public void run() {        while (true) {            lock.lock();            if (ticket > 0) {                System.out.println(Thread.currentThread().getName() + "出售第" + ticket-- + "张票");            }            lock.unlock();        }    }}

死锁:

同步锁引发的弊端:

当线程任务中出现了多个同步时,如果同步中嵌套了其他的同步,这时候就会引发一种现象,程序出现无限等待,这种现象称之为死锁

通俗解释:两个人吃一碗面,却只有一双筷子,两个人一人抢到一支筷子,规定不能用手抓,这时候就无法吃面

代码实现:

package demo1;public class LockA {    private LockA(){}        public final static LockA locka =new LockA();}
package demo1;public class LockB {    private LockB(){}        public final static LockB lockb =new LockB();}
package demo1;public class DeadLock implements Runnable {    private int i = 0;    public void run() {        while (true) {            if (i % 2 == 0) {                synchronized (LockA.locka) {                    System.out.println("if...locka");                    synchronized (LockB.lockb) {                        System.out.println("if...lockb");                    }                }            } else {                synchronized (LockB.lockb) {                    System.out.println("else...lockb");                    synchronized (LockA.locka) {                        System.out.println("else...locka");                    }                }            }            i++;        }    }}
package demo1;public class DeadLockDemo {    public static void main(String[] args) {        DeadLock dead = new DeadLock();        Thread t0 = new Thread(dead);        Thread t1 = new Thread(dead);        t0.start();        t1.start();    }}

运行后发现,会卡在某一处不动,但是并没有停止

微信扫一扫

第七城市微信公众平台