Java 内存模型
Java的内存模型分为主存储器和工作存储器两种。 主存就是实例位置所在的区域。主存为全部线程锁共用;每一个线程都拥有自己独立的作业区,称为工作存储器。 当一个线程须要对某对象的一些字段进行操作时,就会把这些须要的信息从主存储器copy到自己的工作存储器。而这个拷贝就叫做工作拷贝。
讲到这里大家都能够想象Java是怎样保证内存一致的了。主存上面的内容就像是个标准,而每一个线程的工作拷贝就像是草稿。草稿上计算好的结果就要放到主存去。这样其它线程也能够看得到,这就是可见性了。当一个线程获得某一对象的锁,他就获得了改动这个对象状态的权利,他開始copy主存的内容到他的工作存储器。这个时候他对工作拷贝的不论什么改动都没有在主存上,而当他要释放锁的时候就必须把工作拷贝写入到主存去。为什么呢?他假设不写的话主存上的内容还是曾经的内容,仅仅只是其它线程不知道有个线程做了一些事情,能够忽略啊。问题就是不能忽略,为了保证每一个线程做的事情都被他后面的线程看到,他必须在释放锁之前就把内容写到主存,即使他自己的工作仅仅完毕了部分。
这样才有先获得锁和后获得锁的差别,先获得锁的线程所做的操作对后获得锁的线程是可见的。
当然线程不是仅仅有在释放锁时才会被工作拷贝写入主存。他能够在对对象的字段进行一次指定之后就进行一次写入,也能够到最后将终于值写入。由处理器决定。
方法的自变量和局部变量为每一个线程所独有,所以不用考虑同步。
多线程编程须要解决的两个问题就是线程间的干扰和内存不一致问题,synchronized在这里是恰到优点的,它的两个作用就是“线程的同步”和”内存的同步“。 synchronized所保护的方法或者块被称作临界区,在同一时刻仅仅有一个线程能够訪问,就像独木桥。并且是仅仅有一个人能够上去的独木桥。
内存的同步指的就是主存储器和工作存储器上的内容的同步,要保证两个内容是一样的。
那什么时候去进行同步呢。不可能时时刻刻都去同步吧,那全部线程都在一直看自己有没有跟主存同步。这样不是浪费时间吗?线程会在“欲进入synchronized块”和“欲退出synchronized块”是进行同步,同步包含双方面,一个是把自己的工作拷贝写到主存。一个是从主存读内容。在线程欲进入synchronized时会将工作拷贝中没有写入到主存的内容强制写入主存,在退出synchronized是会做相同的强制写入。
不同的是在进入synchronized时线程还会释放工作存储器。这样能够保证新的内容的读入,可是在欲退出synchronized时则不会释放工作存储器,毕竟这个时候还不用考虑读入新的内容,总之下次进入synchronized块时一定会读的。
由此可见主存和工作存储器上的内容并非总是同步的,并且大多数时候应该是不一样的。内存的同步就是保证须要用到这个内容的时候和主存是一致的。对它做完改动之后保证把内容写入主存。
换句话说保证别人写的你看到了,保证你写的别人也看到了。
volatile的与synchronized不同的是它仅仅会进行内存的同步,并不能保证线程的同步。
当线程引用valotile字段时,会先从主存copy值到工作存储器,当线程指定值到volatile字段时会copy值到主存。
volatile的还有一个功能是保证对long和double的指定动作的原子性。指定一个int或者bye之类的基本数据类型都是原子的,仅仅有指定long和double须要分成两次,这就可能导致在多线程的时候出现脏数据。所以在多线程编程中一定要保证对long和double的原子訪问。