精华内容
下载资源
问答
  • 首先要清楚的是synchronized锁住的不是代码而是对象,因而在编写相关的代码块时要注意线程同步安全问题,下面就来以实例解析Java中的synchronized关键字与线程安全问题
  • Volatile关键字与线程安全 volatile关键字,它的语义有二: 1.volatile修饰的变量对于其他线程具有立即可见性 即被volatile修饰的变量值发生变化时,其他线程可以立马感知。而对于普通变量,值发生变化后,需要...

    Volatile关键字与线程安全

    volatile关键字,它的语义有二:
    1.volatile修饰的变量对于其他线程具有立即可见性
    即被volatile修饰的变量值发生变化时,其他线程可以立马感知。而对于普通变量,值发生变化后,需要经过store、write过程将变量从当前线程的工作内存写入主内存,其他线程再从主内存通过read、load将变量同步到自己的工作内存,由于以上流程时间上的影响,可能会导致线程的不安全。
    当然要说使用volatile修饰过的变量是线程安全的,也不全对。因为volatile是要分场景来说的:如果多个线程操作volatile修饰的变量,且此时的“操作”是原子性的,那么是线程安全的,否则不是。如:
    volatile int i=0;
    线程1执行: for(;i++;i<100);
    线程2执行: for(;i++;i<100);
    最后 i 的结果不一定会是200(线程不安全),因为i++操作不是原子性操作,它涉及到了三个子操作:从主内存取出i、i+1、将结果同步回主内存。那么就有可能一个线程拿到最新值,正开始执行第二个子操作,而值还未来得及改变时,第二个线程就已经拿到同样的值开始执行第二个子操作了。这样一来,就有可能两个线程给同一个值加了一次1,所以就算有volatile修饰也是无力回天。
    这时,我们应该使用synchronize或concurrent原子类来保证“操作”的原子性。故volatile的使用场景应该是:修饰的变量的有关操作都是原子性的时候。比如修饰一个控制标志位:
    volatile boolean tag=true;
    线程1 while(tag){};
    线程2 while(tag){};
    当tag=false时,两个线程都能马上感知到并停止while循环,因为简单的赋值语句属于原子操作(赋予具体的值而不是变量),它只负责把主内存的tag同步为true。能实现可见性的关键字除了volatile,还有synchronize与final,synchronize是因为变量执行解锁操作前,会把变量同步到主内存(自带可见性);而final则是被其修饰的变量一旦初始化,且构造器没有把this引用传递到外面去的情况下,其他线程就可以看见它的值(因为它永不发生变化)。
    2.禁止指令重排序
    new一个对象可以分解为如下的3行伪代码
    memory=allocate(); //1:分配对象的内存空间
    ctorInstance(memory); //2:初始化对象
    instance=memory; //3:设置instance指向刚分配的内存地址
    上面3行代码中的2和3之间,可能会被重排序(在一些JIT编译器上,这种重排序是真实发生的)。2和3之间重排序之后的执行时序如下
    memory=allocate(); //1:分配对象的内存空间
    instance=memory; //3:设置instance指向刚分配的内存地址,注意此时对象还没有被初始化
    ctorInstance(memory); //2:初始化对象
    如果发生重排序,另一个并发执行的线程B就有可能在还没初始化对象操作前就拿走了instance,但此时这个对象可能还没有被线程真正初始化,因此这是线程不安全的。
    Java先行发生原则:
    1.程序次序规则:在一个线程内,按照程序代码顺序(准确说应是控制流顺序),先写的先发生,后写的后发生。
    2.管程锁定规则:一个解锁操作先于后面对该锁的锁定操作。
    3.volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作。
    4.线程启动规则:线程对象的start()方法先行发生于此线程的每一个动作。
    5.线程终止规则:线程的所有操作都先于此线程的终止检测。
    6.线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。可通过Thread.interrupted()方法检测是否会有中断将发生。
    7.对象终结规则:一个对象的初始化发生先行于它的finalize()方法的开始。
    8.传递性:如果操作A先于B,B先于C,那么A先于C。
    例:
    private int value=0;
    public void setValue(int value){
    this.value=value;
    }
    public int getValue(){
    return this.value;
    }
    如果线程1调用setValue(1)方法,线程2调用getValue(),那么得到的value是0还是1呢,这是不确定的。因为它不满足上面的先行发生原则:因为不是在一个线程,所以不符合程序次序规则;因为没有同步块,也就不存在加锁和解锁,因此也不符合管程锁定规则;没有volatile修饰,也就不存在volatile变量规则,更没有后面的线程相关规则和传递性可言。针对此,可做以下修改:
    将上面的setter、getter方法都用synchronize修饰,使其满足管程锁定规则;或者使用volatile修饰,因为setValue()是基本的赋值操作,属于原子操作,因此符合volatile的使用场景。
    注:
    1.从上总结,线程安全一般至少需要两个特性:原子性和可见性。
    2.synchronize是具有原子性和可见性的,所以如果使用了synchronize修饰的操作,那么就自带了可见性,也就不再需要volatile来保证可见性了。
    2.对于上面代码,若想实现线程安全的数字的自增自减等操作,也可使用 java.util.concurrent.atomic包来进行无锁的原子性操作。在其底层实现中,如AtomicInteger,同样是使用了volatile来保证可见性;使用Unsafe调用native本地方法CAS,CAS采用总线加锁或缓存加锁方式来保证原子性。
    参考:
    (Java并发编程:volatile关键字解析)http://www.importnew.com/18126.html

    转载于:https://www.cnblogs.com/erma0-007/p/8645516.html

    展开全文
  • myList.add("Hello");
  • 利用synchronized关键字解决线程安全问题    1.线程安全问题  当多条语句在操作同一个线程共享数据时,一个线程对多条语句操作的时候  还没有执行完,另一个线程就参与进来执行,导致共享数据的错误。...

    利用synchronized关键字解决线程安全问题

         

     1.线程安全问题

               当多条语句在操作同一个线程共享数据时,一个线程对多条语句操作的时候
         还没有执行完,另一个线程就参与进来执行,导致共享数据的错误。
     

    2.解决方案
           方法一: synchronized(object)
           {  
                                      同步代码块
           }

           方法二:把操作共享数据的代码封装成一个函数,并用synchronized来修饰该方法,

                            该方法同步其实就是在同步的Object是this

                              synchronized(this){ 同步代码块  } 等价于 同步函数

                           
     3.通过代码块的原理 
               通过标志位来实现的,线程进去flag为false,其它线程就不能执行同步代码块了,
         当该线程执行完了之后,标志位就设置为true了。
          就好像给同步代码块加锁了一样,只有线程持有Object的锁以后才能执行该段代码。
          原理就好像火车上的厕所一样  

     4.加锁的技巧
             加在对共享数据进行操作的代码块中 

    5.加锁的优缺点
              优点:解决线程安全问题
              缺点:比较消耗资源

       注:代码块加锁的时候尽量加在操作共享资源的代码块中,没有操作同步同步资源的代

                码 就别加了,否则效率将会大打折扣 。        

      下面是一个多个线程操作共享资源的一个demo:    

    Java代码  收藏代码
    1. public class SailTicket {  
    2.  public static void main(String[] args) {  
    3.   Ticket ticket = new Ticket();  
    4.   Thread t1 = new Thread(ticket);  
    5.   Thread t2 = new Thread(ticket);  
    6.   Thread t3 = new Thread(ticket);  
    7.   t1.start();  
    8.   t2.start();  
    9.   t3.start();  
    10.  }  
    11.   
    12. }  
    13.   
    14. class Ticket implements Runnable {  
    15.  private  int num = 1000;  
    16.   
    17.  @Override  
    18.  public void run() {  
    19.   synchronized (this) {  
    20.    while (num > 0) {  
    21.     try {  
    22.      Thread.sleep(10);  
    23.      System.out.println(Thread.currentThread().getName() + "...."  
    24.        + num--);  
    25.     } catch (InterruptedException e) {  
    26.      // TODO Auto-generated catch block  
    27.      e.printStackTrace();  
    28.     }  
    29.    }  
    30.   }  
    31.  }  
    32.   
    33. }
    展开全文
  • volatile关键字线程安全问题总结

    千次阅读 2018-03-30 11:34:49
    简介volatile关键字保证了在多线程环境下,被修饰的变量在别修改后会马上同步到主存,这样该线程对这个变量的修改就是对所有其他线程可见的,其他线程能够马上读到这个修改后值. Thread的本地内存每个Thread都拥有自己...

    简介

    volatile关键字保证了在多线程环境下,被修饰的变量在别修改后会马上同步到主存,这样该线程对这个变量的修改就是对所有其他线程可见的,其他线程能够马上读到这个修改后值.

     

    Thread的本地内存

    • 每个Thread都拥有自己的线程存储空间
    • Thread何时同步本地存储空间的数据到主存是不确定的

    例子

    借用Google JEREMY MANSON 的解释,上图表示两个线程并发执行,而且代码顺序上为Thread1->Thread2

    1. 不用 volatile

    假如ready字段不使用volatile,那么Thread 1对ready做出的修改对于Thread2来说未必是可见的,是否可见是不确定的.假如此时thread1 ready泄露了(leak through)了,那么Thread 2可以看见ready为true,但是有可能answer的改变并没有泄露,则thread2有可能会输出 0 (answer=42对thread2并不可见)

    2. 使用 volatile

    使用volatile以后,做了如下事情

    • 每次修改volatile变量都会同步到主存中
    • 每次读取volatile变量的值都强制从主存读取最新的值(强制JVM不可优化volatile变量,如JVM优化后变量读取会使用cpu缓存而不从主存中读取)
    • 线程 A 中写入 volatile 变量之前可见的变量, 在线程 B 中读取该 volatile 变量以后, 线程 B 对其他在 A 中的可见变量也可见. 换句话说, 写 volatile 类似于退出同步块, 而读取 volatile 类似于进入同步块

    所以如果使用了volatile,那么Thread2读取到的值为read=>true,answer=>42,当然使用volatile的同时也会增加性能开销

     

    注意

    volatile并不能保证非源自性操作的多线程安全问题得到解决,volatile解决的是多线程间共享变量的可见性问题,而例如多线程的i++,++i,依然还是会存在多线程问题,它是无法解决了.如下:使用一个线程i++,另一个i--,最终得到的结果不为0

    复制代码
    public class VolatileTest {
    
        private static volatile int count = 0;
        private static final int times = Integer.MAX_VALUE;
    
        public static void main(String[] args) {
    
            long curTime = System.nanoTime();
    
            Thread decThread = new DecThread();
            decThread.start();
    
            // 使用run()来运行结果为0,原因是单线程执行不会有线程安全问题
            // new DecThread().run();
    
            System.out.println("Start thread: " + Thread.currentThread() + " i++");
    
            for (int i = 0; i < times; i++) {
                count++;
            }
    
            System.out.println("End thread: " + Thread.currentThread() + " i--");
    
            // 等待decThread结束
            while (decThread.isAlive());
    
            long duration = System.nanoTime() - curTime;
            System.out.println("Result: " + count);
            System.out.format("Duration: %.2fs\n", duration / 1.0e9);
        }
    
        private static class DecThread extends Thread {
    
            @Override
            public void run() {
                System.out.println("Start thread: " + Thread.currentThread() + " i--");
                for (int i = 0; i < times; i++) {
                    count--;
                }
                System.out.println("End thread: " + Thread.currentThread() + " i--");
            }
        }
    }
    复制代码

    最后输出的结果是

    Start thread: Thread[main,5,main] i++
    Start thread: Thread[Thread-0,5,main] i--
    End thread: Thread[main,5,main] i--
    End thread: Thread[Thread-0,5,main] i--
    Result: -460370604
    Duration: 67.37s

    原因是i++和++i并非原子操作,我们若查看字节码,会发现

    void f1() { i++; }

    的字节码如下

    复制代码
    void f1();
    Code:
    0: aload_0
    1: dup
    2: getfield #2; //Field i:I
    5: iconst_1
    6: iadd
    7: putfield #2; //Field i:I
    10: return
    复制代码

    可见i++执行了多部操作, 从变量i中读取读取i的值 -> 值+1 -> 将+1后的值写回i中,这样在多线程的时候执行情况就类似如下了

    Thread1             Thread2
    r1 = i;             r3 = i;               
    r2 = r1 + 1;        r4 = r3 + 1;
    i = r2;             i = r4;

    这样会造成的问题就是 r1, r3读到的值都是 0, 最后两个线程都将 1 写入 i, 最后 i 等于 1, 但是却进行了两次自增操作

    可知加了volatile和没加volatile都无法解决非原子操作的线程同步问题

     

    线程同步问题的解决

    Java提供了java.util.concurrent.atomic 包来提供线程安全的基本类型包装类,例子如下

    复制代码
    package com.qunar.atomicinteger;
    
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * @author zhenwei.liu created on 2013 13-9-2 下午10:18
     * @version $Id$
     */
    public class SafeTest {
    
        private static AtomicInteger count = new AtomicInteger(0);
        private static final int times = Integer.MAX_VALUE;
    
        public static void main(String[] args) {
    
            long curTime = System.nanoTime();
    
            Thread decThread = new DecThread();
            decThread.start();
    
            // 使用run()来运行结果为0,原因是单线程执行不会有线程安全问题
            // new DecThread().run();
    
            System.out.println("Start thread: " + Thread.currentThread() + " i++");
    
            for (int i = 0; i < times; i++) {
                count.incrementAndGet();
            }
    
            // 等待decThread结束
            while (decThread.isAlive());
    
            long duration = System.nanoTime() - curTime;
            System.out.println("Result: " + count);
            System.out.format("Duration: %.2f\n", duration / 1.0e9);
        }
    
        private static class DecThread extends Thread {
    
            @Override
            public void run() {
                System.out.println("Start thread: " + Thread.currentThread() + " i--");
                for (int i = 0; i < times; i++) {
                    count.decrementAndGet();
                }
                System.out.println("End thread: " + Thread.currentThread() + " i--");
            }
        }
    }
    复制代码

    输出

    Start thread: Thread[main,5,main] i++
    Start thread: Thread[Thread-0,5,main] i--
    End thread: Thread[Thread-0,5,main] i--
    Result: 0
    Duration: 105.15

     

     

    结论

    1. volatile解决了线程间共享变量的可见性问题
    2. 使用volatile会增加性能开销
    3. volatile并不能解决线程同步问题
    4. 解决i++或者++i这样的线程同步问题需要使用synchronized或者AtomicXX系列的包装类,同时也会增加性能开销

    java中volatile关键字的含义--volatile并不能做到线程安全

    Java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉。

    Java语言是支持多线程的,为了解决线程并发的问题,在语言内部引入了 同步块 和 volatile 关键字机制。

    synchronized 

    同步块大家都比较熟悉,通过 synchronized 关键字来实现,所有加上synchronized 和 块语句,在多线程访问的时候,同一时刻只能有一个线程能够用synchronized 修饰的方法 或者 代码块。

     

    volatile

    用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最后的值。volatile很容易被误用,用来进行原子性操作。

     

    下面看一个例子,我们实现一个计数器,每次线程启动的时候,会调用计数器inc方法,对计数器进行加一

     

    执行环境——jdk版本:jdk1.6.0_31 ,内存 :3G   cpu:x86 2.4G

    2
    4
    6
    8
    10
    12
    14
    16
    18
    20
    22
    24
    26
    28
    30
    32
    public class Counter {
     
        public static int count = 0;
     
        public static void inc() {
     
            //这里延迟1毫秒,使得结果明显
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
            }
     
            count++;
        }
     
        public static void main(String[] args) {
     
            //同时启动1000个线程,去进行i++计算,看看实际结果
     
            for (int i = 0; i < 1000; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Counter.inc();
                    }
                }).start();
            }
     
            //这里每次运行的值都有可能不同,可能为1000
            System.out.println("运行结果:Counter.count=" + Counter.count);
        }
    }
      
    运行结果:Counter.count=995
    实际运算结果每次可能都不一样,本机的结果为:运行结果:Counter.count=995,可以看出,在多线程的环境下,Counter.count并没有期望结果是1000
      
    很多人以为,这个是多线程并发问题,只需要在变量count之前加上volatile就可以避免这个问题,那我们在修改代码看看,看看结果是不是符合我们的期望
    2
    4
    6
    8
    10
    12
    14
    16
    18
    20
    22
    24
    26
    28
    30
    32
    public class Counter {
     
        public volatile static int count = 0;
     
        public static void inc() {
     
            //这里延迟1毫秒,使得结果明显
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
            }
     
            count++;
        }
     
        public static void main(String[] args) {
     
            //同时启动1000个线程,去进行i++计算,看看实际结果
     
            for (int i = 0; i < 1000; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Counter.inc();
                    }
                }).start();
            }
     
            //这里每次运行的值都有可能不同,可能为1000
            System.out.println("运行结果:Counter.count=" + Counter.count);
        }
    }

    运行结果:Counter.count=992

    运行结果还是没有我们期望的1000,下面我们分析一下原因

     

    在 java 垃圾回收整理一文中,描述了jvm运行时刻内存的分配。其中有一个内存区域是jvm虚拟机栈,每一个线程运行时都有一个线程栈,

    线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存

    变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,

    在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。下面一幅图

    描述这写交互

    read and load 从主存复制变量到当前工作内存

    use and assign  执行代码,改变共享变量值 
    store and write 用工作内存数据刷新主存相关内容

    其中use and assign 可以多次出现

    但是这一些操作并不是原子性,也就是 在read load之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,所以计算出来的结果会和预期不一样

    对于volatile修饰的变量,jvm虚拟机只是保证从主内存加载到线程工作内存的值是最新的

    例如假如线程1,线程2 在进行read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值

    在线程1堆count进行修改之后,会write到主内存中,主内存中的count变量就会变为6

    线程2由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6

    导致两个线程及时用volatile关键字修改之后,还是会存在并发的情况。

    展开全文
  • 线程安全: 当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步及在调用代码代码不必作其他的协调,这个类的行为仍然是正确的,那么称这个类是线程安全的。 线程...

    线程安全:

    当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步及在调用代码代码不必作其他的协调,这个类的行为仍然是正确的,那么称这个类是线程安全的。

    线程共享:

    当某个资源需要被多个线程同时访问时,该资源就是一个线程共享的资源,这时就需要注意这个变量是否线程安全,会不会因为多个线程的访问而导致值发送不符合预期的改变。

    synchronized(保证同一时刻只有获得锁的线程才能访问,其他线程被阻塞)

    Java提供了强制性的内置锁机制:synchronized块。一个synchronized块有两个部分:锁对象的引用,以及这个锁保护的代码块。执行线程进入synchronized块之前会自动获得锁,无论通过正常控制路径退出还是从块中抛出异常,线程都在放弃对synchronized块的控制时自动释放锁。获得内部锁的唯一途径是:进入这个内部锁保护的同步块或方法。 
    内部锁在Java中扮演了互斥锁的角色,意味着至多只有一个线程可以拥有锁。

    volatile(保证线程的可见性)

    violate关键字对于自增操作无法保证线程安全,自增操作本身并不是原子操作;例如count = count +1;violatile并不能保证原子性

    我们知道,volatile关键字不能用来做计数器,因为它只能保证两个特性:
    1.可见性(对volatile变量所有的写操作都能立即反应到其他线程中,换句话说,volatile变量在各个线程中是一致的(得益于java内存模型—"先行发生原则",任何线程对被violatile关键字修饰的变量的操作,都是直接反应到主存上,其他线程读取时也是从贮存读取,能保证一定能读到最新值))
    2.禁止指令重排序

    ThreadLocal(各个线程维护自己的一个副本,互相隔离)

    ThreadLocal不是一个线程的本地实现版本,它不是一个Thread,而是线程布局变量,为每一个使用该变量的线程都提供了一个变量值的副本。 
      从线程的角度看,每一个线程都保持一个对其线程局部变量副本的隐式引用,只要线程时活动的并且ThreadLocal实例是可访问的。 
      JVM为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境出现的并发问题提供了一种隔离机制。这种隔离机制与同步机制不同,同步机制才用了“以时间换空间”的方式,而ThreadLocal才用了“以空间换时间”的方式,前者仅提供一份变量,让不同的线程排队访问,后者则为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

     

    AtomicInteger(实现线程共享变量)

    可用做多线程的计数器,比如有一个线程共享变量,要保证多个线程对这个计数器进行累加。

    原帖:https://blog.csdn.net/fanrenxiang/article/details/80623884

    我们知道,阻塞同步和非阻塞同步都是实现线程安全的两个保障手段,

    阻塞同步:Sychonize是阻塞同步的一种实现

    非阻塞同步:

    对于阻塞同步而言主要解决了阻塞同步中线程阻塞和唤醒带来的性能问题,那什么叫做非阻塞同步呢?在并发环境下,某个线程对共享变量先进行操作,如果没有其他线程争用共享数据那操作就成功;如果存在数据的争用冲突,那就才去补偿措施,比如不断的重试机制,直到成功为止,因为这种乐观的并发策略不需要把线程挂起,也就把这种同步操作称为非阻塞同步(操作和冲突检测具备原子性)。在硬件指令集的发展驱动下,使得 "操作和冲突检测" 这种看起来需要多次操作的行为只需要一条处理器指令便可以完成,这些指令中就包括非常著名的CAS指令(Compare-And-Swap比较并交换),乐观锁的一种经典实现方式就是CAS,另一种是版本号

    展开全文
  • * 多线程主要的两个点:安全和效率 *volatile关键字: * 作用是保证共享变量的有序性和可见性 * 对于有序性来说 具体指禁止指令冲排序(CPU和JVM都有重排序) * 用volatile经常用来保证共享数据的读取操作安全 * */ ...
  • 不使用volatile,开100个进程自增: ... public class ThreadTest3 { public static int countInt; public volatile static int ...所以volatile不是线程安全的,同步锁不能省。
  • public class TestThread5 { static class Counter2 { public int count = 0;... //synchronized 关键字进行加锁操作 //synchronized可以对某个对象进行加锁 //进入increase方法会尝试加锁 方法执行完毕会解锁
  • volatile关键字线程

    2019-07-22 10:23:46
    volatile关键字是多线程中很重要的知识点,volatile关键字可以保证可见性,不能保证原子性,重点不能保证原子性,网上还有说什么保证弱原子性,还来个弱原子性,扯犊子,不够乱还来个弱原子性。volatile没有原子性,...
  • volatile关键字能否保证线程安全

    千次阅读 2019-12-17 13:53:47
    单纯使用 volatile 关键字是不能保证线程安全的 volatile 只提供了一种弱的同步机制,用来确保将变量的更新操作通知到其他线程 volatile 语义是禁用 CPU 缓存,直接从主内存读、写变量。表现为:更新 volatile ...
  • 我们都知道,Java是一种夸平台的语言,因此Java这种语言在处理线程安全问题的时候,会有自己的处理机制,例如volatile关键字,synchronized关键字,并且这种机制适用于各种平台。 Java内存模型规定所有的变量都是...
  • 文章目录1:线程安全重要性2:关键字synchronized 1:线程安全重要性 并行程序开发的一大关注重点就是线程安全。一 般来说, 程序并行化是为了获得更高的执行效率,但前提是,高效率不能以牺牲正确性为代价。如果...
  • 在印象中,volatile修改的变量是线程安全的,我一直这么认为,殊不知还有条件的: 1. 运算结果并不依赖变量的当前值,或者能够确保只有单一线程修改变量的值. 2. 变量不需要其他的状态变理共同参与不变约束。 ...
  • 我并不想具体介绍什么是volatile,我写这篇博客目的是说明白为什么volatile保证不了线程安全。想要线程安全必须保证原子性,可见性,有序性。而volatile只能保证可见性和有序性    ...
  • 想要解决线程安全问题,首先要知道为什么会造成线程不安全? 在单线程中,我们从来没有提到个线程安全问题,线程安全问题是只出现在多线程中的一个问题。因为多线程情况下有共享数据,每个线程都共享这些数据并对...
  • final 关键字与安全发布 多线程中篇(十三) final的通常理解 在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量) 大家应该都知道final表示最终的、最后的含义,也就是不能在...
  • 1、基本概念 Java 内存模型中的可见性、原子性和有序性。 可见性:  可见性是一种复杂的属性,因为可见性... 可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结...
  • 要想解决“线程同步”的问题,最简单的方法就是使用synchronized关键字来使run方法同步,代码如下: public synchronized void run() { } 从上面的代码可以看出,只要在void和public之间加上synchronized...
  • 线程安全变量及关键字

    千次阅读 2018-02-01 11:27:15
    1、synchronized关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized...
  • volatile修饰变量,强制线程访问共享内存里的变量,但是volatile是非线程安全的,多线程访问volatile不会发生阻塞。 synchronized修饰方法或代码块,实现线程执行的同步,实现私有内存和共享内存的变量同步,保证...
  • 多线程之线程安全关键字synchronized

    千次阅读 2018-08-03 11:08:27
    synchronized关键字,是多线程编程时保证线程安全使用非常广泛的java知识。下面我们学习下synchronized的相关知识: 实现原理  synchronized的实现原理是基于内存中的lock原则。内存模型中的变量读写有八个步骤也...
  • 讨论之前还是先就内存模型及相关概念进行简单的介绍 内存模型: 我们都知道CPU内存之间有一个高速缓存,当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以...
  • 文章目录volatile关键字变量可见性禁止重排序volatile关键字是否可以保证线程安全 volatile关键字 Java 语言提供了一种稍弱的同步机制,即 volatile 变量,用来确保将变量的更新操作通知到其他线程。volatile 变量...
  • 内存模型的相关概念 并发编程中的三个概念:可见性、原子性、有序性  Java内存模型:主内存、线程的工作内存 深入剖析volatile关键字:lock前缀指令——内存屏障,保证有序性,不保证原子性——无法替代...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 195,584
精华内容 78,233
关键字:

哪些关键字与线程安全相关