聊一聊synchronized

原子性:表示一个不可分割的操作.Synchronized 同步代码块和同步方法可以确保以原子的方式操作,但是常见的误解是:Synchronized 只能用于实现原子性或者临界区,其实

Synchronized  还有一个更重要的特性:内存可见性(Memery  Visibility),我们不仅希望防止某个Thread正在使用对象状态而被其他的Thread修改,而且希望某个线程修改了状态,对其他线程能看到变化。如果没有同步这种情况就无法实现。可以通过Synchronized 或者 类库中的内置锁保证对象安全的发布出来.

可见性:在多线程环境下,当读操作和写操作在不同的的线程中.无法保证读操作能够适当的看到修改后的状态.为了确保多个线程之间堆内存的可见性,必须使用同步机制。

package net.concurrent;

public class NoVisibility {

	private static boolean isRead;
	private static int number;

	static class ReadThread extends Thread {

		public void run() {

			while (!isRead) {
				Thread.yield();
			}
			System.out.println("ReadThread:" + number);
		}
	}

	static class ReadThread1 extends Thread {

		public void run() {

			while (!isRead) {
				Thread.yield();
			}
			System.out.println("ReadThread1:" + number);
		}
	}

	static class ReadThread2 extends Thread {

		public void run() {

			while (!isRead) {
				Thread.yield();
			}
			System.out.println("ReadThread2:" + number);
		}
	}

	static class ReadThread3 extends Thread {

		public void run() {

			while (!isRead) {
				Thread.yield();
			}
			System.out.println("ReadThread3:" + number);
		}
	}

	static class ReadThread4 extends Thread {

		public void run() {

			while (!isRead) {
				Thread.yield();
			}
			System.out.println("ReadThread4:" + number);
		}
	}

	public static void main(String args[]) throws InterruptedException {

		for (int i = 0; i < 50; i++) {
			isRead = false;
			new ReadThread().start();
			new ReadThread1().start();
			new ReadThread2().start();
			new ReadThread3().start();
			new ReadThread4().start();
			number++;
			number++;
			number++;
			number++;
			isRead = true;
			Thread.currentThread().sleep(1000);
			System.out.println("----------------------------------------");
		}

	}
}

在不同步的情况下,读线程可能会出现死循环,可能会读到isReader,但是Number会是旧数据.这种现象称为:重排序(Reordering).在没有同步的情况下写入Reader,那么读线程看到的顺序可能与写入的的顺序相反.编译器、处理器在运行过程中对操作的顺序进行调整,在缺乏同步的情况下,对内存的操作的的状态变量进行判断,几乎无法得到正确的结果。只要数据在多个线程中进行访问,就要使用同步

数据失效:食物过期了,还可以吃只不过味道查了一些,但是程序中数据失效呢?在WEB应用程序中统计访问命中数,出现数据失效还不算多严重的问题,在其他的情况数据失效可能会导致严重的安全问题和活跃性问题。例如对象的引用链表指针失效,出现意外的异常,被破坏的数据结构,不精确的算法,死循环。那么情况就复杂多了.


非64位的原子操作:在没有进行同步的情况下,读取一个可变、共享的值,可能会读到一个失效的值,至少这个值是由某个线程设置的,不是一个随机值,这种安全性也成为了最低的安全性.最低的安全性适用于绝大多数变量,但也存在一个例外,非volatile的64位变量(double/long).Java内存模型要求:所有的读取和写入操作必须是原子操作,但对于double 和 long 类型来说,JVM 将64位的读操作或者写操作分解为两个32位的操作。我们可能会读取某个值的高32位或者低32位。所以即使不考虑数据失效的情况下,double 和long 在多线程中也是不安全的。所以需要使用volatile声明或者使用锁包括起来.


总结:加锁的行为不仅仅是为了线程互斥,原子操作。更重要的是实现内存的可见性,为了确保所有的线程都能够看到共享变量最新的值,所有的读操作和写操作必须在同一个锁上同步.






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值