多线程并发执行及解决方法

用一个案例来说明:假如我们要实现一个售票的小功能,用3个线程售出共2000张票。

初始模型为:

package com.test7;

public class synchronizedTest {
    public static void main(String [] args){
        TicketWindow tw = new TicketWindow();
        Thread t1 = new Thread(tw);
        Thread t2 = new Thread(tw);
        Thread t3 = new Thread(tw);
        t1.start();
        t2.start();
        t3.start();
    }

}

class TicketWindow implements Runnable{
    int nums = 2000;

    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(nums>0){
                System.out.println(Thread.currentThread().getName()+"当前正在售出弟"+nums+"张票");
                nums--;
            }
            else{
                break;
            }
        }
    }
}

 代码运行结果为:

但是我们发现这3个线程会出现同时售出一张票的情况,这是怎么回事呢?以售出第1766张票为例,因为这3个线程是多线程并发执行的,当其中一个线程Thread-2访问第1766张票时,进入if语句,执行完

System.out.println(Thread.currentThread().getName()+"当前正在售出弟"+nums+"张票");

这条语句而还未执行nums--时,另一个线程Thread-1也开始访问第1766张票,进入了if语句,开始执行

System.out.println(Thread.currentThread().getName()+"当前正在售出弟"+nums+"张票");

此时便会同时售出两张1766张票。

我们知道,这种线程是极其危险的,那么怎么解决,让线程变得安全呢?

其实很简单,只需要加一个对象锁,什么是对象锁呢?别急,往下看。

如果我们把上面线程并发执行的过程抽象成上厕所的话,就一个马桶,很多个人在外面排队,如果两个或者多个人程同时上一个厕所,就很容易出现问题,这种线程是不安全的。要解决问题只需要在门上安一把锁,必须等上一个人解决了下一个人才能进入,要解决的一个一个来。(扩展一下,会不会有一直解决不了的情况呢?会,比如当一个线程a在A厕所中执行线程,线程b等a解决完了才能进入A厕所,而a执行过程中要调用B厕所,而B厕所正好有人在里面了,这个人正好是B。这样的话,线程A永远结束不了,就出现了死锁问题)

我们解决上述问题的方法就是加个对象锁,对象锁有0和1两种状态,默认情况为1,代表厕所里没人,线程可以进入。一旦线程进入,她就会变成0---有人的状态,其他线程不能进入,不能进入的线程会进入线程等待池中(blocked阻塞状态)。任何对象都可以充当对象锁,一般用this充当对象锁。

代码是

synchronized (Object) {要线程同步的代码块}

最终修改代码为:

package com.test7;

public class synchronizedTest {
    public static void main(String [] args){
        TicketWindow tw = new TicketWindow();
        Thread t1 = new Thread(tw);
        Thread t2 = new Thread(tw);
        Thread t3 = new Thread(tw);
        t1.start();
        t2.start();
        t3.start();
    }

}

class TicketWindow implements Runnable{
    int nums = 2000;

    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (this) {
                if (nums > 0) {
                    System.out.println(Thread.currentThread().getName() + "当前正在售出弟" + nums + "张票");
                    nums--;
                } else {
                    break;
                }
            }
        }
    }
}

运行结果为:多线程并发执行及解决方法_第1张图片

你可能感兴趣的:(java_demo,java)