多个atomic类连续调用能否构成原子性?
public class Demo {
AtomicInteger count = new AtomicInteger(0);
//比如count加到999了,这时候一个线程拿到count判断,虽然.get方法保证原子性,但是他阻止
//不了其它线程也来判断,所以第一个线程还没加完,第二个线程也进来了,这时候两个线程都给count加了
public void test(){
for (int i = 0; i < 10000; i++) {
if(count.get() < 1000){
count.incrementAndGet();
}
}
}
public static void main(String[] args) {
Demo demo = new Demo();
List<Thread> threads = new ArrayList();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(demo::test, "thread-" + i));
}
threads.forEach((o)->o.start());
threads.forEach((o)->{
try {
o.join();
} catch (Exception e) {
e.printStackTrace();
}
});
System.out.println(demo.count);
}
}
countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
实现一个容器,提供两个方法,add,size
写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束线程2
CountDownLatch
- 使用await和countdown方法替代wait和notify
- CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行
- 相当于是发令枪,运动员线程调用await等待,计数到0开始运行
- 当不涉及同步,只是涉及线程通信的时候,用synchronized加wait,notify就显得太重了
public class Container5 {
volatile List lists = new ArrayList();
public void add(Object o){
lists.add(o);
}
public int size(){
return lists.size();
}
public static void main(String[] args) {
Container5 c = new Container5();
CountDownLatch latch = new CountDownLatch(1);
new Thread(()->{
System.out.println("t2启动");
if (c.size() != 5) {
try {
latch.await();//准备
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("t2结束");
}
}," t2").start();
new Thread(()->{
System.out.println("t1启动");
for (int i = 0; i < 10; i++) {
c.add(new Object());
System.out.println("add " + i);
if (c.size() == 5) {
latch.countDown();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}, "t1").start();
}
}
写一个固定容量同步容器,拥有Put和get方法,以及getCount方法, 能够支持两个生产者线程以及10个消费者线程的阻塞调用
这里探究为什么大多数情况下wait和while是一起使用的,
- 因为这里是有两个生产者线程并且容器容量是固定的,生产方法加了锁。
- 如果容器满了,这时候第一个生产者线程拿到了锁,他会判断有没有满,如果满了就等待,在等待
- 的时候会释放掉锁资源,这时候第二个生产者线程就会拿到锁,然后他也会判断是否满,因为容器是
- 满了,第二个生产者线程也会等待并且释放锁,当消费者消费之后唤醒所有线程,这时候两个生产者线程
- 都醒来了,因为要竞争锁资源,比如第一个生产者线程拿到了锁,他给容器又加到十了,陷入等待状态,
- 锁资源释放掉,第二个生产者线程这时候拿到锁资源,他会继续执行(从上次睡眠的地方继续),如果是if
- 的话,他在wait阻塞之前就已经执行了一次if,所以不会再执行,而是继续往下执行,那这时候就超过了
- 容器的容量。所以为了让他再一次判断,这里使用while
public class Container1<T>{
private final LinkedList<T> lists = new LinkedList<>();
private final int MAX = 10;
private int count = 0;
public synchronized void put(T t){
while (lists.size() == MAX) {//
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
lists.add(t);
++count;
this.notifyAll();
}
public synchronized T get(){
T t = null;
while (lists.size() == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
t = lists.removeFirst();
count--;
this.notifyAll();
return t;
}
public static void main(String[] args) {
Container1<String> c = new Container1<>();
for (int i = 0; i < 100; i++) {
new Thread(()->{
for (int j = 0; j < 5; j++) {
System.out.println(c.get());
}
}, "c" + i).start();
}
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 2; i++) {
new Thread(()->{
for (int j = 0; j < 25; j++) {
c.put(Thread.currentThread().getName() + "" + j);
}
}, "p" + i).start();
}
}
}
使用Lock和Condition来实现
- condition就是在什么条件下怎么做
- 对比上一个例子,Condition的方式可以更加精确的指定哪些线程被唤醒
public class Container2<T> {
private final LinkedList<T> lists = new LinkedList<>();
private final int MAX = 10;
private int count = 0;
private Lock lock = new ReentrantLock();
private Condition producer = lock.newCondition();
private Condition consumer = lock.newCondition();
public void put(T t){
try {
lock.lock();
while (lists.size() == MAX) {
producer.await();
}
lists.add(t);
++count;
consumer.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public T get(){
T t = null;
try {
lock.lock();
while (lists.size() == 0) {
consumer.await();
}
t = lists.removeFirst();
count --;
producer.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return t;
}
public static void main(String[] args) {
Container2<String> c = new Container2<>();
for (int i = 0; i < 100; i++) {
new Thread(()->{
for (int j = 0; j < 5; j++) {
System.out.println(c.get());
}
}, "c" + i).start();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 2; i++) {
new Thread(()->{
for (int j = 0; j < 25; j++) {
c.put(Thread.currentThread().getName() + " " + j);
}
}, "p" + i).start();
}
}
}