Java 知识点总结之Java 并发 API(一)

1、什么是CAS

答:(1)CAS是一种系统原语(所谓原语属于操作系统用语范畴。原语由若干条指令组成的,用于完成一定功能的一个过程。primitive or atomic action 是由若干个机器指令构成的完成某种特定功能的一段程序,具有不可分割性·即原语的执行必须是连续的,在执行过程中不允许被中断)。

(2)CAS是Compare And Set的缩写。CAS3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做

         直接使用字段偏移量取内存值

         compareAndSwapInt就是借助C来调用CPU底层指令实现的。

private volatile int value;
public final int get() {
        return value;
}
//实现++1
public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }
}

(3)CAS缺点:

         ABA问题:因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。

         循环时间长开销大:旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。

         只能保证一个共享变量的原子操作:当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作。

 补充:AtomicReference是通过"volatile"和"Unsafe提供的CAS函数实现"原子操作。

(01) value是volatile类型。这保证了:当某线程修改value的值时,其他线程看到的value值都是最新的value值,即修改之后的volatile的值。对象中的变量也需要volatile类型!

(02) 通过CAS设置value。这保证了:当某线程池通过CAS函数(如compareAndSet函数)设置value时,它的操作是原子的,即线程在操作value时不会被中断。

for (int i = 0; i < 100; i++) {
			new Thread() {
				public void run() {
					for (int j = 0; j < 10; j++) {
						Person p = personRef.get();
						p.setId(p.getId()+1);
						if (!personRef.compareAndSet(null, p)) {
							System.out.println(p.getId());
						}
					}
				}
			}.start();
		}

2、自旋锁的原理

答:(1)获取自旋锁时锁已经有保持者,那么获取锁操作将自旋在那里,直到该自旋锁的保持者释放了锁。简单而言就是:利用CAS原理,做无限for循环。

(2)自旋锁比较适用于锁使用者保持锁时间比较短的情况。正是由于自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。

(3)缺点:

递归死锁,递归程序决不能在持有自旋锁时调用它自己,也决不能在递归调用时试图获得相同的自旋锁。

        过多占用cpu资源,一般自旋锁实现会有一个参数限定最多持续尝试次数. 超出后, 自旋锁放弃当前time slice. 等下一次机会

3、UNSAFE的原理

答:Unsafe类提供了硬件级别的原子操作,主要提供了以下功能:

(1)      通过Unsafe类可以分配内存,可以释放内存;提供的3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存

(2)      可以定位对象某字段的内存位置,也可以修改对象的字段值,即使它是私有的;

(3)      挂起与恢复:Unsafe.park()

(4)      CAS操作

补充:unsafe涉及到的偏移量,其实就是类成员变量在内存中的偏移量

stateOffset = unsafe.objectFieldOffset (AbstractQueuedSynchronizer.class.getDeclaredField("state"));


4、AQS实现原理

答:

它维护了一个volatile intstate(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。这里volatile是核心关键词,具体volatile的语义,在此不述。state的访问方式有三种:

getState()、setState()、compareAndSetState()

AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。

定义同步器在实现时只需要实现共享资源state的获取与释放方式即可

         isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。

tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。

tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。

tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。

tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

protected final boolean tryReleaseShared(int releases) { //Semaphore
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }
		
		
		
		protected final boolean tryRelease(int releases) { //ReentreLock
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

5、ReentrantLock的Condition

答:Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set (wait-set)。

其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现,这里注意,Condition是被绑定到Lock上的,要创建一个Lock的Condition必须用newCondition()方法。

Condition的强大之处在于它可以为多个线程间建立不同的Condition

public class MyBlockingQueue {
	private final Lock lock = new ReentrantLock();
	private Condition addCondition = lock.newCondition();
	private Condition takeCondition = lock.newCondition();

	private Object[] data;
	private int count = 0;
	private int position = -1;

	public MyBlockingQueue(int size) {
		this.data = new Object[size];
	}

	public void add(Object obj) {
		lock.lock();
		try {
			while (count == this.data.length) {
				try {
					addCondition.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			this.data[++position] = obj;

			count++;
			takeCondition.signalAll();
		} finally {
			lock.unlock();
		}
	}

	public Object take() {
		lock.lock();
		try {
			while (count == 0) {
				try {
					takeCondition.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			Object obj = this.data[position--];
			count--;
			addCondition.signalAll();
			return obj;
		} finally {
			lock.unlock();
		}
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值