星辰的零落

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

 

如下例中,如果有三个线程T0,T1,T2。若ticket只剩最后1张票时,当T0抢到CPU资源时,T0运行if语句判断后休眠;T1又在此时抢到了CPU资源,T1也运行到if语句判断,此时票数仍然为1,因为T0并没有运行到把票数减少的语句,所以T1也在此时休眠·;如果T2又在此时抢到了CPU资源,那么它也会运行到if后,因为此时票数仍然没有减少。

那么问题来了,线程不会走回头路,那么当CPU回过头来再进行这三个线程时,三个线程都可以继续向下执行,但是只剩下了一张票,就会出现0和负数,就出现了错误。

public class Ticket implements Runnable {
    //共100票
    int ticket = 100;

    @Override
    public void run() {
        //模拟卖票
        while(true){
            if (ticket > 0) {
                //模拟选坐的操作
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
            }
        }
    }

 

java中提供了线程同步机制,它能够解决上述的线程安全问题。

         线程同步的方式有两种:

方式1:同步代码块

方式2:同步方法

同步代码块: 在代码块声明上 加上synchronized

synchronized (锁对象) {

    可能会产生线程安全问题的代码

}

同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。

使用同步代码块,对电影院卖票案例中Ticket类进行如下代码修改:

public class Ticket implements Runnable {
    //共100票
    int ticket = 100;
    //定义锁对象
    Object lock = new Object();
    @Override
    public void run() {
        //模拟卖票
        while(true){
            //同步代码块
            synchronized (lock){
                if (ticket > 0) {
                    //模拟电影选坐的操作
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在卖票:" + ticket--);
                }
            }
        }
    }
}

当线程遇到同步代码块后,线程判断同步锁是否还有,也就是上面代码中的lock,如果有,线程就把锁获取并进入同步中,当执行完同步代码块内的语句并出去同步代码块的时候,线程会将锁对象(也就是上面代码中的lock)还回去。如果线程遇到同步代码块但没有锁对象,那么没有锁的线程不能进入同步中。

 

加了同步后,导致程序运行速度下降,但在安全性和速度之间应选择安全性。

posted on 2017-11-22 00:32  星辰的零落  阅读(610)  评论(0编辑  收藏  举报