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--;
}
}
第一次运行结果:
第二次运行结果:
至此,我们就成功实现了线程数据安全了,我们下回再见