最近准备将自己的学习笔记记录下来,养成写博客的习惯,一下内容是根据书上和网上资料整合而成,同时也有一部分自己的理解。
一、synchronize同步锁(内置锁)
为了防止多线程同时操作同一个位置,可以上锁的有对象和class。
public class synchronizedDemo{
//同步方法
public synchronized void method{
//doSomething
}
//同步块
synchronized(this){
//doSomething
}
//同步class对象
synchronized(synchronizedDemo.class){
//doSomething
}
//同步静态方法
public synchronized static void method{
//doSomething
}
}
同步方法和同步块锁的是对象,同步class和同步静态方法锁的是class对象。
锁class对象是作用于多线程访问同一个synchronized代码块时
而锁对象是作用于多线程访问同一个对象的synchronized代码块时
二、ReentrantLock(显示锁)
java6.0后增加的新的机制
和synchronize不同的是,synchronized获取和释放都在同一个代码块,而它更加灵活,可以自由控制锁的获取和释放。
同时可以提供轮训所、定时锁、公平锁和非公平锁。
lock()—>获取锁;
tryLock()—>尝试获取锁;
tryLock(long timeout ,timeUnit Unit)—>指定获取超时的时间
unLock()—>释放锁
newCondition()—> 获取锁的condition
ps:显示锁Lock之后必须解锁,不然会导致死锁,这也是显示锁没有完成代替synchronized的原因。
reentrantlock还有一个非常重要的方法:newCondition(),作用于锁上的一个条件,来完成wait()、notify()、notifyall()等方法。
下面是通过reentrantLock完成的一个有界的阻塞队列
public class MyArrayBlockingQueue {
private final T[] items;
//显示锁
private final Lock lock = new ReentrantLock();
//队满的条件
private Condition notFull = lock.newCondition();
//队空的条件
private Condition notEmpty = lock.newCondition();
//头部
private int head;
//尾部
private int tail;
//当前数据个数
private int count;
public MyArrayBlockingQueue(int MaxSize) {
items = (T[]) new Object[MaxSize];
}
public MyArrayBlockingQueue() {
this(10);
}
public void put(T t) {
lock.lock();
try {
while (count == getCapacity()) {
System.out.println("数据已满,请等待");
notFull.wait();
}
items[tail] = t;
if (++tail == getCapacity())
tail = 0;
++count;
notEmpty.signalAll();//唤醒等待数据的线程
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
public T take() {
lock.lock();
try {
while (count == getCapacity()) {
System.out.println("暂无数据,请等待");
notEmpty.wait();
}
T ret = items[head];
items[head] = null;
if (++head == getCapacity())
head = 0;
--count;
notFull.signalAll();//唤醒添加数据的线程
return ret;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
return null;
}
public int getCapacity() {
return items.length;
}
public int size() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
三、信号量Semaphore
他的本质是”共享锁“,线程必须先获取信号量(acquire)才能继续执行,否则将等待。
在执行完成之后,必须释放信号(release),其他线程才能取得信号。
示例代码
public static void main(String[] args) {
// 线程池
ExecutorService exec = Executors.newCachedThreadPool();
// 只能3个线程同时访问
final Semaphore semp = new Semaphore(3);
// 模拟5个客户端访问
for (int index = 0; index < 5; index++) {
final int NO = index;
Runnable run = new Runnable() {
public void run() {
try {
// 获取许可
semp.acquire();
System.out.println("Accessing: " + NO);
Thread.sleep((long) (Math.random() * 10000));
// 访问完后,释放
semp.release();
System.out.println("-----------------"+semp.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
exec.execute(run);
}
// 退出线程池
exec.shutdown();
}
}
执行结果如下:
Accessing: 0
Accessing: 1
Accessing: 2
—————–0
Accessing: 4
—————–1
Accessing: 3
四、循环栅栏CyclicBarrier
CyclicBarrier是一个同步辅助类,允许N个线程相互等待(await),直到达到某个公共点,再一起继续执行;因为可以重用,所以称它为循环Barrier.
示例:赛跑时,等待所有人都准备好时,才起跑.
public class CyclicBarrierTest {
2.
3. public static void main(String[] args) throws IOException, InterruptedException {
4. //如果将参数改为4,但是下面只加入了3个选手,这永远等待下去
5. //Waits until all parties have invoked await on this barrier.
6. CyclicBarrier barrier = new CyclicBarrier(3);
7.
8. ExecutorService executor = Executors.newFixedThreadPool(3);
9. executor.submit(new Thread(new Runner(barrier, "1号选手")));
10. executor.submit(new Thread(new Runner(barrier, "2号选手")));
11. executor.submit(new Thread(new Runner(barrier, "3号选手")));
12.
13. executor.shutdown();
14. }
15. }
16.
17. class Runner implements Runnable {
18. // 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)
19. private CyclicBarrier barrier;
20.
21. private String name;
22.
23. public Runner(CyclicBarrier barrier, String name) {
24. super();
25. this.barrier = barrier;
26. this.name = name;
27. }
28.
29. @Override
30. public void run() {
31. try {
32. Thread.sleep(1000 * (new Random()).nextInt(8));
33. System.out.println(name + " 准备好了...");
34. // barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
35. barrier.await();
36. } catch (InterruptedException e) {
37. e.printStackTrace();
38. } catch (BrokenBarrierException e) {
39. e.printStackTrace();
40. }
41. System.out.println(name + " 起跑!");
42. }
43. }
输出结果:
3号选手 准备好了…
2号选手 准备好了…
1号选手 准备好了…
1号选手 起跑!
2号选手 起跑!
3号选手 起跑!
五、闭锁CountDownLatch
CountDownLatch也是同步辅助类。他的作用是使一个或多个线程等待(await),直到所有线程都完成某个事件,调用(countDown)后,才能继续执行。
示例代码:
1. public class countDownlatchTest {
2. public static void main(String[] args) throws InterruptedException {
3. CountDownLatch countDownLatch = new CountDownLatch(5);
4. for(int i=0;i<5;i++){
5. new Thread(new readNum(i,countDownLatch)).start();
6. }
7. countDownLatch.await();
8. System.out.println("等待结束,继续执行");
9. //dosomething
10. }
11.
12. static class readNum implements Runnable{
13. private int id;
14. private CountDownLatch latch;
15. public readNum(int id,CountDownLatch latch){
16. this.id = id;
17. this.latch = latch;
18. }
19. @Override
20. public void run() {
21. synchronized (this){
22. System.out.println("id:"+id);
23. latch.countDown();
24. System.out.println("线程组任务"+id+"结束,其他任务继续");
25. }
26. }
27. }
28. }
输出结果:
id:1
线程组任务1结束,其他任务继续
id:0
线程组任务0结束,其他任务继续
id:2
线程组任务2结束,其他任务继续
id:3
线程组任务3结束,其他任务继续
id:4
线程组任务4结束,其他任务继续
等待结束,继续执行
cyclicbarrier countdownlatch 区别:(个人见解)
1、很明显的区别:cyclicbarrier可重用,后者不行。
2、前者偏向于等待所有玩家(线程)到齐之后,一起去做后续事件
后者偏向于等待所有玩家(线程)都达成某个条件后,继续做后续事件。