-
2022-04-06 20:15:16
一 点睛
并发编程的三个重要特性是原子性、有序性和可见性。
二 原子性
指在一次的操作或者多次操作中,要么所有的操作全部得到执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行。
银行转账是原子性最好的例子,转账要么成功,要么失败。不可能出现其他状态。否则就是代码写得有问题,这种情况是客户不允许的。
再举一个例子
Object o = new Object();
引用类型 o 占用四个字节(32位),假设这样的赋值语句不能够保证原子性的话,那么会出现赋值出现错误的数据,所以它也是原子性的。
注意
两个原子性的操作结合在一起未必还是原子的,比如 i++,它包括 get i,i+1,set i=x,三个过程,三个过程都是原子性操作,但不代表 i++ 就是原子性操作。
volatile 关键字不保证数据的原子性,synchronized 关键字保证,从 JDK 1.5 版本开始,其提供的原子类型变量也可以保证原子性。
三 可见性
可见性是指,当一个线程对共享变量进行了修改,那么另外的线程可以立即看到修改后的新值。
new Thread(() -> { int localValue = init_value; while (localValue < MAX) { if (init_value != localValue) { System.out.printf("The init_value is update to [%d]\n", init_value); // 对 localValue 进行重新赋值 localValue = init_value; } } }, "Reader").start();
当 init_value 不使用 volatile 关键字时,Reader 线程会将 init_value 从主内存缓存到 CPU Cache 中,也就是从主内存缓存到线程的本地内存中,Updater 线程对 init_value 的修改对 Reader 线程是不可见的。加上 volatile 关键字,init_value 对其它线程就是可见的。
四 有序性
所谓有序性是指程序代码在执行过程中先后顺序,由于 Java 在编译器以及运行期的优化,导致了代码的执行顺序未必就是开发者编写代码的顺序,比如
int x = 10; int y = 0; x++; y = 20
上面这段代码定义了两个 int 类型的变量 x 和 y,对 x 进行自增操作,对 y 进行赋值操作,从编写程序的角度看上面代码肯定是顺序执行下去的,但是 JVM 真正地运行这段代码的时候未必会是这样的顺序,比如 y = 20 语句有可能会在 x++ 语句的前面执行,这种情况就是通常所说的指令重排。
一般来说,处理器为了提高程序的运行效率,可能会对输入的代码指令做一定的优化,它不会百分百的保证代码的执行顺序严格按照编写代码中的顺序进行,但是它会保证程序的最终运算结果是编码时所期望的那样,比如上文中的 x++ 和 y = 20,不管它们的执行顺序如何,执行完上面四行代码之后得到的结果肯定都是 x =11,y = 20。
当然对指令的重排序要严格遵守指令之间的数据依赖关系,并不是可以任意进行重排序的,比如下面的代码片段。
int x = 10; int y = 0; x++; y=x+1;
对于这段代码有可能它的执行顺序就是代码本身的顺序,有可能发生了重排序导致 int y=0 优于 int x =10 执行,但是绝对不能出现 y= x+1 优于 x++ 执行的执行情况,如果一个指令 x 在执行的过程中需要用到指令 y 的执行结果,那么处理器会保证指令 y 在指令 x 之前执行,这就好比 y = x+1 执行前肯定要先执行 x++ 一样。
在单线程情况下,无论怎样的重排序最终都会保证程序的执行结果和代码顺序执行结果完全一致的,但是在多线程情况下,如果有序性得不到保证,那么很有可能就会出现非常大的问题,比如下面的代码片段
private boolean initialized = false; private Context context; public Context load(){ if(!initialized){ context = loadContext(); initialized = true; } return context; }
上面代码使用 boolean 变量 initialized 来控制 context 是否已经被加载过,在单线程下,无论怎样的重排序,最终返回给使用者的 context 都是可用的。如果在多线程的情况下发生了重排序,比如 context = loadContext 的执行顺序被重排序到 initialized = true; 的后面,那么这就是灾难性的。比如第一个线程首先判断 initialized = false,然后准备执行 loadContext 方法,但由于重排序,将 initialized 设置为 true,此时如果另外一个线程也执行 load 方法,发现此时 initialized 已经为 true 了,则返回一个还未被加载的 context,那么在程序的运行过程中势必会出现错误。
更多相关内容 -
并发编程三大特性
2022-04-11 15:03:07并发编程三大特性,代码解析并发编程三大特性:原子性,可见性,有序性
1.什么是原子性?
原子性是指在一个操作中,所有的子操作都是一个整体,要么同时全部执行,要么同时不执行,且在执行过程中,不能被挂起,直到执行完。可能这样解释很懵,那么看下面例子就全明白了。
int等不大于32位的基本类型的操作都是具有原子性,对于long和double变量,把它们作为2个原子性的32位值来对待,而不是一个原子性的64位值, 这样将一个long型的值保存到内存的时候,可能是2次32位的写操作, 2个竞争线程想写不同的值到内存的时候,可能导致内存中的值是不正确的结果。
nt i=1,这是原子操作;
double i=1 这不是一个,因为double是64位数据类型。
x=y也不是原子操作,因为执行这个操作需要三步,要先从内存中拿取y的值,然后对x值进行修改,然后把x的值写回内存中。
2.可见性
可见性就是指当一个线程修改了线程共享变量的值,其它线程能够立即得知这个修改。
讲可见性,就必须先了解JMM模型(java内存模型),在JMM中,主存中有A=2,子线程复制一份副本放在自己的工作内存中,如果要对A进行操作修改,那么要先修改自己工作内存的A,然后再将数据同步到主存中。
3.有序性
指令重排是指在程序执行过程中,为了提高性能, 编译器和CPU可能会对指令进行重新排序,在单线程中这完全没问题,还能提高性能,而在多线程中却有很多问题,会出现重排后结果不一致问题。为了解决这一问题就会防止指令重排,采用内存屏障来确保指令不会被重排序,下面是内存屏障的原理,了解即可。
那么java是通过什么来预防三大特性所带来的问题的呢?
原子性
当我们执行以下操作时,创建10个线程,每个线程循环10000此次,每次循环对value+1,按照如此逻辑,打印结果应该时100000,而运行以下代码,每次结果都会不一样,因为他不能保证原子性,当线程1对value进行加1操作,还没回写主存中,时间片就轮转到线程2对value进行加1操作,但是线程2对数据进行了回写主存,那么此时主存的value的值为1,再次时间片轮转到线程1,而线程1就差数据回写没完成,那么执行回写操作,将value的值又改为了1,此时线程1修改的值被更新丢失(类似数据库的丢失更新)。
public class DefaultTest { private static int value = 0; public static void main(String[] args) throws InterruptedException { //创建10个线程 for (int i = 0; i < 10; i++) { new Thread(() -> { //每个线程对value加10000 for (int j = 0; j < 10000; j++) { value++; } }).start(); } Thread.sleep(2000);; System.out.println(value); } }
java为了解决上面并发带来的问题引入了java.util.concurrent.atomic包,此包下的所有类都是线程安全的,保证了原子性。以下用AtomicInteger来替代int
public class AtomicIntegerTest { private static AtomicInteger value = new AtomicInteger(0); public static void main(String[] args) throws InterruptedException { for (int i = 0; i < 10; i++) { new Thread(() -> { for (int j = 0; j < 10000; j++) { value.addAndGet(1); } }).start(); } Thread.sleep(2000); System.out.println(value); } }
也可以用synchronized来保证原子性,可见性,有序性,这是重量级锁。对于不同情况采用不同应对方式,这里就不讲synchronized了。
可见性
当我们执行以下代码,创建两个线程,线程t1将线程t2的stop改为true,按照逻辑,此时线程t2应该会退出循环,然而并没有这样子。因为线程t1对线程t2的值进行修改后,线程t2时没有感知到,这就是数据不可见问题,那么java对这种问题,引入了volatile关键字。
public class u_volatile { public static void main(String[] args) throws InterruptedException { Task task = new Task(); Thread t1 = new Thread(task, "线程t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); System.out.println("开始通知线程停止"); task.stop = true; } catch (InterruptedException e) { e.printStackTrace(); } } }); t1.start(); t2.start(); Thread.sleep(1000); } } class Task implements Runnable { boolean stop = false; //属性 int i = 0; @Override public void run() { long s = System.currentTimeMillis(); while (!stop) { i++; } System.out.println("线程退出" + (System.currentTimeMillis() - s)); } }
volatile关键字只能修改全局变量,对属性stop加入volatile修饰,就能保住可见性了
public class u_volatile { public static void main(String[] args) throws InterruptedException { Task task = new Task(); Thread t1 = new Thread(task, "线程t1"); Thread t2 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); System.out.println("开始通知线程停止"); task.stop = true; } catch (InterruptedException e) { e.printStackTrace(); } } }); t1.start(); t2.start(); Thread.sleep(1000); } } class Task implements Runnable { volatile boolean stop = false; //属性 int i = 0; @Override public void run() { long s = System.currentTimeMillis(); while (!stop) { i++; } System.out.println("线程退出" + (System.currentTimeMillis() - s)); } }
总结
在java中通过atomic来保证原子性;volatile保证可见性、禁止指令重排,但是不保证原子性;synchronized保证了有序,可见,原子性,volatile不会造成线程阻塞,synchronized可能会造成线程的阻塞,所以后面才有锁优化和无锁编程。
-
【Java】并发编程三大特性
2022-03-15 12:19:18Java并发编程三大特性:可见性、有序性、原子性。目录
本文为个人学习笔记,一切内容仅供参考。
一、可见性(visibility)
1.用 volatile 保障可见性
可见性:
-
被 volatile 修饰的内存,对所有线程可见。
-
线程每一次使用被 volatile 修饰的变量时,都需要去内存中重新读取一遍该变量的值。
代码:
public class Main { private static volatile boolean running = true; private static void m() { System.out.println("m start!"); while (running) {} System.out.println("m end!"); } public static void main(String[] args) throws InterruptedException { new Thread(() -> { Main.m(); }, "t1").start(); TimeUnit.SECONDS.sleep(1); running = false; } }
在上例中,如果 running 不用 volatile 修饰,则在 main 线程中修改其值后,并不会影响 t1 线程中 copy 到的 running 的值,线程会一直循环运行。
volatile 修饰引用类型(包括数组)只能保证引用本身的可见性,不能保证内部字段(成员变量)的可见性。
2.某些语句会触发可见性机制
某些语句内部带有 synchronized,可以触发可见性,如:println()。
这些语句会触发内存缓存同步刷新:可以将线程本地的缓存和主内存之间的数据进行刷新与同步。
二、有序性(ordering)
1.语句乱序执行的现象
举例:
-
提高 CPU 的执行效率:CPU 的处理速度远大于寄存器,当指令 1 去读取数据时,需要等待很长的时间,这个时候 CPU 就可能会先去执行指令 2 做计算。
-
前提:前后两条语句没有依赖关系,如:x = 1; x++; 就不会发生乱序。
-
as-if-serial:不论在计算机底层实际是谁先执行,都不会影响单线程的最终一致性。
如果在多线程环境下发生乱序,则可能会造成一些不好的影响。
代码:
public class Main { private static boolean ready = false; private static int number; private static class MyThread extends Thread { @Override public void run() { while (!ready) { Thread.yield(); } System.out.println(number); } } public static void main(String[] args) { Thread t = new MyThread(); t.start(); // 下面这两条语句没有依赖关系,可能会发生乱序执行 number = 42; ready = true; } }
分析:
-
在类初始化时,会先给静态变量赋初值,即 number = 0。
-
若发生了语句的乱序执行,即若 ready = true 先执行,则此时的 number 还没被初始化就会被打印。
-
最后打印的 number 可能是 42 或 0。
2.this 对象逸出的现象
代码:
public class Main { private int num = 8; public Main() { new Thread(() -> { System.out.println(num); }).start(); } public static void main(String[] args) { new Main(); } }
分析:
-
在对象初始化的过程中,可能会先将对象(this)与内存空间建立关联,再调用构造方法对成员变量进行初始化。
-
在构造方法还未执行完时,可能会先启动线程,然后打印输出还未初始化的变量值 num = 0。
-
对象的创建过程详见:。
解决方法:不要在构造方法中启动线程(但是可以创建线程),可以单独写一个方法用于启动线程。
3.阻止乱序的方式:内存屏障
内存屏障的含义:是一种特殊的指令,其前面的指令必须执行完,后面的才能执行。
JVM 内存屏障:
volatile 底层使用内存屏障实现:
-
volatile 读 = 读数据 + LoadLoadBarrier + LoadStoreBarrier
-
volatile 写 = LoadStoreBarrier + 写数据 + StoreLoadBarrier
volatile:
-
使用 volatile 可以保证可见性。
-
使用 volatile 可以保证有序性。
三、原子性(atomicity)
1.什么是原子性操作?
原子性操作:不能被其它线程打断,不能与其它线程一起并发执行的操作。
代码:
public class Main { private static long n = 0L; public static void main(String[] args) throws InterruptedException { Thread[] t = new Thread[100]; CountDownLatch latch = new CountDownLatch(t.length); for (int i = 0; i < t.length; i++) { t[i] = new Thread(() -> { for (int j = 0; j < 10000; j++) { n++; } latch.countDown(); }); } for (int k = 0; k < t.length; k++) { t[k].start(); } // 阻塞,等待所有线程执行完毕后再打印n的值 latch.await(); System.out.println(n); } }
理论结果为:1 000 000。
实际结果为:122 507。
原因:多个线程访问同一个共享数据时会产生竞争,可能会造成数据的不一致(并发访问之下产生的不期望出现的结果)。
如何解决这个问题?
用 synchronized 上锁保证原子性:
-
可以将 n++ 这条语句用 synchronized(Main.class) 上锁来实现线程同步(保障数据一致性)。
-
上锁的本质就是把并发编程序列化。
-
用 synchronized 也可以保证可见性,上例中每次 n++ 完以后,一定会与主内存做同步。
-
用 synchronized 不能保证有序性,其内部的语句仍然可能出现乱序重排的现象。
2.如何保证操作的原子性?
(1)悲观锁
含义:悲观地认为这个操作会被别的线程打断。
(2)乐观锁
含义:乐观地认为这个操作不会被别的线程打断,又叫做无锁、自旋锁(使用 CAS 实现)。
CAS(compare and swap / exchange / set)
CAS 原理:
-
当这里的值 E、V 为普通的数据类型时,不用考虑 ABA 问题;但如果是引用类型,则需要加 version 来解决 ABA 问题。
-
version 可以由带时间戳、数字或布尔类型变量等方式来实现。
-
CAS 自身必须保证原子性。
代码:
public class Main { // 设置初始值为0 AtomicInteger count = new AtomicInteger(0); static CountDownLatch latch = new CountDownLatch(100); void run() { for (int i = 0; i < 10000; i++) { // count++ count.incrementAndGet(); } latch.countDown(); System.out.println(Thread.currentThread().getName() + ":" + count); } public static void main(String[] args) throws InterruptedException { Main m = new Main(); List<Thread> t = new ArrayList<>(); for (int i = 0; i < 100; i++) { t.add(new Thread(m::run, "thread-" + i)); } // 启动所有线程 t.forEach((o) -> o.start()); latch.await(); System.out.println(m.count); // 1000000 } }
分析:
-
AtomicInteger 可以保证原子性,用于对 int 类型的数据进行原子性的访问,此时的访问是加了锁的。
-
AtomicXxx:通过 CAS 实现,一般会比直接用 synchronized 上锁效率更高。
-
incrementAndGet():实现线程安全的自增(原子性操作)。
-
其他保证原子性的类:AtomicLong、LongAdder(效率:LongAdder > AtomicLong > Sync)。
CAS 底层实现:
-
由 CPU 原语(指令级别)支持。
-
底层还是通过上锁实现的(汇编指令:lock cmpxchg),不允许其他 CPU(线程)打断当前对某块内存的 CAS 操作(单核 CPU 没必要上锁)。
(3)总结
基本概念补充:
critical section(临界区,即被锁住的代码):
-
若临界区的执行时间长,语句多,就叫锁的粒度比较粗。
-
若临界区的执行时间短,语句少,就叫锁的粒度比较细。可以使线程争用时间变短,从而提高效率。
悲观锁与乐观锁的效率对比:
悲观锁:
-
有一个等待队列,等待中的线程不消耗 CPU 的资源。
-
适用场景:临界区执行时间长,且等待线程很多。
乐观锁:
-
所有线程会一直循环地去访问数据,这些线程都是活着的,需要线程间的调度,会消耗 CPU 的资源。
-
适用场景:临界区执行时间短,且等待线程数少。
-
-
【第一篇】Java并发编程的三大特性:原子性、可见性、有序性
2022-02-24 10:46:55【重点】Java并发编程的三大特性原子性可见性有序性总结 Java并发编程中,往往涉及到三个性质:原子性、可见性。有序性。 原子性 熟悉数据库特性的我们都知道数据库也有原子性,数据库中的原子性是这样定义的: ...Java并发编程中,往往涉及到三个性质:原子性、可见性。有序性。
原子性
熟悉数据库特性的我们都知道数据库也有原子性,数据库中的原子性是这样定义的:
事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
其实Java并发编程中跟数据库的原子性也类似:即一个操作或者多个操作要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。也可理解为要么所有的指令都执行,要么都不执行。这样才能保证并发操作的安全性和一致性
下面我们来举一个比较经典的转账问题,该过程可以分为两步:
1、A账户减掉1000元;
2、B账户增加1000元。
如果上述两个步骤如果中途被打断会造成什么后果?A账户已经被扣掉1000了,但是B账户确没有收到1000,这种情况肯定要出错的。所以这2个操作必须要具备原子性才能保证不出现一些意外的问题。要想在多线程环境下保证线程的原子性,则可以通过锁、synchronized关键字来确保。volatile是无法保证复合操作的原子性。
可见性
可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
如下图所示:
对于可见性,Java提供了volatile关键字来保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。有序性
有序性指的是程序按照代码的先后顺序执行。
下面解释一下什么是指令重排,一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。
虽然重排序不会影响单个线程内程序执行的结果,但是会影响到线程并发执行的正确性。多线程环境中线程交替执行,由于编译器指令重排的存在,两个线程使用的变量能否保证一致性是无法确认的,结果无法预测。
volatile实现禁止指令重排优化底层原理:
由于编译器和处理器都能执行指令重排优化。如果在指令间插入一条Memory Barrier则会告诉编译器和CPU,不管什么指令都不能和这条Memory Barrier指令重排,也就是说通过插入内存屏障,就能禁止在内存屏障前后的指令执行重排优化。内存屏障另外一个作用就是强制刷出各种CPU的缓存数据,因此任何CPU上的线程都能读取到这些数据的最新版本。
volatile 可以在单例双重检查中实现可见性和禁止指令重排序,从而保证安全性。总结
要想并发程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确
-
01-并发编程之深入理解JMM&并发三大特性(一).pdf
2021-10-27 22:22:15并发编程之深入理解JMM&并发三大特性 -
多线程(六):并发编程的三大特性之可见性
2022-01-14 10:33:31并发编程的三大特性之可见性 -
并发中的三大特性详解
2020-05-21 22:57:04前言:Java并发编程的三大特性:原子性、可见性、有序性。要保证并发代码的安全性则必须满足这三大特性 原子性:一个或者多个操作,要么全部执行(执行的过程是不会被打断的)、要么全部不执行。 案例分析:... -
JAVA多线程基础--------并发编程三大特性(原子性、可见性、有序性)
2021-08-16 13:08:03并发编程三大特性(原子性、可见性、有序性) 并发编程三大特性的定义和由来 凡事有因才有果,有果必有因,并发编程的三大特性也如此,人们不会莫名其妙定义出并发编程的三大特性。接下来我们探讨下为什么会有并发... -
JAVA并发编程:三大特性-可见性、有序性
2018-11-27 00:33:01在JAVA并发编程,如果要保证程序的线程安全,就要保证代码的原子性、可见性、有序性。 昨天聊了原子性。今天来看下可见性、有序性。 什么是可见性? 当多个线程访问同一个变量时,一个线程修改了一个变量的值,其他... -
【并发编程】并发编程的三大特性
2020-05-21 20:55:02并发编程的书籍都会讲到并发编程的三大特性,这是并发...为什么会有并发编程特性? 线程切换导致了原子性问题 对于变量a来说,我们对其执行以下代码 a++; 此时需要分三步执行: (1)读取a的值 (2)将a的值加1 -
lock cmpxchg 指令
-
并发编程之深入理解JMM&并发三大特性(一)
2022-03-08 16:39:23并发编程之深入理解JMM&并发三大特性(一)前提知识并发并行并发三大特性原子性可见性有序...并发编程BUG的根源就是三大特性:原子性、可见性、有序性 原子性 原子性则是一个操作或者多个操作之间不可被打断,要么 -
0 new #2 <java/lang/Object> 3 dup 4 invokespecial #1 <java/lang/Object.<init>> 7 astore_1 8 return
-
并发三大特性——可见性
2021-10-28 22:14:12熟悉并发的童鞋们都知道,并发编程有三大特性,分别是可见性、有序性、原子性,今天我们从一个demo中分析可见性,以及我们如何保障可见性。 JMM模型 在我们分析可见性之前,我们需要了解一个概念,就是JMM模型,也... -
并发编程的三大特性
2020-05-12 08:08:15原子性 狭义上指的是CPU操作指令必须是原子操作 广义上指的是字节码指令是原子操作 保证原子性? 加锁(Synchronize、Lock) 有序性 可见性 -
【并发三大特性】
2022-01-29 14:49:15并发编程并发和并行 并发和并行 目标都是最大化CPU的使用率 并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行。所以无论从微观还是从宏观来看,二者都是一起执行的。 并发(concurrency):指在同一... -
Go并发编程实战+第2版_Lite.pdf_golang_
2021-09-29 02:44:10本书首先介绍了Go语言的优秀特性、安装设置方法、工程结构、标准命令和工具、语法基础、数据类型以及流程控制方法,接着阐述了与多进程编程和多线程编程有关的知识,然后重点介绍了goroutine、channel以及Go提供的... -
Java并发编程技术总结
2020-05-21 16:17:48Java并发编程技术总结,所含内容有并发特性、并发锁、线程池、并发场景解决方案等,对于性能思考和内容参考资料有一定说明 -
并发编程的三大特性——原子性,可见性,有序性
2020-03-15 21:39:41总结: 这就是并发编程的3种特性,而synchronized关键字在需要这3种特性时都可以作为一种解决方案,看起来很“万能”,但是越这样“万能”的并发控制,也是会伴随着一些性能的影响的(如线程越多... -
并发编程一:深入理解JMM和并发三大特性(上)
2022-02-03 17:15:39文章目录深入理解JMM和并发三大特性(上)前言并发编程三特性JMM内存模型可见性深入分析总结 深入理解JMM和并发三大特性(上) 前言 JMM属于整个Java并发编程中最难的部分也是最重要的部分(JAVA多线程通信模型——共享 ... -
并发编程 - 三大特性
2020-04-26 22:28:08多线程有三大特性 原子性、可见性、有序性 1.什么是原子性 即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 一个很经典的例子就是银行账户转账问题: 比如从账户A向账户B转... -
Go并发编程第二版(书签版全)
2019-01-18 17:14:57郝林第二版 Go并发编程书籍PDF版,带高清目录 第1章 初识Go语言 1 1.1 语言特性 1 1.2 安装和设置 2 1.3 工程结构 3 1.3.1 工作区 3 1.3.2 GOPATH 4 1.3.3 源码文件 5 1.3.4 代码包... -
并发编程的三个重要特征
2020-04-08 13:08:39并发编程的三个重要特征 原子性 : 一个操作或者多次操作,要么所有的操作全部都得到执行并且不会收到任何因素的干扰而中断,要么所有的操作都执行,要么都不执行。synchronized 可以保证代码片段的原子性。 可见性 ... -
3. 并发编程的3大特性
-
Go并发编程研讨课.pdf
2019-06-09 09:58:59只有深入了解并发原语的实现,全面了解它们的特性和限制场景,注意它们的局限和容易踩的坑,才能提高我们的并发编程的能力。通过了解和学习其他人的经验和贡献的项目和库,我们可以更好的扩展我们的视野,避免重复的... -
c++并发编程指南
2017-12-12 11:18:07c++11并发编程指南,用markdown写的,可以用来学习使用c++11新特性进行并发开发。