线程,JVM锁整理

一、线程的等待和通知java

首先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();
    }
}

执行结果dom

1538646195634:T1 start!
1538646195635:T1 wait for object
1538646197635:T2 start! notify one thread
1538646197635:T2 end!
1538646197635:T1 end!ide

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

二、等待线程结束性能

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

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);
    }
}

执行结果this

10000000spa

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

三、守护线程

守护线程的做用就是全部用户线程(包含主线程)都结束了,该线程也天然结束了。

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();
    }
}

四、重入锁

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

用加锁来实现多线程累加

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

五、优先中断

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

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

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

六、公平锁

让全部参与的线程都可以依次公平的获取锁,成本高,性能底下

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得到锁

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

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

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

八、信号量

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

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线程执行完。

九、读写锁

当全部的写锁释放后,全部的读锁将并行执行,不然读锁和写锁都将进行一一锁定。

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秒。

十、倒计时器

倒计时器的做用是让参与的线程挨个执行,其余线程等待,到计时器计时完毕,其余线程才能够继续执行。

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次,主线程就继续执行了。而剩余次数混乱说明是并行执行,而不是依次执行。

本人以前博客《静态变量的多线程同步问题》有一个countDown()方法和await()方法调换位置的样例,目的是为了让全部的任务线程等待(此时不一样的任务线程已经生成),直到主线程countDown()的时候,任务线程才能够继续执行,从而不会出现单独线程抢占,让不一样的线程都可以产生订单号。有兴趣的朋友能够查看以前的该博文。

十一、循环栅栏

循环栅栏跟倒计时器最大的不一样就是倒计时器当计数减到0的时候,开始容许其余线程执行,倒计时器不可再使用,而循环栅栏则不管多少线程执行,只要到了设置的限制数,就会执行绑定的线程方法,能够循环使用。若是到不了设置的限制数就会进行堵塞。

总共10个士兵,每5个士兵集合,进行一次报告共报告2次,所有集合后,每5个士兵执行任务进行一次报告共报告2次。

public class CyclicBarrierDemo {
    //任务线程
    public static class Soldier implements Runnable {
        private String soldier;
        private final CyclicBarrier cyclic;

        public Soldier(CyclicBarrier cyclic,String soldierName) {
            this.cyclic = cyclic;
            this.soldier = soldierName;
        }

        public void run() {
            try {
                //等待全部士兵到齐
                cyclic.await();
                doWork();
                //等待全部士兵完成任务
                cyclic.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
        private void doWork() {
            try {
                Thread.sleep(new Random().nextInt(1000));
                BarrierRun.flag = true;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(soldier + ":任务完成");
        }
    }
    //跟循环栅栏绑定的线程
    public static class BarrierRun implements Runnable {
        int N;
        public static volatile boolean flag;
        public BarrierRun(int N) {
            this.N = N;
        }

        public void run() {
            if (flag) {
                System.out.println("司令:[士兵" + N + "个,任务完成!]");
            }else {
                System.out.println("司令:[士兵" + N + "个,集合完毕!]");
            }
        }
    }

    public static void main(String[] args) {
        final int N = 5;
        Thread[] allSoldier = new Thread[10];
        BarrierRun.flag = false;
        //定义循环栅栏
        CyclicBarrier cyclic = new CyclicBarrier(N,new BarrierRun(N));
        System.out.println("集合队伍");
        for (int i = 0;i < 10;i++) {
            System.out.println("士兵" + i + "报道!");
            allSoldier[i] = new Thread(new Soldier(cyclic,"士兵 " + i));
            allSoldier[i].start();
        }
    }
}

运行结果

集合队伍
士兵0报道!
士兵1报道!
士兵2报道!
士兵3报道!
士兵4报道!
士兵5报道!
司令:[士兵5个,集合完毕!]
士兵6报道!
士兵7报道!
士兵8报道!
士兵9报道!
司令:[士兵5个,集合完毕!]
士兵 6:任务完成
士兵 5:任务完成
士兵 4:任务完成
士兵 0:任务完成
士兵 2:任务完成
司令:[士兵5个,任务完成!]
士兵 8:任务完成
士兵 3:任务完成
士兵 9:任务完成
士兵 7:任务完成
士兵 1:任务完成
司令:[士兵5个,任务完成!]

十二、线程中断、阻塞

暴力中止线程stop()方法,该方法无视任何加锁状况是否执行完毕,直接把线程中止,会出现数据不一致的状况,在生产环境中禁止使用

public class StopThreadUnsafe {
    public static User u = new User();
    public static class User {
        private int id;
        private String name;
        public User() {
            id = 0;
            name = "0";
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "User [id=" + id + ",name=" + name +"]";
        }
    }
    public static class ChangeObjectThread implements Runnable {
        public void run() {
            while (true) {
                synchronized (u) {
                    int v = (int)(System.currentTimeMillis() / 1000);
                    u.setId(v);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    u.setName(String.valueOf(v));
                }
                //线程让步
                Thread.yield();
            }
        }
    }
    public static class ReadObjectThread implements Runnable {
        public void run() {
            while (true) {
                synchronized (u) {
                    if (u.getId() != Integer.parseInt(u.getName())) {
                        System.out.println(u.toString());
                    }
                }
                Thread.yield();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new Thread(new ReadObjectThread()).start();
        while (true) {
            Thread t = new Thread(new ChangeObjectThread());
            t.start();
            Thread.sleep(150);
            t.stop();
        }
    }
}

运行结果(截取部分)

User [id=1538981520,name=1538981519]
User [id=1538981520,name=1538981519]
User [id=1538981520,name=1538981519]
User [id=1538981520,name=1538981519]
User [id=1538981520,name=1538981519]
User [id=1538981520,name=1538981519]
User [id=1538981520,name=1538981519]
User [id=1538981523,name=1538981522]
User [id=1538981523,name=1538981522]
User [id=1538981523,name=1538981522]

结果解读:为何会出现这样的打印呢,按道理user的id跟name赋值都是相同的,其实就是t.stop()出的问题,t.stop()在线程t尚未执行完毕时强行结束线程,使得user的id可能更新,name仍是上一次循环的值,没有赋新值,因此出现不等,读取线程就会判断不等进行打印.

通知中断interrupt(),该方法并不能立刻让线程中止,只是一个通知,目标线程接到通知后如何处理,彻底由目标线程自行决定,若是无条件退出,则会碰到stop()的老问题.

public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                while (true) {
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("Interruted!");
                        break;
                    }
                    Thread.yield();
                }
            }
        };
        t1.start();
        Thread.sleep(2000);
        t1.interrupt();
    }
}

