java并发(二)java线程安全 原子性操作

synchronized和volatile

对于并发编程不能避免的就是内存可见性和线程安全的问题。
比如这个例子:

class unsafeClass {
        private int mark;

        public int getMark() {
            return mark;
        }

        public void setMark(int mark) {
            this.mark = mark;
        }
    }

在多线程操作和读取mark变量的时候就会遇到问题,线程1读取mark,然后线程2读取mark又写入mark,但是这时线程1操作的mark就已经过期了。这样就会出现脏数据或者其他不可见的问题。

所以使用synchronized和volatile解决这个问题。

package day0128;

/**
 * @Author Braylon
 * @Date 2020/1/28 9:35
 */
public class JavaAtomicity {
    class unsafeClass {
        private int mark;

        public int getMark() {
            return mark;
        }

        public void setMark(int mark) {
            this.mark = mark;
        }
    }

    class safeClass {
        private int mark;

        public synchronized int getMark() {
            return mark;
        }

        public synchronized void setMark(int mark) {
            this.mark = mark;
        }
    }

    class safeClass2 {
        private volatile int mark;

        public int getMark() {
            return mark;
        }

        public void setMark(int mark) {
            this.mark = mark;
        }
    }
}

synchronized时java提供的一种原子性内置锁,线程的执行代码在进入synchronized代码块前会自动获取内部锁,这时候同步代码块会被阻塞挂起。
但是由于java中的线城市与操作系统的原生线程一一对应 ,所以当阻塞一个线程时需要从用户态切换到内核态,非常的耗费时间。如何避免呢?


这里我们就提到了volatile关键字。
由于使用synchronized锁太笨重了。于是java提供了一种弱的同步,这个关键字确保对一个变量的更新对其他线程马上可见。当一个变量被用volatile声明,线程在写入变量的时候不会把值缓存在寄存器或者别的地方,而是直接写穿,刷新回内存。

两种关键字优缺点比较

相同点是,synchronized和volatile都解决了共享变量的内存可见性问题。
不同点是,前者是独占锁同时只能有一个线程调用get方法,其他调用的线程都会被阻塞。缺点就是增加了切换上下文的开销。
可是volatile虽然没有上下文切换的开销,却不能保证原子性。

怎么选择使用这两种关键字

  • 当写入变量值不依赖于变量的当前值的时候。我们可以使用volatile来提高效率
  • 当对线程安全要求较高同时需要依赖于变量的当前值的时候,就是用synchronized。

有没有更好的方法呢?

我之前学习的时候就疑惑过,依然这两种方法在实际应用中都不太靠谱,那么现在以先的技术是如何解决这个问题的呢。
我在下一章中将会给大家分享,CAS操作。

发布了31 篇原创文章 · 获赞 33 · 访问量 2825

猜你喜欢

转载自blog.csdn.net/qq_40742298/article/details/104097727