talk is cheap,show me the code
这里创建了包含10个线程的线程池,每个线程池对变量做1000次自增
如果去掉synchronized,最后的结果就不是10000,因为自增操作不是原子性的
举个栗子:
假如某个时刻变量inc的值为10,
线程1对变量进行自增操作,线程1先读取了变量inc的原始值,然后线程1被阻塞了;
然后线程2对变量进行自增操作,线程2也去读取变量inc的原始值,由于线程1只是对变量inc进行读取操作,而没有对变量进行修改操作,所以不会导致线程2的工作内存中缓存变量inc的缓存行无效,所以线程2会直接去主存读取inc的值,发现inc的值时10,然后进行加1操作,并把11写入工作内存,最后写入主存。
然后线程1接着进行加1操作,由于已经读取了inc的值,注意此时在线程1的工作内存中inc的值仍然为10,所以线程1对inc进行加1操作后inc的值为11,然后将11写入工作内存,最后写入主存。
那么两个线程分别进行了一次自增操作后,inc只增加了1。
最后说明一点,操作的原子性是靠锁保证的,仅仅用volatile关键字修饰是没有用的
因为volatile仅仅保证了变量进行操作时的可见性,也就是读取到最新的值,但是无法保证对变量的操作的原子性
那么volatile关键字修饰有啥用呢?
- 使用volatile关键字会强制将修改的值立即写入主存;
- 使用volatile关键字的话,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效);
- 由于线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。
package learn_synchronized;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class test_volatile {
// 可见性
public volatile int inc = 0;
public synchronized void increase() {
inc++;
}
public static void main(String[] args) {
final test_volatile test = new test_volatile();
ExecutorService addTreads = Executors.newFixedThreadPool(10);
for (int i =1; i <= 10;i++){
final int index=i ;
addTreads.execute(new Runnable(){
@Override
public void run() {
System.out.println("\n第"+index+"个线程"+Thread.currentThread().getName());
for(int j = 0;j < 1000;j++) {
test.increase();
}
}
});
}
addTreads.shutdown();
// 若关闭后所有任务都已完成,则返回true。
// 注意除非首先调用shutdown或shutdownNow,否则isTerminated永不为true。
// 返回:若关闭后所有任务都已完成,则返回true
while (!addTreads.isTerminated()) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("\n结束了!");
System.out.println("\n"+test.inc);
}
}