线程 JVM锁整理

1、线程的等待和通知

首先wait()和notify(),notifyAll()方法一定是一般对象方法,他们并不属于线程对象方法,一定是跟synchronized(监视器锁)结伴出现的。wait()方法执行时会释放获取的监视器锁,线程进入休眠等待状态。而notify()执行时,会随机唤醒一个等待状态的线程,并重新获取监视器锁,然后再继续执行。notifyAll()方法是唤醒所有的相同对象的等待线程,再去竞争获取监视器锁。

public class SimpleWN {
 final static Object object = new Object();
 public static class T1 implements Runnable {
 public void run() {
 synchronized (object) {
 System.out.println(System.currentTimeMillis() + ":T1 start!");
 try {
 System.out.println(System.currentTimeMillis() + ":T1 wait for object");
 object.wait();
 }catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println(System.currentTimeMillis() + ":T1 end!");
 }
 }
 }
 public static class T2 implements Runnable {
 public void run() {
 synchronized (object) {
 try {
 //让线程T1先执行,自己先睡2秒
 Thread.sleep(2000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 System.out.println(System.currentTimeMillis() + ":T2 start! notify one thread");
 object.notify();
 System.out.println(System.currentTimeMillis() + ":T2 end!");
 }
 }
 }
 public static void main(String[] args) {
 Thread t1 = new Thread(new T1());
 Thread t2 = new Thread(new T2());
 t1.start();
 t2.start();
 }
}

执行结果

1538646195634:T1 start!

1538646195635:T1 wait for object

1538646197635:T2 start! notify one thread

1538646197635:T2 end!

1538646197635:T1 end!

如果注释掉Thread.sleep(2000)代码块,则可能T2线程先执行,T1后执行,整个程序进入堵塞状态,无法唤醒!

2、等待线程结束

join()方法是执行一个wait()方法作用于当前线程,进行等待,如果当前线程是主线程则会使主线程等待。

public class JoinMain {
 public volatile static int i = 0;
 public static class AddThread implements Runnable {
 @Override
 public void run() {
 for (i = 0;i < 10000000;i++);
 }
 }
 public static void main(String[] args) throws InterruptedException {
 Thread at = new Thread(new AddThread());
 at.start();
 at.join();
 System.out.println(i);
 }
}

执行结果

10000000

如果注释掉at.join(),主线程输出值为0,主线程执行打印时,线程at还未执行。

3、守护线程

守护线程的作用就是所有用户线程(包含主线程)都结束了,该线程也自然结束了。

public class FIndReady {
 private static int num;
 private static boolean ready;
 private static class ReaderThread extends Thread {
 public void run() {
 while (ready) {
 System.out.println(num);
 }
 }
 }
 public static void main(String[] args) throws InterruptedException {
 Thread t = new ReaderThread();
 //设置守护线程
 t.setDaemon(true);
 t.start();
 num = 45;
 ready = true;
 }
}

这段代码如果不设置守护线程t.setDaemon(true),则会无限打印45,但设置了守护线程后,主线程结束后,就会停止打印45.

再来说说volatile,volatile本来是设置寄存器到内存的复制到所有线程可见的,不过寄存器到内存的复制以现在的电脑性能实在是太快了,所以我觉得volatile的意义已经不大了。

不过即便是设置了守护线程,如果加入了join()方法,主线程依然会等待守护线程执行完,这样就会无限打印45.

public class FIndReady {
 private static volatile int num;
 private static boolean ready;
 private static class ReaderThread extends Thread {
 public void run() {
 while (ready) {
 System.out.println(num);
 }
 }
 }
 public static void main(String[] args) throws InterruptedException {
 Thread t = new ReaderThread();
 //设置守护线程
 t.setDaemon(true);
 t.start();
 num = 45;
 ready = true;
 t.join();
 }
}

4、重入锁

重入锁指的是当一个线程申请获得一次加锁之后,当释放锁后再次获取该锁将无需再次申请,节省开销。

用加锁来实现多线程累加

public class VolatileQuestion {
 private static volatile Integer i = 0;
 private static Lock lock = new ReentrantLock();
 public static class PlusTask implements Runnable {
 public void run() {
 for (int k = 0; k < 10000; k++) {
 add();
 }
 }
 private static void add() {
 lock.lock();
 try {
 i++;
 }finally {
 lock.unlock();
 }
 }
 }
 public static void main(String[] args) throws InterruptedException {
 Thread[] threads = new Thread[10];
 for (int j = 0;j < 10;j++) {
 threads[j] = new Thread(new PlusTask());
 threads[j].start();
 }
 for (int j = 0;j< 10;j++) {
 threads[j].join();
 }
 System.out.println(i);
 }
}

当然还有两种方式可以达到同样的效果

public class VolatileQuestion {
 private static volatile Integer i = 0;
// private static Lock lock = new ReentrantLock();
 public static class PlusTask implements Runnable {
 public void run() {
 for (int k = 0; k < 10000; k++) {
 add();
 }
 }
 private static synchronized void add() {
 i++;
 }
 }
 public static void main(String[] args) throws InterruptedException {
 Thread[] threads = new Thread[10];
 for (int j = 0;j < 10;j++) {
 threads[j] = new Thread(new PlusTask());
 threads[j].start();
 }
 for (int j = 0;j< 10;j++) {
 threads[j].join();
 }
 System.out.println(i);
 }
}

原子类无锁

public class VolatileQuestion {
 private static AtomicInteger i = new AtomicInteger(0);
// private static Lock lock = new ReentrantLock();
 public static class PlusTask implements Runnable {
 public void run() {
 for (int k = 0; k < 10000; k++) {
 i.getAndIncrement();
 }
 }
 }
 public static void main(String[] args) throws InterruptedException {
 Thread[] threads = new Thread[10];
 for (int j = 0;j < 10;j++) {
 threads[j] = new Thread(new PlusTask());
 threads[j].start();
 }
 for (int j = 0;j< 10;j++) {
 threads[j].join();
 }
 System.out.println(i);
 }
}

运行结果都一样

100000

5、优先中断

优先中断并不是以获取锁为目的,而是以优先获取中断为目标

把一个死锁的例子逐步改成非死锁

public class InLock implements Runnable {
 public static ReentrantLock lock1 = new ReentrantLock();
 public static ReentrantLock lock2 = new ReentrantLock();
 private int lock;
 public InLock(int lock) {
 this.lock = lock;
 }
 public void run() {
 try {
 if (lock == 1) {
 lock1.lock();
// lock1.lockInterruptibly();
// lock1.tryLock();
 try {
 Thread.sleep(500);
 System.out.println(Thread.currentThread().getId() + "获取锁");
 }catch (InterruptedException e) {}
 lock2.lock();
// lock2.lockInterruptibly();
// lock2.tryLock();
 }else {
 lock2.lock();
// lock2.lockInterruptibly();
// lock2.tryLock();
 try {
 Thread.sleep(500);
 System.out.println(Thread.currentThread().getId() + "获取锁");
 }catch (InterruptedException e) {}
 lock1.lock();
// lock1.lockInterruptibly();
// lock1.tryLock();
 }
 }catch (Exception e) {
 e.printStackTrace();
 }finally {
 //lock1是否获取锁
 if (lock1.isHeldByCurrentThread()) {
 lock1.unlock();
 }
 if (lock2.isHeldByCurrentThread()) {
 lock2.unlock();
 }
 System.out.println(Thread.currentThread().getId() + ":线程退出");
 }
 }
 public static void main(String[] args) throws InterruptedException {
 InLock r1 = new InLock(1);
 InLock r2 = new InLock(2);
 Thread t1 = new Thread(r1);
 Thread t2 = new Thread(r2);
 t1.start();
 t2.start();
// Thread.sleep(1000);
 //t2.interrupt();
 }
}

运行结果

12获取锁

13获取锁

这是一个死锁,t1,t2线程都分别获取了lock1,lock2的锁之后在未解锁的情况下,去获取对方的锁,谁也得不到对方的锁而出现死锁,程序堵塞。

程序修改成中断t2,t1可以获取锁,程序执行完毕,抛出一个中断异常

public class InLock implements Runnable {
 public static ReentrantLock lock1 = new ReentrantLock();
 public static ReentrantLock lock2 = new ReentrantLock();
 private int lock;
 public InLock(int lock) {
 this.lock = lock;
 }
 public void run() {
 try {
 if (lock == 1) {
// lock1.lock();
 lock1.lockInterruptibly();
// lock1.tryLock();
 try {
 Thread.sleep(500);
 System.out.println(Thread.currentThread().getId() + "获取锁");
 }catch (InterruptedException e) {}
// lock2.lock();
 lock2.lockInterruptibly();
// lock2.tryLock();
 }else {
// lock2.lock();
 lock2.lockInterruptibly();
// lock2.tryLock();
 try {
 Thread.sleep(500);
 System.out.println(Thread.currentThread().getId() + "获取锁");
 }catch (InterruptedException e) {}
// lock1.lock();
 lock1.lockInterruptibly();
// lock1.tryLock();
 }
 }catch (Exception e) {
 e.printStackTrace();
 }finally {
 //lock1是否获取锁
 if (lock1.isHeldByCurrentThread()) {
 lock1.unlock();
 }
 if (lock2.isHeldByCurrentThread()) {
 lock2.unlock();
 }
 System.out.println(Thread.currentThread().getId() + ":线程退出");
 }
 }
 public static void main(String[] args) throws InterruptedException {
 InLock r1 = new InLock(1);
 InLock r2 = new InLock(2);
 Thread t1 = new Thread(r1);
 Thread t2 = new Thread(r2);
 t1.start();
 t2.start();
 Thread.sleep(1000);
 t2.interrupt();
 }
}

lock2.lockInterruptibly()会优先响应t2.interrupt()发生中断,抛出中断异常,lock2自动解锁,运行结果

12获取锁

13获取锁

java.lang.InterruptedException

at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)

at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)

