Java多线程学*之线程数据安全(二)

发布于:2021-09-25 20:06:15

1. 数据不共享的情况


在探讨数据共享的话题前,先来看看数据不共享的情况,每一个线程里面的数据都是独立的,就像下面的例子,3个线程,每一个线程自己对自己的数据进行扣减,直到0为止


代码demo:


public class TestThread {
public static void main(String[] args) {

MyThread myThread_A = new MyThread("A");
MyThread myThread_B = new MyThread("B");
MyThread myThread_C = new MyThread("C");

myThread_A.start();
myThread_B.start();
myThread_C.start();

}

}

class MyThread extends Thread {

private int count = 5;
private String name;

public MyThread(String name) {
this.name = name;
}

@Override
public void run() {
// super.run();

while (count != 0) {
System.out.println(name + " = " + count);
count--;
}
}
}


2. 数据共享的情况


这里先看一段测试代码,看看共享数据的结构,按照理想的情况下,MyRunnable只实例了一次,所以里面的count=5将会被扣减5次,打印的结果应该是,5,4,3,2,1,因为这里有5个线程,每次调用run的时候,都会减去1,但是结果,确实有点出乎意外....而且每一次执行的结果都不一样


public class TestThread {
public static void main(String[] args) {

//新建一个带有Runnable接口的类
MyRunnable myRunnable = new MyRunnable();

//新建5个线程,但使用了同一个Runnable实例对象,意味着里面的数据是共享的
//这里的Thread(Runnable,String)是一个构造函数,第一次参数为Runnable接口,第二个为线程名称
Thread thread_A = new Thread(myRunnable,"A");
Thread thread_B = new Thread(myRunnable,"B");
Thread thread_C = new Thread(myRunnable,"C");
Thread thread_D = new Thread(myRunnable,"D");
Thread thread_E = new Thread(myRunnable,"E");

thread_A.start();
thread_B.start();
thread_C.start();
thread_D.start();
thread_E.start();

}

}

class MyRunnable implements Runnable {

private int count = 5;

public MyRunnable() {
}

@Override
public void run() {
// super.run();
System.out.println("当前线程名称" + Thread.currentThread().getName() + " = " + count);
count--;

}
}

?第一次结果:



第二次结果:



?第三次结果:



3.线程不安全


经过上面的例子隐身出了一个问题,就是线程安全问题,在实际场景当中,这是一个非常危险的问题,例如在双11,秒杀,活动中,很多买家同时在0点的时候按购买,但货品只有1个,很明显这里就是多线程处理同一个数据(货品库存量),那如果在线程不安全的情况下,会出现更上面例子一样的情况,两个人同一时间都在对同一个数字进行处理,结果有可能是,多名买家同时获得这个商品。


为什么这样呢,主要原因是count--这个代码,一般情况,这行代码做了3个动作


    获取count当前的值对count的值进行-1的动作对count重新赋值

那问题很明显就出在第一步,假如A线程运行到这一行代码获取到count的值为5,接下来,B线程抢到CPU的使用前,他也执行到了这行代码,获取count的值也是5,因为A线程还没有进行-1的操作


4.线程安全


那怎么办呢,关键字synchronized,在run方法前加上这句的代码就可以达到排队执行方法的作用了,意思就是说,在执行run代码的时候,线程先查看当前代码块有没有钥匙,如果有钥匙,即可进入这扇门(代码块),然后执行里面的内容,执行完之后就会把钥匙交出来,由下一个抢到钥匙的人进入,并执行里面的内容。在这个抢钥匙的过程中是人人*等,谁先抢到钥匙,谁先进入。我们叫这块区域“互斥区”。


?


package com.xu.chap2;

public class TestThread2 {

public static void main(String[] args) {

//新建一个带有Runnable接口的类
MyRunnable myRunnable = new MyRunnable();

//新建5个线程,但使用了同一个Runnable实例对象,意味着里面的数据是共享的
//这里的Thread(Runnable,String)是一个构造函数,第一次参数为Runnable接口,第二个为线程名称
Thread thread_A = new Thread(myRunnable, "A");
Thread thread_B = new Thread(myRunnable, "B");
Thread thread_C = new Thread(myRunnable, "C");
Thread thread_D = new Thread(myRunnable, "D");
Thread thread_E = new Thread(myRunnable, "E");

thread_A.start();
thread_B.start();
thread_C.start();
thread_D.start();
thread_E.start();

}

}

class MyRunnable implements Runnable {

private int count = 5;

public MyRunnable() {
}

@Override
synchronized public void run() {
// super.run();
System.out.println("当前线程名称" + Thread.currentThread().getName() + " = " + count);
count--;

}
}

第一次运行结果:



第二次运行结果:



至此,我们就成功实现了线程数据安全了,我们下回再见

相关推荐

最新更新

猜你喜欢