北京的做网站公司,免费企业网站建设单位,网站建设 数据库,asp.net做电商网站页面文章目录 一、CAS1.什么是CAS2.实现原子类3.实现自旋锁 一、CAS
1.什么是CAS
Compare and swap 比较并交换。 比较交换的是 内存 和 寄存器 比如此时有一个内存 : M。 还有两个寄存器A,B
CAS ( M , A , B ) :如果M和A的值相同的话#xff0c;就把M和B的值进行交换(交换的… 文章目录 一、CAS1.什么是CAS2.实现原子类3.实现自旋锁 一、CAS
1.什么是CAS
Compare and swap 比较并交换。 比较交换的是 内存 和 寄存器 比如此时有一个内存 : M。 还有两个寄存器A,B
CAS ( M , A , B ) :如果M和A的值相同的话就把M和B的值进行交换(交换的目的是为了把B赋值给M,M B)同时整个操作返回 true。如果M和A的值不同的话无事发生同时整个操作返回false。
//伪代码
boolean CAS(address, expectValue, swapValue) {if (address expectedValue) {address swapValue;return true;}return false;
}CAS其实是一个CPU指令一个CPU指令就可以完成上述比较交换的逻辑。单个的CPU指令是原子的。所以可以使用CAS来完成一些操作来代替“加锁”。为编写线程安全的代码提供了线的思路基于CAS实现线程安全的方式也叫“无锁编程”。
优点保证线程安全同时避免发生阻塞。
缺点代码可读性差并且只能适用在特定场景不如加锁普适。
CAS本质上是CPU提供的指令被操作系统封装后提供成为APi。又再次被JVM进行封装提供成API,最终由程序员调用方法实现操作。
2.实现原子类 之前int count的操作就不是原子的load,add,save 在Java标准库中提供了原子类例如AtomicInteger,基于CAS的方式对int进行封装就封装成原子操作了。 public static AtomicInteger count new AtomicInteger(0);//对int进行原子化包装//0作为构造方法的参数public static void main(String[] args) throws InterruptedException {Thread t1 new Thread(()-{for (int i 0; i 50000; i) {count.getAndIncrement();//count; 后置
// count.incrementAndGet();//count 前置
// count.getAndDecrement();//count--; 后置--
// count.decrementAndGet();//--count 前置--}});Thread t2 new Thread(()-{for (int i 0; i 50000; i) {count.getAndIncrement();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count);}在Java中有些操作是偏底层的操作在使用时有着更多的注意事项稍有不慎就容易写出问题。这些操作就同一放在unsafe这个类中 //伪代码
class AtomicInteger {private int value;//相当于内存public int getAndIncrement() {int oldValue value;//oldValue相当于寄存器 while ( CAS(value, oldValue, oldValue1) ! true) {//CAS(内存寄存器A,寄存器B)oldValue value;}return oldValue;}
}寄存器A先保存当前内存的值CAS来判断此时有没有被穿插执行。如果没有穿插执行内存的值等于寄存器A的值把寄存器BB执行1操作的值赋值给内存同时CAS返回的是true,循环不成立返回是是自增前的值。 如果被穿插执行了CAS中内存的值不等于寄存器A的值CAS直接返回false.此时循环成立将被穿插执行过的内存值重新赋值给寄存器A,也就是说要再读一遍内存。再次执行CAS。 线程不安全的本质是进行操作的过程中穿插执行。 1.加锁是通过阻塞的方式避免穿插。 2.CAS是通过重试的方式避免穿插。如果值和之前不相等了重新更新一下在进行操作。 3.实现自旋锁
//伪代码
public class SpinLock {private Thread owner null;//记录当前这个锁被哪个线程获取到。 null表示未加锁public void lock(){//加锁// 通过 CAS 看当前锁是否被某个线程持有. // 如果这个锁已经被别的线程持有, 那么就自旋等待. // 如果这个锁没有被别的线程持有, 那么就把 owner 设为当前尝试加锁的线程. while(!CAS(this.owner, null, Thread.currentThread())){}}public void unlock (){//解锁this.owner null;}
}while循环中CAS判断当前owner是不是未加锁状态。如果是未加锁的就把owner赋值成 Thread.currentThread())调用lock方法的线程引用进行加锁。如果已经被加锁了CAS返回false,取反之后while就会反复执行。一旦其他线程释放了这个锁owner变成null时因为while不停循环就会立即拿到当前这把锁。
缺点while循环在忙等的过程中不断消耗CPU资源。如果加锁的概率不大付出的CPU资源就是值得的。如果当前的锁冲突很激烈可能会拿不到此时自旋锁就不适用了。
点击移步博客主页欢迎光临~