at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)

at com.guanjian.InLock.run(InLock.java:38)

at java.lang.Thread.run(Thread.java:745)

13:线程退出

12:线程退出

再修改成尝试获取锁

public class InLock implements Runnable {
 public static ReentrantLock lock1 = new ReentrantLock();
 public static ReentrantLock lock2 = new ReentrantLock();
 private int lock;
 public InLock(int lock) {
 this.lock = lock;
 }
 public void run() {
 try {
 if (lock == 1) {
// lock1.lock();
// lock1.lockInterruptibly();
 lock1.tryLock();
 try {
 Thread.sleep(500);
 System.out.println(Thread.currentThread().getId() + "获取锁");
 }catch (InterruptedException e) {}
// lock2.lock();
// lock2.lockInterruptibly();
 if (lock2.tryLock()) {
 System.out.println("1获取成功");
 }
 }else {
// lock2.lock();
// lock2.lockInterruptibly();
 lock2.tryLock();
 try {
 Thread.sleep(500);
 System.out.println(Thread.currentThread().getId() + "获取锁");
 }catch (InterruptedException e) {}
// lock1.lock();
// lock1.lockInterruptibly();
 if (lock1.tryLock()) {
 System.out.println("2获取成功");
 }
 }
 }catch (Exception e) {
 e.printStackTrace();
 }finally {
 //lock1是否获取锁
 if (lock1.isHeldByCurrentThread()) {
 System.out.println(Thread.currentThread().getId() + "解锁");
 lock1.unlock();
 }
 if (lock2.isHeldByCurrentThread()) {
 System.out.println(Thread.currentThread().getId() + "解锁");
 lock2.unlock();
 }
 System.out.println(Thread.currentThread().getId() + ":线程退出");
 }
 }
 public static void main(String[] args) throws InterruptedException {
 InLock r1 = new InLock(1);
 InLock r2 = new InLock(2);
 Thread t1 = new Thread(r1);
 Thread t2 = new Thread(r2);
 t1.start();
 t2.start();
// Thread.sleep(1000);
// t2.interrupt();
 }
}

