Java线程的安全性问题的分析

在说到线程的安全性问题的时候,我们来看下面这一段代码:

package com.breakyizhan.thread.t3;

public class Sequence {
	private int value;
	public int getNext() {
		return value ++;
	}

	public static void main(String[] args) {
		
		Sequence s = new Sequence();
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					System.out.println(Thread.currentThread().getName() + " " + s.getNext());
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					System.out.println(Thread.currentThread().getName() + " " + s.getNext());
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					System.out.println(Thread.currentThread().getName() + " " + s.getNext());
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
	}

}

我们要让int value的值不断增加,用三个线程来增加,那么这样会变成什么呢?我们可以看到下面这个结果:

Thread-0 0
Thread-1 1
Thread-2 2
Thread-1 4
Thread-0 3
Thread-2 5
Thread-0 6
Thread-2 7
Thread-1 7
Thread-0 8
..................

Thread-1和Thread-2同时操作的时候,value的值都是7,这样是不合常理的,不应该出现的。这是为什么呢?我们可以看一下,我们用JVM给的一个命令工具来看一下,这个Sequence.class类是做了什么?

java -verbose Sequence.class

Java的并发编程:线程的安全性问题的分析

获得的结果如下图:

Java的并发编程:线程的安全性问题的分析

在红色框框里面就是getNext()方法的执行流程图。 大体会出现线程安全问题的流程为 1. Thread-1拿到value的值6去执行; 2. Thread-1执行到第17步iadd的时候,没有执行第18步putfield的时候,Thread-2拿到了value的值6去执行; 3. Thread-1和Thread-2同时输出7. 那么,要让上面的代码不会出现上面的错误的话,拿怎么办呢?很简单,只要加上synchronized就行了,如下:

package com.breakyizhan.thread.t3;

public class Sequence {
	private int value;
	public synchronized int getNext() {
		return value ++;
	}

	public static void main(String[] args) {
		
		Sequence s = new Sequence();
//		while(true) {
//			System.out.println(s.getNext());
//		}
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					System.out.println(Thread.currentThread().getName() + " " + s.getNext());
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					System.out.println(Thread.currentThread().getName() + " " + s.getNext());
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					System.out.println(Thread.currentThread().getName() + " " + s.getNext());
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
	}

}

上面的错误对于订票系统来说是致命的,比如说,value++是value--,拿很多人抢最后一张票,结果有很多人抢到了,拿票务系统不是出了致命的错误么???

Java线程安全问题的必要条件

总结一下,要出现线程安全问题必须具备下面三个条件 •多线程环境下 •多个线程共享一个资源 •对资源进行非原子性操作

第三个条件是非原子性的操作,也就是说要有读写操作,只具备读操作是没有线程安全问题的。比如上面代码中的:

public int getNext() {
		return value ++;
	}

变成:

public int getNext() {
		return 666;
	}

那么,每个线程也只会返回666,这样就不具备线程的安全性问题了。

 
转载请保留页面地址:https://www.breakyizhan.com/java/6624.html
扫描二维码添加微信 
  • ,每次淘宝领取淘宝优惠券,淘宝购物更优惠。现在添加微信,还可以领取机械键盘50元优惠券!添加微信后回复机械键盘即可领取!
    支持我们,就用微信淘宝!