java.util.concurrent下的包
volatile关键字
定义:当多个线程操作共享数据时,可以保证内存中的数据可见.
用法:对共享的数据添加volatile关键字.保证内存可见性.
解释:当两个线程共享一个数据时,每个线程会将共享数据放入到自己的内存中进行操作,完成后再对数据进行修改,假定A线程一直对共享数据读取,B线程对共享数据进行修改,当B线程将数据取出来还没有来得及将数据放回共享数据中时,A线程将还未改变的数据取出来进行操作,这样就会造成读取的结果和期望值不一致,给属性添加volatile关键字,这样多个线程是直接对线程进行操作而不是自己取出来操作完成后再对数据进行修改.
特点:与synchronized相比是一种轻量级的同步策略.
注意:
1.volatile不具备"互斥性".
2.volatile不能保证变量的"原子性".
CAS算法(Compare and Swap)
定义:CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
synchronized是独占锁,是一种悲观锁.
在java.util.concurrent包中,使用的乐观锁就是CAS机制.
concurrentHashMap
hashMap: 普通的Map实现类
hashTable:给整个表添加锁,当所有线程访问到hashTable的时候,由并行转化为串行和访问,效率很低.
concurrentHashMap:锁分段机制,并发访问
CountDownLatch
重要的两个方法:
countDown()
:倒数一次,将计数器减一.
await(long timeout,TimeUnit unit)
:等待倒数到0,如果没有到达0,会一直阻塞,第一个参数表示设置超时时间,第二个参数为超时时间单位.
如果计数到达零,则返回 true;如果在计数到达零之前超过了等待时间,则返回 false.
一个简单的countDownLatch例子
public static void main(String[] args) throws InterruptedException {
// 开始的倒数计时
CountDownLatch start = new CountDownLatch(1);
// 准备的倒数计时
CountDownLatch end = new CountDownLatch(10);
// 创建一个容量为10的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
final int num = i;
Runnable runnable = new Runnable() {
public void run() {
try {
// 开始倒计时暂停等待
start.await(10000,TimeUnit.SECONDS);
Thread.sleep((long) (Math.random() * 10000));
System.out.println("number of " + num + " over!");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 每次一个线程完成后,结束的计数器就减一
end.countDown();
}
}
};
// 线程池执行线程
threadPool.execute(runnable);
}
System.out.println("start....");
start.countDown();// 将开始的计数设为0,所有线程同时出发
end.await();// 准备的倒计时阻塞,当end.countDown()为0时,放行
System.out.println("GG");
threadPool.shutdown();//关闭线程池
}
CyclicBarrier:栅栏
定义:一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)
一个简单的栅栏例子:
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3);
ExecutorService threadPool = Executors.newFixedThreadPool(3);
threadPool.submit(new Thread(new Runner(barrier, "hehe1")));
threadPool.submit(new Thread(new Runner(barrier, "hehe2")));
threadPool.submit(new Thread(new Runner(barrier, "hehe3")));
}
}
class Runner implements Runnable {
private CyclicBarrier barrier;
private String name;
public Runner(CyclicBarrier barrier, String name) {
super();
this.barrier = barrier;
this.name = name;
}
@Override
public void run() {
try {
Thread.sleep(1000 * (new Random()).nextInt(8));
System.out.println(name + " 准备好了...");
// barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(name + ":GO!" + System.currentTimeMillis());
}
}
CyclicBarrier和CountDownLatch的区别:
1.CountDownLatch : 一个线程(或者多个), 等待另外N个线程完成某个事情之后才能执行。
2.CyclicBarrier : N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。这样应该就清楚一点了,对于CountDownLatch来说,重点是那个“一个线程”, 是它在等待, 而另外那N的线程在把“某个事情”做完之后可以继续等待,可以终止。而对于CyclicBarrier来说,重点是那N个线程,他们之间任何一个没有完成,所有的线程都必须等待。
CountDownLatch 是计数器, 线程完成一个就记一个, 就像 报数一样, 只不过是递减的.
而CyclicBarrier更像一个水闸, 线程执行就想水流, 在水闸处都会堵住, 等到水满(线程到齐)了, 才开始泄流.CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。
Semaphore 信号量
主要方法:
acquire()
:用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。
release()
:用来释放许可。注意,在释放许可之前,必须先获获得许可。
实例:
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 创建一个容量为5的信号量池,表示只能最大五个线程池访问
final Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 20; i++) {
final int NO = i;
Runnable runnable = new Runnable() {
public void run() {
try {
//获取许可
semaphore.acquire();
System.out.println("允许..." + NO);
Thread.sleep(1000);
// 访问完后,释放
semaphore.release();
Thread.sleep(500);
System.out.println("可用:" + semaphore.availablePermits());
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
cachedThreadPool.execute(runnable);
}
cachedThreadPool.shutdown();
}
网友评论