tryLock()在拿不到锁的时候可以马上返回false,不会堵塞,可以大大减少死锁的可能性,运行结果如下

12获取锁

13获取锁

12解锁

12:线程退出

13解锁

13:线程退出

我们可以看到他们都没有拿到对方的锁,但是没有死锁堵塞。

tryLock()可以设等待时间,等待时间后再返回

public class TimeLock implements Runnable {
 public static ReentrantLock lock = new ReentrantLock();
 public void run() {
 try {
 if (lock.tryLock(7, TimeUnit.SECONDS)) {
 System.out.println(Thread.currentThread().getName());
 Thread.sleep(6000);
 }else {
 System.out.println("get lock failed");
 }
 }catch (InterruptedException e) {
 e.printStackTrace();
 }finally {
 if (lock.isHeldByCurrentThread()) {
 lock.unlock();
 }
 }
 }
 public static void main(String[] args) {
 TimeLock tl = new TimeLock();
 Thread t1 = new Thread(tl);
 Thread t2 = new Thread(tl);
 t1.start();
 t2.start();
 }
}

一个线程拿到锁以后睡眠6秒解锁,另一个线程等待7秒拿锁,结果2个线程都拿到了锁

运行结果

Thread-0

Thread-1

如果等待时间小于睡眠时间,则拿不到锁