运行结果

Interruted!

当发出中断通知后,只有遇到Thread.currentThread().isInterrupted()代码段时,才会响应中断,并做出本身的处理,不然不会有任何响应.好比

public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                while (true) {
//                    if (Thread.currentThread().isInterrupted()) {
//                        System.out.println("Interruted!");
//                        break;
//                    }
                    Thread.yield();
                }
            }
        };
        t1.start();
        Thread.sleep(2000);
        t1.interrupt();
    }
}

注释掉该段代码,程序将永续运行,并不会产生任何中断.

中断通知可让sleep()方法在休眠时产生中断异常,捕获这个异常能够手动让线程产生中断.

public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread() {
            @Override
            public void run() {
                while (true) {
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("Interruted!");
                        break;
                    }
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        System.out.println("Interruted When Sleep");
                        Thread.currentThread().interrupt();
                    }
                    Thread.yield();
                }
            }
        };
        t1.start();
        Thread.sleep(2000);
        t1.interrupt();
    }
}

运行结果

Interruted When Sleep
Interruted!

挂起和继续执行

suspend()和resume()方法,一旦resume()方法在suspend()方法以前执行,将永远被挂起,没法释放锁,程序被堵塞,这是极度危险的,不要在生产环境中使用这两个方法.

public class BadSuspend {
    public static Object u = new Object();
    static Thread t1 = new Thread(new ChangeObjectThreed(),"t1");
    static Thread t2 = new Thread(new ChangeObjectThreed(),"t2");
    public static class ChangeObjectThreed implements Runnable {
        public void run() {
            synchronized (u) {
                System.out.println("in " + Thread.currentThread().getName());
                Thread.currentThread().suspend();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        t1.start();
        Thread.sleep(100);
        t2.start();
        t1.resume();
        t2.resume();
        t1.join();
        t2.join();
    }
}

运行结果(程序堵塞,没法结束)

in t1
in t2

结果解读:t1在得到监视器锁以后被挂起,t2没法拿到锁,t1.resume();t2.resume();执行后,t1能够继续执行,释放锁,t2拿到锁,被挂起,因为t2.resume()已经执行过了,t2没法继续执行,没法释放锁,致使程序出现及严重的错误.

修改方法,不管是否被挂起,等待,都不能影响锁的释放.

public class GoodSuspend {
    public static Object u = new Object();
    public static volatile boolean suspendme = false;
    public static class ChangeObjectThread extends Thread {
        public ChangeObjectThread(Runnable changeObjectRunnable) {
            super(changeObjectRunnable);
        }

