JAVA多线程(6)——synchronized和volatile

0. 前言

转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52370068

在多线程中,提到线程安全、线程同步,我们经常会想到两个关键字:volatilesynchronized,那么这两者有什么区别呢?

1. volatile修饰的变量具有可见性

volatile是变量修饰符,其修饰的变量具有可见性。

可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,可以立即获取修改之后的值。

Java中为了加快程序的运行效率,对一些变量的操作通常是在该线程的寄存器或是CPU缓存上进行的,之后才会同步到主存中,而加了volatile修饰符的变量则是直接读写主存


2. volatile禁止指令重排 

volatile可以禁止进行指令重排。

指令重排是指处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证各个语句的执行顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性

程序执行到volatile修饰变量的读操作或者写操作时,在其前面的操作肯定已经完成,且结果已经对后面的操作可见,在其后面的操作肯定还没有进行


3.  synchronized 

synchronized可作用于一段代码或方法,既可以保证可见性,又能够保证原子性。

可见性体现在:通过synchronized或者Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存中。

原子性表现在:要么不执行,要么执行到底。

 

4. 总结
(1从而我们可以看出volatile虽然具有可见性但是并不能保证原子性。

2)性能方面,synchronized关键字是防止多个线程同时执行一段代码,就会影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized。

但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。

看下面例子:

public class VolatileTest {
    public volatile int inc = 0;
    public void increase() {
        inc++;
    }

    public static void main(String[] args) {
        final VolatileTest test = new VolatileTest();
        for(int i=0;i<10;i++){
            new Thread(){
                public void run() {
                    for(int j=0;j<1000;j++)
                        test.increase();
                };
            }.start();
        }

        while(Thread.activeCount()>2)  //保证前面的线程都执行完
            Thread.yield();
        System.out.println(test.inc);
    }
}
例子中用new10个线程,分别去调用1000increase()方法,每次运行结果都不一致,都是一个小于10000的数字。自增操作不是原子操作,volatile 是不能保证原子性的。使用volatile修饰int型变量i,多个线程同时进行i++操作。比如有两个线程ABvolatile修饰的i进行i++操作,i的初始值是0A线程执行i++时刚读取了i的值0,就切换到B线程了,B线程(从内存中)读取i的值也为0,然后就切换到A线程继续执行i++操作,完成后i就为1了,接着切换到B线程,因为之前已经读取过了,所以继续执行i++操作,最后的结果i就为1了。同理可以解释为什么每次运行结果都是小于10000的数字。

但是使用synchronized对部分代码进行如下修改,就能保证同一时刻只有一个线程获取锁然后执行同步代码。运行结果必然是10000。

public  int inc = 0;  
public synchronized void increase() {  
        inc++;  
}  

synchronized (this)和synchronized (object)区别?请java多线程高手帮忙解决。

03-31

下面自己写的帐号转账的例子,用synchronized (this)没有问题,用synchronized (object)就报java.lang.IllegalMonitorStateException: current thread not owner。rn并且这个object是从外边传入应该是一个对象,为什么还报这个错误。rnrn代码分别如下:rn[b][color=#99CCFF]并发访问安全的SyncAccount.java[/color][/b]rn[code=Java]rnpackage com.thread.account;rnrnimport java.util.Arrays;rnrnpublic class SyncAccount rn public static final double SUM_NUM = 10000;rn public static final int ACCOUNT_NUM = 10;rn public static final double ACCOUNT_ININ = SUM_NUM / ACCOUNT_NUM;rn private double[] account;rnrn private Object lock;rn //private final Object lock = new Object();// 采用this替代rnrn public SyncAccount(double initAmount,Object lock) rn this.lock=lock;rn rn account = new double[ACCOUNT_NUM];rn for (int i = 0; i < ACCOUNT_NUM; i++) rn account[i] = initAmount;rn rn rnrn public boolean checkFrom(int from, double transMoney) rn //synchronized (this) rn synchronized (lock) rn return account[from] >= transMoney;rn rn rnrn public void print(String prompt) rn //synchronized (this) rn synchronized (lock) rn System.outrn .println(prompt + "Arrays=" + Arrays.toString(account) + "");rn rn rnrn public boolean checkSum() rn double sum = 0;rnrn //synchronized (this) rn synchronized (lock) rnrn for (int i = 0; i < ACCOUNT_NUM; i++) rn sum = account[i] + sum;rn rn rnrn return sum == SUM_NUM;rn rnrn public int SyncTransform(int from, int to, double transMoney)rn throws InterruptedException rn int result = 0;rnrn //synchronized (this) rn synchronized (lock) rn String threadName = "Thread=" + Thread.currentThread().getName()rn + ",";rn System.out.println(threadName + "lock=" + lock.hashCode());rn System.out.println(threadName + "AccountFrom=" + from);rn System.out.println(threadName + "AccountTo=" + to);rn System.out.println(threadName + "Account[From]=" + account[from]);rn System.out.println(threadName + "transMoney=" + transMoney);rn print(threadName + "before trans:");rnrn while (account[from] < transMoney) // if的惯用法rn System.out.println("in wait()");rn wait();rn rnrn account[from] = account[from] - transMoney;rn account[to] = account[to] + transMoney;rn System.out.println(threadName + "check sum=" + checkSum());rn System.out.println(threadName + "notify all!");rn print(threadName + "after trans:");rn notifyAll();rnrn return result;rn rn rnrnrn[/code]rnrn[b]封装成多线程的SyncAccoutManager.java[/b][color=#99CCFF][/color]rn[code=Java]rnpackage com.thread.account;rnrnpublic class SyncAccoutManager implements Runnable rnrn private SyncAccount acc;rn private int transFrom;rnrn public SyncAccoutManager(SyncAccount acc,int transFrom) rn this.acc = acc;rn this.transFrom=transFrom;rn rnrn public SyncAccoutManager(SyncAccount acc) rn this.acc = acc;rn this.transFrom=0;rn rn rn // 客户端启动多线程,系统自动调用类的run方法rn public void run() rn try rn while (true) rn rn double transMoney = SyncAccount.ACCOUNT_ININ * Math.random();rn int transTo=(int)(SyncAccount.ACCOUNT_NUM *Math.random());rn rn acc.SyncTransform(transFrom,transTo,transMoney);rn Thread.sleep((long)(100*Math.random()));rn rn catch (InterruptedException e) rn System.out.println("error-------");rn e.printStackTrace();rn rnrn rnrnrn[/code]rnrn[b]测试客户端的MutiThreadClient.java[/b][color=#99CCFF][/color]rnrn

没有更多推荐了,返回首页

私密
私密原因:
请选择设置私密原因
  • 广告
  • 抄袭
  • 版权
  • 政治
  • 色情
  • 无意义
  • 其他
其他原因:
120
出错啦
系统繁忙,请稍后再试