public class TimeLock implements Runnable {
 public static ReentrantLock lock = new ReentrantLock();
 public void run() {
 try {
 if (lock.tryLock(5, TimeUnit.SECONDS)) {
 System.out.println(Thread.currentThread().getName());
 Thread.sleep(6000);
 }else {
 System.out.println("get lock failed");
 }
 }catch (InterruptedException e) {
 e.printStackTrace();
 }finally {
 if (lock.isHeldByCurrentThread()) {
 lock.unlock();
 }
 }
 }
 public static void main(String[] args) {
 TimeLock tl = new TimeLock();
 Thread t1 = new Thread(tl);
 Thread t2 = new Thread(tl);
 t1.start();
 t2.start();
 }
}

运行结果

Thread-0

get lock failed

6、公平锁

让所有参与的线程都能够依次公平的获取锁,成本高,性能底下

public class FairLock implements Runnable {
 //设置公平锁
 public static ReentrantLock lock = new ReentrantLock(true);
 public void run() {
 while (true) {
 try {
 lock.lock();
 System.out.println(Thread.currentThread().getName() + "获得锁");
 }finally {
 lock.unlock();
 //break;
 }
 }
 }
 public static void main(String[] args) {
 FairLock r1 = new FairLock();
 Thread t1 = new Thread(r1);
 Thread t2 = new Thread(r1);
 t1.start();
 t2.start();
 }
}

运行结果(截取部分)

Thread-0获得锁

Thread-1获得锁

Thread-0获得锁

Thread-1获得锁

Thread-0获得锁

Thread-1获得锁

Thread-0获得锁

Thread-1获得锁

Thread-0获得锁

Thread-1获得锁

Thread-0获得锁

从结果可以看出,两个线程之间总是交替获取锁。

取消公平锁

public class FairLock implements Runnable {
 //设置公平锁
 public static ReentrantLock lock = new ReentrantLock();
 public void run() {
 while (true) {
 try {
 lock.lock();
 System.out.println(Thread.currentThread().getName() + "获得锁");
 }finally {
 lock.unlock();
 //break;
 }
 }
 }
 public static void main(String[] args) {
 FairLock r1 = new FairLock();
 Thread t1 = new Thread(r1);
 Thread t2 = new Thread(r1);
 t1.start();
 t2.start();
 }
}

运行结果(截取部分)

Thread-0获得锁

Thread-0获得锁

Thread-0获得锁

Thread-0获得锁

Thread-0获得锁

Thread-0获得锁

Thread-0获得锁

Thread-0获得锁

Thread-0获得锁

Thread-1获得锁

Thread-1获得锁

Thread-1获得锁

Thread-1获得锁

Thread-1获得锁

由结果可以看出,获取过一次锁的线程总是更容易获取下一次锁,是非公平的。

7、与重入锁结伴的等待与通知

await()方法,singal()方法与singalAll()方法类似于Object的wait(),notify(),notifyAll()方法。

public class ReenterLockCondition implements Runnable{
 public static ReentrantLock lock = new ReentrantLock();
 public static Condition condition = lock.newCondition();
 public void run() {
 try {
 lock.lock();
 condition.await();
 System.out.println("Thread is going on");
 }catch (InterruptedException e) {
 e.printStackTrace();
 }finally {
 lock.unlock();
 }
 }
 public static void main(String[] args) throws InterruptedException {
 ReenterLockCondition tl = new ReenterLockCondition();
 Thread t1 = new Thread(tl);
 t1.start();
 System.out.println("唤醒前先嗨2秒");
 Thread.sleep(2000);
 lock.lock();
 condition.signal();
 lock.unlock();
 }
}

运行结果

唤醒前先嗨2秒

Thread is going on

8、信号量

信号量的理解就是如果有很多线程需要执行,而每次仅允许几个线程执行,只有其中有线程执行完毕才允许后面的线程进入执行,但总执行线程数不能多于限制数。