        public void suspendMe() {
            suspendme = true;
        }
        public void resumeMe() {
            suspendme = false;
            synchronized (this) {
                notify();
            }
        }
    }
    public static class ChangeObjectRunnable implements Runnable {
        public void run() {
            while (true) {
                synchronized (Thread.currentThread()) {
                    while (suspendme) {
                        try {
                            Thread.currentThread().wait();
                        }catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                synchronized (u) {
                    System.out.println("in ChangeObjectThread");
                }
                Thread.yield();
            }
        }
    }
    public static class ReadObjectRunnable implements Runnable {

        public void run() {
            while (true) {
                synchronized (u) {
                    System.out.println("in ReadObjectThread");
                }
                Thread.yield();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ChangeObjectThread t1 = new ChangeObjectThread(new ChangeObjectRunnable());
        Thread t2 = new Thread(new ReadObjectRunnable());
        t1.start();
        t2.start();
        Thread.sleep(1000);
        t1.suspendMe();
        System.out.println("suspend t1 2 sec");
        Thread.sleep(2000);
        System.out.println("resume t1");
        t1.resumeMe();
    }
}

运行结果(截取部分)

结果解读:两个线程启动时,会并行执行,会出现相似于这种

in ChangeObjectThread
in ReadObjectThread
in ChangeObjectThread
in ReadObjectThread
in ChangeObjectThread
in ReadObjectThread
in ChangeObjectThread
in ReadObjectThread
in ChangeObjectThread
in ReadObjectThread
in ChangeObjectThread
in ReadObjectThread

中间会有2秒钟,所有都是读线程的,相似于这种

in ReadObjectThread
in ReadObjectThread
in ReadObjectThread
in ReadObjectThread
in ReadObjectThread
in ReadObjectThread
in ReadObjectThread
in ReadObjectThread
in ReadObjectThread
in ReadObjectThread
in ReadObjectThread
in ReadObjectThread
in ReadObjectThread
in ReadObjectThread

这是由于t1被等待wait()了2秒,而后被唤醒notify(),以后就是并行执行了.其实这样写的好处就是即使t1未被唤醒,也不会占用锁而不释放.

线程阻塞工具类

LockSupport弥补了suspend()方法的不足,resume()在前发生,致使线程没法继续执行的状况,与Object.wait()比,不须要先得到对象锁.同时支持中断通知处理.

public class LockSupportDemo {
    public static Object u = new Object();
    static Thread t1 = new Thread(new ChangeObjectThreed(),"t1");
    static Thread t2 = new Thread(new ChangeObjectThreed(),"t2");
    public static class ChangeObjectThreed implements Runnable {
        public void run() {
            synchronized (u) {
                System.out.println("in " + Thread.currentThread().getName());
                LockSupport.park();
                if (Thread.interrupted()) {
                    System.out.println(Thread.currentThread().getName() + "被中断了");
                }
            }
            System.out.println(Thread.currentThread().getName() + "执行结束!");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        t1.start();
        Thread.sleep(100);
        t2.start();
//        t1.interrupt();
        LockSupport.unpark(t1);
        LockSupport.unpark(t2);
        t1.join();
        t2.join();
    }
}

运行结果:(运行结束,无堵塞)

in t1
t1执行结束!
in t2
t2执行结束!

这里不管LockSupport.unpark(t2)先执行仍是后执行,都会给LockSupport.park()一个许可,让挂起能够继续执行.不会进行堵塞.若是有锁则会释放锁.

public class LockSupportDemo {
    public static Object u = new Object();
    static Thread t1 = new Thread(new ChangeObjectThreed(),"t1");
    static Thread t2 = new Thread(new ChangeObjectThreed(),"t2");
    public static class ChangeObjectThreed implements Runnable {
        public void run() {
            synchronized (u) {
                System.out.println("in " + Thread.currentThread().getName());
                LockSupport.park();
                if (Thread.interrupted()) {
                    System.out.println(Thread.currentThread().getName() + "被中断了");
                }
            }
            System.out.println(Thread.currentThread().getName() + "执行结束!");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        t1.start();
        Thread.sleep(100);
        t2.start();
        t1.interrupt();
//        LockSupport.unpark(t1);
        LockSupport.unpark(t2);
        t1.join();
        t2.join();
    }
}

运行结果:

in t1
t1被中断了
t1执行结束!
in t2
t2执行结束!

当LockSupport.park()时有中断通知,当有Thread.interrupted()的中断标识时,会进入中断处理.