Synchronized和Volatile的区别

1.多线程三大特性?

原子性、可见性、有序性
(1)什么是原子性?

        即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
一个很经典的例子就是银行账户转账问题:
比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。这2个操作必须要具备原子性才能保证不出现一些意外的问题。
        我们操作数据也是如此,比如i = i+1;其中就包括,读取i的值,计算i,写入i。这行代码在Java中是不具备原子性的,则多线程运行肯定会出问题,所以也需要我们使用同步和lock这些东西来确保这个特性了。
原子性其实就是保证数据一致、线程安全一部分.

(2) 什么是可见性

        当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
        若两个线程在不同的cpu,那么线程1改变了i的值还没刷新到主存,线程2又使用了i,那么这个i值肯定还是之前的,线程1对变量的修改线程没看到这就是可见性问题。
这是产生线程不安全最主要的问题,假如我们有A,B两个线程共享同一变量,变量放在方法区,也就是主内存,JVM体系结构中堆和方法区是线程共享的,A,B线程存放着主内存变量的副本,即各自对自己所在线程的主内存变量的副本读写,假如变量是10,A对变量进行减1,这时值为9,但是没来及刷新到主内存,B保存的是变量的副本10,B这时也进行减1,这时值也为9,导致数据不一致。
具体原理可以参考:java内存模型

(3)什么是有序性

程序执行的顺序按照代码的先后顺序执行。
一般来说处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。 如下:
int a = 10; //语句1
int r = 2; //语句2
a = a + 3; //语句3
r = a*a; //语句4
则因为重排序,他还可能执行顺序为 2-1-3-4,1-3-2-4
但绝不可能 2-1-4-3,因为这打破了依赖关系。
显然重排序对单线程运行是不会有任何问题,而多线程就不一定了,所以我们在多线程编程时就得考虑这个问题了。

2.Synchronized关键字的两种用法?

说明:Java提供了一种内置的锁机制来支持原子性
每一个Java对象都可以用作一个实现同步的锁,称为内置锁,线程进入同步代码块之前自动获取到锁,代码块执行完成正常退出或代码块中抛出异常退出时会释放掉锁
内置锁为互斥锁,即线程A获取到锁后,线程B阻塞直到线程A释放锁,线程B才能获取到同一个锁
        内置锁使用synchronized关键字实现,synchronized关键字有两种用法:
        1.修饰需要进行同步的方法(所有访问状态变量的方法都必须进行同步),此时充当锁的对象为调用同步方法的对象
        2.同步代码块和直接使用synchronized修饰需要同步的方法是一样的,但是锁的粒度可以更细,并且充当锁的对象不一定是this,也可以是其它对象,所以使用起来更加灵活
同步代码块synchronized
就是将可能会发生线程安全问题的代码,给包括起来。

synchronized(同一个数据){
 可能会发生线程冲突问题
}
就是同步代码块 
synchronized(对象)//这个对象可以为任意对象 
{ 
    需要被同步的代码 
} 

if (flag) {
            while (trainCount > 0) {
                synchronized (oj) {
                    try {
                        Thread.sleep(10);
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                    if (trainCount > 0) {
                        System.out
                                .println(Thread.currentThread().getName() + "," + "出售第" + (100 - trainCount + 1) + "票");
                        trainCount--;
                    }
                }

            }
        } 

synchronized修饰方法
1.在方法上修饰synchronized 称为同步方法,使用的是this锁
2.方法上加上static关键字,使用synchronized 关键字修饰 称为静态同步方法,使用锁是当前类的字节码文件,可以用 getClass方法获取,也可以用当前 类名.class 表示。

证明同步方法使用的是this锁,this指样例中的SaleTickets对象
/**
 * @author Administrator
 */
class SaleTickets implements Runnable {
    private int trainTickets = 100;
    private Object obj = new Object();
    private boolean flag = true;


    @Override
    public void run() {
        if (flag) {
            while (trainTickets > 0) {
                synchronized (obj) {
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (trainTickets > 0) {
                        System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainTickets + 1) + "张票");
                        trainTickets--;
                    }
                }
            }
        } else {
            while (trainTickets > 0) {
                sale();
            }
        }
    }

    public synchronized void sale() {
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (trainTickets > 0) {
            System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - trainTickets + 1) + "张票");
            trainTickets--;
        }
    }

    public static class Test {
        public static void main(String[] args) throws Exception {
            SaleTickets saleTickets = new SaleTickets();
            Thread thread1 = new Thread(saleTickets, "1号窗口");
            Thread thread2 = new Thread(saleTickets, "2号窗口");
            thread1.start();
            Thread.sleep(40);
            saleTickets.flag = false;
            thread2.start();
        }
    }
}

此时synchronized (obj),锁为任意对象;
在这里插入图片描述

此时synchronized (this),锁为this;
在这里插入图片描述
总结: 同步代码块为obj锁时发生线程不安全,为this锁时线程安全,证明同步方法为this锁

Volatile关键字用法?

可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,可以立即获取修改之后的值。
在Java中为了加快程序的运行效率,对一些变量的操作通常是在该线程的寄存器或是CPU缓存上进行的,之后才会同步到主存中,而加了volatile修饰符的变量则是直接读写主存。
Volatile 保证了线程间共享变量的及时可见性,但不能保证原子性
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值