public class SemaphoreDemo {
 private Semaphore smp = new Semaphore(3,true); //公平策略
 private Random rnd = new Random();
 class Task implements Runnable{
 private String id;
 Task(String id){
 this.id = id;
 }
 public void run(){
 try {
 //阻塞,等待信号
 smp.acquire();
 //smp.acquire(int permits);//使用有参数方法可以使用permits个许可
 System.out.println("Thread " + id + " is working");
 System.out.println("在等待的线程数目:"+ smp.getQueueLength());
 work();
 System.out.println("Thread " + id + " is over");
 } catch (InterruptedException e) {
 }
 finally
 {
 //释放信号
 smp.release();
 }
 }
 public void work() {//假装在工作,实际在睡觉
 int worktime = rnd.nextInt(1000);
 System.out.println("Thread " + id + " worktime is "+ worktime);
 try {
 Thread.sleep(worktime);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 }
 public static void main(String[] args){
 SemaphoreDemo semaphoreDemo = new SemaphoreDemo();
 ExecutorService se = Executors.newCachedThreadPool();
 se.submit(semaphoreDemo.new Task("a"));
 se.submit(semaphoreDemo.new Task("b"));
 se.submit(semaphoreDemo.new Task("c"));
 se.submit(semaphoreDemo.new Task("d"));
 se.submit(semaphoreDemo.new Task("e"));
 se.submit(semaphoreDemo.new Task("f"));
 se.shutdown();
 }
}

运行结果

Thread b is working

在等待的线程数目:0

Thread b worktime is 860

Thread a is working

Thread c is working

在等待的线程数目:1

在等待的线程数目:1

Thread a worktime is 445

Thread c worktime is 621

Thread a is over

Thread d is working

在等待的线程数目:2

Thread d worktime is 237

Thread c is over

Thread e is working

在等待的线程数目:1

Thread e worktime is 552

Thread d is over

Thread f is working

在等待的线程数目:0

Thread f worktime is 675

Thread b is over

Thread e is over

Thread f is over

结果解读:a,b,c三个线程进入工作,其他线程无法进入,a线程执行完,空出一个线程位,d线程进入工作,c线程执行完,又空出一个线程位,e线程进入工作,d线程执行完,f线程进入工作,b线程执行完,e线程执行完,f线程执行完。

9、读写锁

当所有的写锁释放后,所有的读锁将并行执行,否则读锁和写锁都将进行一一锁定。

public class ReadWriteLockDemo {
 private static Lock lock = new ReentrantLock();
 private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
 private static Lock readLock = reentrantReadWriteLock.readLock();
 private static Lock writeLock = reentrantReadWriteLock.writeLock();
 private volatile int value;
 public Object handleRead(Lock lock) throws InterruptedException {
 try {
 lock.lock();
 Thread.sleep(1000);
 return "读" + Thread.currentThread().getName() + " " + value;
 }finally {
 lock.unlock();
 }
 }
 public void handleWrite(Lock lock,int index) throws InterruptedException {
 try {
 lock.lock();
 Thread.sleep(1000);
 value = index;
 System.out.println("写" + Thread.currentThread().getName() +" " + value);
 }finally {
 lock.unlock();
 }
 }
 public static void main(String[] args) {
 final ReadWriteLockDemo demo = new ReadWriteLockDemo();
 Runnable readRunnable = new Runnable() {
 public void run() {
 try {
 System.out.println(demo.handleRead(readLock));
// System.out.println(demo.handleRead(lock));
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 };
 Runnable writeRunnable = new Runnable() {
 public void run() {
 try {
 demo.handleWrite(writeLock,new Random().nextInt(100));
// demo.handleWrite(lock,new Random(100).nextInt());
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 };
 for (int i = 0;i < 2;i++) {
 new Thread(writeRunnable).start();
 }
 for (int i = 0;i < 18;i++) {
 new Thread(readRunnable).start();
 }
 }
}

运行结果

读Thread-2 0

读Thread-3 0

读Thread-4 0

写Thread-0 82

写Thread-1 5

读Thread-5 5

读Thread-10 5

读Thread-9 5

读Thread-8 5

读Thread-6 5

读Thread-7 5

读Thread-13 5

读Thread-15 5

读Thread-18 5

读Thread-16 5

读Thread-12 5

读Thread-19 5

读Thread-11 5

读Thread-14 5

读Thread-17 5

运行结果解读:在读Thread-5 5之前,每秒出一个结果,从读Thread-5 5开始到读Thread-17 5没有1秒停顿,并行同时执行,说明在读Thread-17 5之后没有锁竞争。

如果把读写锁换成可重入锁

public class ReadWriteLockDemo {
 private static Lock lock = new ReentrantLock();
 private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
 private static Lock readLock = reentrantReadWriteLock.readLock();
 private static Lock writeLock = reentrantReadWriteLock.writeLock();
 private volatile int value;
 public Object handleRead(Lock lock) throws InterruptedException {
 try {
 lock.lock();
 Thread.sleep(1000);
 return "读" + Thread.currentThread().getName() + " " + value;
 }finally {
 lock.unlock();
 }
 }
 public void handleWrite(Lock lock,int index) throws InterruptedException {
 try {
 lock.lock();
 Thread.sleep(1000);
 value = index;
 System.out.println("写" + Thread.currentThread().getName() +" " + value);
 }finally {
 lock.unlock();
 }
 }
 public static void main(String[] args) {
 final ReadWriteLockDemo demo = new ReadWriteLockDemo();
 Runnable readRunnable = new Runnable() {
 public void run() {
 try {
// System.out.println(demo.handleRead(readLock));
 System.out.println(demo.handleRead(lock));
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 };
 Runnable writeRunnable = new Runnable() {
 public void run() {
 try {
// demo.handleWrite(writeLock,new Random().nextInt(100));
 demo.handleWrite(lock,new Random().nextInt(100));
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 };
 for (int i = 0;i < 2;i++) {
 new Thread(writeRunnable).start();
 }
 for (int i = 0;i < 18;i++) {
 new Thread(readRunnable).start();
 }
 }
}

虽然运行结果一样,但是结果是1秒出一个,说明次次都是被锁锁了1秒。

10、倒计时器

倒计时器的作用是让参与的线程挨个执行,其他线程等待,到计时器计时完毕,其他线程才可以继续执行。

public class CountDownLatchDemo implements Runnable {
 //设定计时器
 static final CountDownLatch end = new CountDownLatch(10);
 private static AtomicInteger i = new AtomicInteger(10);
 public void run() {
 try {
 Thread.sleep(new Random().nextInt(10) * 1000);
 i.getAndDecrement();
 System.out.println("check complete,剩余次数" + i.toString());
 //计时器中的一个线程完成,计时器-1
 end.countDown();
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 public static void main(String[] args) throws InterruptedException {
 ExecutorService exec = Executors.newFixedThreadPool(10);
 CountDownLatchDemo demo = new CountDownLatchDemo();
 for (int i = 0;i < 10;i++) {
 exec.submit(demo);
 }
 //让主线程等待计时器倒数完成才允许继续执行
 end.await();
 System.out.println("Fire!");
 exec.shutdown();
 }
}

运行结果

check complete,剩余次数9

check complete,剩余次数8

check complete,剩余次数7

check complete,剩余次数6

check complete,剩余次数5

check complete,剩余次数4

check complete,剩余次数3

check complete,剩余次数2

check complete,剩余次数1

check complete,剩余次数0

Fire!

如果我们把static final CountDownLatch end = new CountDownLatch(10);改成小于10的数,比如3

public class CountDownLatchDemo implements Runnable {
 //设定计时器
 static final CountDownLatch end = new CountDownLatch(3);
 private static AtomicInteger i = new AtomicInteger(10);
 public void run() {
 try {
 Thread.sleep(new Random().nextInt(10) * 1000);
 System.out.println("check complete,剩余次数" + i.decrementAndGet());
 //计时器中的一个线程完成,计时器-1
 end.countDown();
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 public static void main(String[] args) throws InterruptedException {
 ExecutorService exec = Executors.newFixedThreadPool(10);
 CountDownLatchDemo demo = new CountDownLatchDemo();
 for (int i = 0;i < 10;i++) {
 exec.submit(demo);
 }
 //让主线程等待计时器倒数完成才允许继续执行
 end.await();
 System.out.println("Fire!");
 exec.shutdown();
 }
}

这里面我们做了一点小小的调整,就是原子类打印System.out.println("check complete,剩余次数" + i.decrementAndGet())而并不是i.getAndDecrement(); System.out.println("check complete,剩余次数" + i.toString());这个改动是为了让打印不会打印出相同的数,否则即便是原子类,这也是两步操作,依然会打印出相同的数,原因可以自己思考。

运行结果

check complete,剩余次数9

check complete,剩余次数7

check complete,剩余次数8

Fire!

check complete,剩余次数6

check complete,剩余次数4

check complete,剩余次数3

check complete,剩余次数5

check complete,剩余次数2

check complete,剩余次数1

check complete,剩余次数0

从结果可以看出,线程demo只并行执行了3次,主线程就继续执行了。而剩余次数混乱说明是并行执行,而不是依次执行。

线程 JVM锁整理

jvm

相关推荐