70.原子变量和CAS

1.原子变量基于CAS(比较并交换、可看做一条指令)内部使用volatile:
  //如果当前值等于expect,则更新为update,否则不更新,如果更新成功,返回true,否则返回false
  public final boolean compareAndSet(int expect, int update)
  
    public final int incrementAndGet() {
        for (;;) {//无线循环
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))//如果current没有变就会return next,否则重试
                return next;
        }
    }
2.synchronized(悲观锁-认为会冲突)代表一种阻塞式算法,得不到锁的时候,进入锁等待队列,等待其他线程唤醒,有上下文切换开销。
  原子变量(乐观-认为冲突较少)的更新逻辑是非阻塞式的,更新冲突的时候,它就重试,不会阻塞,不会有上下文切换开销
3.使用场景:并发环境中的计数(AtomicInteger)、产生序列号(AtomicLong)
  1. 显示锁
1.显式锁接口Lock:
    public interface Lock {
        void lock();//就是普通的获取锁和释放锁方法,lock()会阻塞直到成功
        void lockInterruptibly() throws InterruptedException;//与lock()的不同是,可以响应中断
        boolean tryLock();//只是尝试获取锁,立即返回,不阻塞,如果获取成功,返回true,否则返回false
        boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//尝试获取锁,如果成功则立即返回true,
                             否则阻塞等待time时长,等待期间可以响应中断。如果在等待的时间内获得了锁,返回true,否则返回false
        void unlock();//就是普通的获取锁和释放锁方法,lock()会阻塞直到成功
        Condition newCondition();//新建一个条件,一个Lock可以关联多个条件
    }
2.相比synchronized,显式锁支持以非阻塞方式获取锁、可以响应中断、可以限时,可以指定公平性、可以解决死锁问题
3.Lock接口的主要实现类是ReentrantLock,它的基本用法lock/unlock实现了与synchronized一样的语义,包括:
    a.可重入,一个线程在持有一个锁的前提下,可以继续获得该锁
    b.可以解决竞态条件问题
    c.可以保证内存可见性
  一般使用方法:
    public class Counter {
        private final Lock lock = new ReentrantLock();
        private volatile int count;
        public void incr() {
            lock.lock();
            try {
                count++;
            } finally {
                lock.unlock();
            }
        }       
        public int getCount() {
            return count;
        }
    }
4.使用tryLock避免死锁。
    A获得了a锁,需要申请b锁,与此同时,B获得了b锁,需要申请a锁,这样就会死锁(使用lock的方式),
    如果使用tryLock则可以避免,因为A如果申请不到b锁会释放a锁返回失败,B也一样。应该这么使用:如果失败则重试
    boolean success = false;
    do {
    success = tryTransfer(from, to, money);//内部使用tryLock获取锁
    if (!success) {
        Thread.yield();
    }
} while (!success);
5.获取锁信息:
    public boolean isLocked()//锁是否被持有,只要有线程持有就返回true,不一定是当前线程持有
    public boolean isHeldByCurrentThread()//锁是否被当前线程持有
    public int getHoldCount()//锁被当前线程持有的数量,0表示不被当前线程持有
    public final boolean isFair()//锁等待策略是否公平
    public final boolean hasQueuedThreads()//是否有线程在等待该锁
    public final boolean hasQueuedThread(Thread thread)//指定的线程thread是否在等待该锁
    public final int getQueueLength()//在等待该锁的线程个数
6. LockSupport
LockSupport.park()使当前线程放弃CPU,进入等待状态(WAITING),其他线程对它调用了LockSupport.unpark(Thread t),则该线程才会恢复运行状态
park不同于Thread.yield(),yield只是告诉操作系统可以先让其他线程运行,但自己依然是可运行状态,而park会放弃调度资格,使线程进入WAITING状态
park是响应中断的

72.显示条件(Condition)

1.锁用于解决竞态条件问题,条件是线程间的协作机制。显式锁与synchronzied相对应,而显式条件与wait/notify相对应。
  wait/notify与synchronized配合使用,显式条件与显式锁配合使用。
2.Condition表示条件变量,是一个接口:
    public interface Condition {
      void await() throws InterruptedException;//类似wait()
      void awaitUninterruptibly();//不响应中断,如果等待过程中发生了中断,中断标志位会被设置
      long awaitNanos(long nanosTimeout) throws InterruptedException;//响应中断,如果发生了中断,中断标志位会被清空
      boolean await(long time, TimeUnit unit) throws InterruptedException;//响应中断,如果发生了中断,中断标志位会被清空
      boolean awaitUntil(Date deadline) throws InterruptedException;//响应中断,这是绝对时间,如果发生了中断,中断标志位会被清空
      void signal();//类似notify()
      void signalAll();//类似notifyAll()
    }
3.一般使用:
    public class WaitThread extends Thread {
        private volatile boolean fire = false;
        private Lock lock = new ReentrantLock();
        private Condition condition = lock.newCondition();
    
        @Override
        public void run() {
            try {
                lock.lock();
                try {
                    while (!fire) {
                        condition.await();
                    }
                } finally {
                    lock.unlock();
                }
                System.out.println("fired");
            } catch (InterruptedException e) {
                Thread.interrupted();
            }
        }
    
        public void fire() {
            lock.lock();
            try {
                this.fire = true;
                condition.signal();
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            WaitThread waitThread = new WaitThread();
            waitThread.start();
            Thread.sleep(1000);
            System.out.println("fire");
            waitThread.fire();
        }
}

73.并发容器 - 写时拷贝的List和Set

1. CopyOnWriteArrayList,1.8前,CopyOnWriteArrayList的迭代器不支持修改操作,也不支持一些依赖迭代器修改方法的操作,比如Collections的sort方法
2.CopyOnWriteArrayList直接支持两个原子方法:
    public boolean addIfAbsent(E e) //不存在才添加,如果添加了,返回true,否则返回false
    public int addAllAbsent(Collection<? extends E> c) //批量添加c中的非重复元素,不存在才添加,返回实际添加的个数
3. 内部实现是volitile数组+ReentrantLock。每次修改都创建一个新数组,然后复制所有内容。读不需要锁,可以并行,读和写也可以并行,但多个线程不能同时写
4.CopyOnWriteArrayList不适用于数组很大,且修改频繁的场景。它是以优化读操作为目标的,读不需要同步,性能很高,但在优化读的同时就牺牲了写的性能(读远多于写、集合不太大的场合)
5.保证线程安全的思路:
      a.加锁
      b.CAS
      c.写时拷贝

77.异步任务执行服务

1.Runnable:无返回值、不会抛异常
  Callable:有返回值、会抛异常
2. Executor:执行一个Runnable,接口没有限定任务如何执行,可能是创建一个新线程,可能是复用线程池中的某个线程,也可能是在调用者线程中执行
      public interface Executor {
          void execute(Runnable command);
      }
3. ExecutorService:
    public interface ExecutorService extends Executor {
        <T> Future<T> submit(Callable<T> task);
        <T> Future<T> submit(Runnable task, T result);
        Future<?> submit(Runnable task);
        void shutdown();//不再接受新任务,但已提交的任务会继续执行(未开始的任务也会)
        List<Runnable> shutdownNow();//不接受新任务,已提交但未执行的任务会被终止,对于正在执行的任务,调用线程的interrupt方法,返回已提交但尚未执行的任务列表
        boolean isShutdown();
        boolean isTerminated();
        //shutdown和shutdownNow不会阻塞等待,返回后不代表所有任务已结束,但isShutdown返回true
       //awaitTermination等待所有任务结束,可限定等待时间,如果超时前所有任务都结束,即isTerminated方法返回true,则返回true,否则返回false。
        boolean awaitTermination(long timeout, TimeUnit unit)throws InterruptedException;
        //等待所有任务完成,返回的Future列表(每个Future的isDone是true)
        <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)throws InterruptedException;
      //指定等待时间,如果超时后有的任务没完成,就会被取消
        <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)throws InterruptedException;
      //只要有一个任务在限时内成功返回了,它就会返回该任务的结果,其他任务会被取消
        <T> T invokeAny(Collection<? extends Callable<T>> tasks)throws InterruptedException, ExecutionException;
      //如果没有任务能在限时内成功返回,抛出TimeoutException,如果限时内所有任务都结束了,但都发生了异常,抛出ExecutionException
        <T> T invokeAny(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;
    }
  
4.Future:
    public interface Future<V> {
        boolean cancel(boolean mayInterruptIfRunning);//取消异步任务。
            1.已完成、或已取消、或不能取消返回false,否则返回true
            2.如果任务还未开始,则不再运行
            3.如果任务已经在运行,则不一定能取消(mayInterruptIfRunning表示,如果任务正在执行,是否调用interrupt方法(不一定会中断线程))
        boolean isCancelled();//是否取消。只要cancel方法返回了true,随后的isCancelled方法都会返回true,即使执行任务的线程还未真正结束
        boolean isDone();//是否完成。任务正常结束、可能抛出异常、也可能任务被取消
        V get() throws InterruptedException, ExecutionException;//阻塞等待直到有结果
        V get(long timeout, TimeUnit unit) throws InterruptedException, 
            ExecutionException, TimeoutException;//等待指定时间,超时会抛出TimeoutException
     get的结果:
        1.正常完成,get方法会返回其执行结果,如果任务是Runnable且没有提供结果,返回null
        2.任务执行抛出了异常,get方法会将异常包装为ExecutionException重新抛出,通过异常的getCause方法可以获取原异常
        3.任务被取消了,get方法会抛出异常CancellationException
        4.如果调用get方法的线程被中断了,get方法会抛出InterruptedException
    }       
5.内部使用FutureTask

78.线程池

1. ThreadPoolExecutor构造方法
  public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue)
  a.有新任务到来的时候,如果当前线程个数小于corePoolSiz,就会创建一个新线程来执行该任务(即使其他核心线程是空闲的也会创建)
  b.如果线程个数大于等于corePoolSiz,不会立即创建新线程,先进队列workQueue,如果队列满了或其他原因不能立即入队,
    它就不会排队,而是检查线程个数是否达到了maximumPoolSize,如果没有,就会继续创建线程,直到线程数达到maximumPoolSize
    空闲的核心线程会从队列里面取任务执行
  c. keepAliveTime是非核心线程能空闲等待的时间,超时就被回收
  d.ThreadPoolExecutor要求的队列类型是阻塞队列BlockingQueue
        -LinkedBlockingQueue:基于链表的阻塞队列,可以指定最大长度,但默认是无界的
        -ArrayBlockingQueue:基于数组的有界阻塞队列
        -PriorityBlockingQueue:基于堆的无界阻塞优先级队列
        -SynchronousQueue:没有实际存储空间的同步阻塞队列(当尝试排队时,只有正好有空闲线程在等待接受任务时,才会入队成功,否则,总是会创建新线程,直到达到maximumPoolSize)
  e.任务拒绝策略
      ThreadPoolExecutor.AbortPolicy:这就是默认的方式,抛出异常
      ThreadPoolExecutor.DiscardPolicy:静默处理,忽略新任务,不抛异常,也不执行
      ThreadPoolExecutor.DiscardOldestPolicy:将等待时间最长的任务扔掉,然后自己排队
      ThreadPoolExecutor.CallerRunsPolicy:在任务提交者线程中执行任务,而不是交给线程池中的线程执行
  f.如果需要自定义一些线程的属性,比如名称,可以实现自定义的ThreadFactory
  g.核心线程不会预先创建(prestartAllCoreThreads()会预先创建),只有当有任务时才会创建
    核心线程不会因为空闲而被终止,keepAliveTime参数不适用于它(allowCoreThreadTimeOut,如果参数为true,则keepAliveTime参数也适用于核心线程)
  h.内置创建线程池:
    public static ExecutorService newSingleThreadExecutor() {
       //所有任务被顺序执行,无界队列LinkedBlockingQueue,线程创建后不会超时终止,如果排队任务过多,可能会消耗非常大的内存
        return new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
    }   
    public static ExecutorService newFixedThreadPool(int nThreads) {
      //固定数目的n个线程
        return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
    }
    public static ExecutorService newCachedThreadPool() {
      //新任务到来,如果有空闲线程在等待任务,则线程接受任务,否则创建新线程,创建的总线程个数不受限制,对空闲线程,60秒内没有新任务,就终止
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
    }
  i.线程池死锁:任务A,在它的执行过程中,线程池提交了一个任务B,但需要等待任务B结束,此时如果B在等待队列,就会死锁
    解决方法:1.使用newCachedThreadPool(让创建线程不再受限)
            2.使用SynchronousQueue。
                  对于普通队列,入队只是把任务放到了队列中;
                  对于SynchronousQueue来说,入队成功就意味着已有线程接受处理,如果入队失败,可以创建更多线程直到maximumPoolSize,如果达到了maximumPoolSize,会触发拒绝机制

79.方便的CompletionService

1. 实现类ExecutorCompletionService,主线程提交多个异步任务,然后希望有任务完成就处理结果,并且按任务完成顺序逐个处理

80.定时任务的那些坑

1.固定延时:基于上次任务的"实际"执行时间来算的,如果由于某种原因,上次任务延时了,则本次任务也会延时
2.固定频率会尽量补够运行次数(任务被延迟后,可能会立即执行多次,将次数补够)
3.一个Timer对象只有一个Timer线程,上一个任务的执行完毕后才能够执行下一个任务
4.在执行任何一个任务的run方法时,一旦run抛出异常,Timer线程就会退出,从而所有定时任务都会被取消
5. ScheduledExecutorService:任务队列是一个无界的优先级队列,所以最大线程数对它没有作用,即使corePoolSize设为0,它会至少运行一个线程(总是会保留至少一个线程以监控定时任务啊),对于以上四点ScheduledExecutorService会没有这些坑

81.并发同步协作工具

1、读写锁ReentrantReadWriteLock:只有"读-读"操作是可以并行的,"读-写"和"写-写"都不可以(在读多写少的场景中使用)
    public class MyCache {
        private Map<String, Object> map = new HashMap<>();
        private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        private Lock readLock = readWriteLock.readLock();
        private Lock writeLock = readWriteLock.writeLock();

        public Object get(String key) {
            readLock.lock();
            try {
                return map.get(key);
            } finally {
                readLock.unlock();
            }
        }

        public Object put(String key, Object value) {
            writeLock.lock();
            try {
                return map.put(key, value);
            } finally {
                writeLock.unlock();
            }
        }

        public void clear() {
            writeLock.lock();
            try {
                map.clear();
            } finally {
                writeLock.unlock();
            }
        }
    }
2、信号量Semaphore:限制对资源的并发访问数eg:限制并发访问的用户数不超过100
    public class AccessControlService {
        public static class ConcurrentLimitException extends RuntimeException {
            private static final long serialVersionUID = 1L;
        }

        private static final int MAX_PERMITS = 100;
        private Semaphore permits = new Semaphore(MAX_PERMITS, true);

        public boolean login(String name, String password) {
            if (!permits.tryAcquire()) {//尝试获取许可
                // 同时登录用户数超过限制
                throw new ConcurrentLimitException();
            }
            // ..其他验证
            return true;
        }

        public void logout(String name) {
            permits.release();//释放许可
        }
    }
  如果我们将permits的值设为1,它与一般的锁是不同的:一般锁只能由持有锁的线程释放,而Semaphore表示的只是一个许可数,任意线程都可以调用其release方法
3、倒计时门栓CountDownLatch
    a.同时开始:
    public class RacerWithCountDownLatch {
        static class Racer extends Thread {
            CountDownLatch latch;

            public Racer(CountDownLatch latch) {
                this.latch = latch;
            }

            @Override
            public void run() {
                try {
                    this.latch.await();
                    System.out.println(getName()
                            + " start run "+System.currentTimeMillis());
                } catch (InterruptedException e) {
                }
            }
        }

        public static void main(String[] args) throws InterruptedException {
            int num = 10;
            CountDownLatch latch = new CountDownLatch(1);
            Thread[] racers = new Thread[num];
            for (int i = 0; i < num; i++) {
                racers[i] = new Racer(latch);
                racers[i].start();
            }
            Thread.sleep(1000);
            latch.countDown();
        }
    }
  b.主从协作(主线程依赖工作线程完成)
    public class MasterWorkerDemo {
        static class Worker extends Thread {
            CountDownLatch latch;

            public Worker(CountDownLatch latch) {
                this.latch = latch;
            }

            @Override
            public void run() {
                try {
                    // simulate working on task
                    Thread.sleep((int) (Math.random() * 1000));

                    // simulate exception
                    if (Math.random() < 0.02) {
                        throw new RuntimeException("bad luck");
                    }
                } catch (InterruptedException e) {
                } finally {
                    this.latch.countDown();
                }
            }
        }

        public static void main(String[] args) throws InterruptedException {
            int workerNum = 100;
            CountDownLatch latch = new CountDownLatch(workerNum);
            Worker[] workers = new Worker[workerNum];
            for (int i = 0; i < workerNum; i++) {
                workers[i] = new Worker(latch);
                workers[i].start();
            }
            latch.await();
            System.out.println("collect worker results");
        }
    }
4、循环栅栏CyclicBarrier :所有线程在到达该栅栏后都需要等待其他线程,等所有线程都到达后再一起通过
    public class CyclicBarrierDemo {
        static class Tourist extends Thread {
            CyclicBarrier barrier;

            public Tourist(CyclicBarrier barrier) {
                this.barrier = barrier;
            }

            @Override
            public void run() {
                try {
                    // 模拟先各自独立运行
                    Thread.sleep((int) (Math.random() * 1000));

                    // 集合点A
                    barrier.await();

                    System.out.println(this.getName() + " arrived A "
                            + System.currentTimeMillis());

                    // 集合后模拟再各自独立运行
                    Thread.sleep((int) (Math.random() * 1000));

                    // 集合点B
                    barrier.await();
                    System.out.println(this.getName() + " arrived B "
                            + System.currentTimeMillis());
                } catch (InterruptedException e) {
                } catch (BrokenBarrierException e) {
                }
            }
        }

        public static void main(String[] args) {
            int num = 3;
            Tourist[] threads = new Tourist[num];
            CyclicBarrier barrier = new CyclicBarrier(num, new Runnable() {

                @Override
                public void run() {
                    System.out.println("all arrived " + System.currentTimeMillis()
                            + " executed by " + Thread.currentThread().getName());
                }
            });
            for (int i = 0; i < num; i++) {
                threads[i] = new Tourist(barrier);
                threads[i].start();
            }
        }
    }

82.理解ThreadLocal

1.每个线程对与ThreadLocal变量都有一个拷贝
    public class ThreadLocalBasic {
        static ThreadLocal<Integer> local = new ThreadLocal<>();

        public static void main(String[] args) throws InterruptedException {
            Thread child = new Thread() {
                @Override
                public void run() {
                    System.out.println("child thread initial: " + local.get());
                    local.set(200);
                    System.out.println("child thread final: " + local.get());
                }
            };
            local.set(100);
            child.start();
            child.join();
            System.out.println("main thread final: " + local.get());
        }
    }
  输出:
    child thread initial: null
    child thread final: 200
    main thread final: 100
  这说明,main线程对local变量的设置对child线程不起作用,child线程对local变量的改变也不会影响main线程,
  它们访问的虽然是同一个变量local,但每个线程都有自己的独立的值,这就是线程本地变量的含义
2.initialValue()、remove()
    public class ThreadLocalInit {
        static ThreadLocal<Integer> local = new ThreadLocal<Integer>(){
            @Override
            protected Integer initialValue() {
                return 100;
            }
        };
        public static void main(String[] args) {
            System.out.println(local.get());//输出100
            local.set(200);
            local.remove();//remove之后再调用get会重新调用initialValue
            System.out.println(local.get());//输出100
        }
    }
3.使用场景:
      a.DateFormat/SimpleDateFormat:(ThreadLocal对象一般都定义为static,以便于引用)
    public class ThreadLocalDateFormat {
        static ThreadLocal<DateFormat> sdf = new ThreadLocal<DateFormat>() {
            @Override
            protected DateFormat initialValue() {
                return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            }
        };
        public static String date2String(Date date) {
            return sdf.get().format(date);
        }
        public static Date string2Date(String str) throws ParseException {
            return sdf.get().parse(str);
        }
    }
    b. ThreadLocalRandom:Random是线程安全的,但如果并发访问竞争激烈的话,性能会下降,所以Java并发包提供了类ThreadLocalRandom
    c.上下文信息:
    public class RequestContext {
        public static class Request { //...
        };

        private static ThreadLocal<String> localUserId = new ThreadLocal<>();
        private static ThreadLocal<Request> localRequest = new ThreadLocal<>();

        public static String getCurrentUserId() {
            return localUserId.get();
        }

        public static void setCurrentUserId(String userId) {
            localUserId.set(userId);
        }

        public static Request getCurrentRequest() {
            return localRequest.get();
        }

        public static void setCurrentRequest(Request request) {
            localRequest.set(request);
        }
    }
4.原理:每个线程都有一个Map,类型为ThreadLocalMap,对于每个ThreadLocal对象,调用其get/set实际上就是以ThreadLocal对象为键读写当前线程的Map
5.线程池与ThreadLocal:由于线程池会复用线程,在没有处理的情况下,一个线程执行完一个任务如果修改了ThreadLocal对象的值,会被带到下一个任务去:
    public class ThreadPoolProblem {
        static ThreadLocal<AtomicInteger> sequencer = new ThreadLocal<AtomicInteger>() {

            @Override
            protected AtomicInteger initialValue() {
                return new AtomicInteger(0);
            }
        };

        static class Task implements Runnable {

            @Override
            public void run() {
                AtomicInteger s = sequencer.get();
                int initial = s.getAndIncrement();
                // 期望初始为0
                System.out.println(initial);
            }
        }

        public static void main(String[] args) {
            ExecutorService executor = Executors.newFixedThreadPool(2);//两个线程工作
            executor.execute(new Task());
            executor.execute(new Task());
            executor.execute(new Task());
            executor.shutdown();
        }
    }
  输出:
      0
      0  
      1//线程池中的线程在执行完一个任务,执行下一个任务时,其中的ThreadLocal对象并不会被清空,所以输出1
  解决方法:
    1.第一次使用ThreadLocal对象时,总是先调用set设置初始值,或者如果ThreaLocal重写了initialValue方法,先调用remove
    static class Task implements Runnable {
        @Override
        public void run() {
            sequencer.set(new AtomicInteger(0));
            //或者 sequencer.remove();
            AtomicInteger s = sequencer.get();
            //...
        }
    }
    2.使用完ThreadLocal对象后,总是调用其remove方法
    static class Task implements Runnable {
        @Override
        public void run() {
            try{
                AtomicInteger s = sequencer.get();
                int initial = s.getAndIncrement();
                // 期望初始为0
                System.out.println(initial);    
            }finally{
                sequencer.remove();
            }
        }
    }
    3.使用自定义的线程池
    static class MyThreadPool extends ThreadPoolExecutor {
        public MyThreadPool(int corePoolSize, int maximumPoolSize,
                long keepAliveTime, TimeUnit unit,
                BlockingQueue<Runnable> workQueue) {
            super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
        }

        @Override//每个线程在执行前会被调用
        protected void beforeExecute(Thread t, Runnable r) {
            try {
                //使用反射清空所有ThreadLocal
                Field f = t.getClass().getDeclaredField("threadLocals");
                f.setAccessible(true);
                f.set(t, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
            super.beforeExecute(t, r);
        }
    }

83.并发总结

1.线程安全的机制
    (1) synchronized(隐式锁、悲观锁):
            既可以解决竞态条件问题,也可以解决内存可见性问题
            需要注意,它不能尝试获取锁,也不响应中断,还可能会死锁。不过,相比显式锁,synchronized简单易用,JVM也可以不断优化它的实现,应该被优先使用
    (2)使用显式锁(乐观锁)
            可以实现synchronzied同样的功能,但需要程序员自己创建锁,调用锁相关的接口,主要接口是Lock,主要实现类是ReentrantLock
            支持以非阻塞方式获取锁、可以响应中断、可以限时、可以指定公平性、可以解决死锁问题
            在读多写少、读操作可以完全并行的场景中,可以使用读写锁以提高并发度,读写锁的接口是ReadWriteLock,实现类是ReentrantReadWriteLock
    (3)使用volatile
            synchronized和显式锁都是锁,使用锁可以实现安全,但使用锁是有成本的,获取不到锁的线程还需要等待,会有线程的上下文切换开销等
            保证安全不一定需要锁。如果共享的对象只有一个,操作也只是get/set操作,set不依赖于之前的值,那就不存在竞态条件问题,只有内存可见性问题,这时使用volatile就可以了
    (4)使用原子变量和CAS
            使用volatile,set的新值不能依赖于旧值,但很多时候,set的新值与原来的值有关,这时,也不一定需要锁,如果需要同步的代码比较简单,可以考虑原子变量
            原子变量的基础是CAS,比较并设置,非阻塞式的
    (5)写时复制(CopyOnWriteArrayList)
            之所以会有线程安全的问题,是因为多个线程并发读写同一个对象,如果每个线程读写的对象都是不同的,或者,如果共享访问的对象是只读的,不能修改,那也就不存在线程安全问题了
            原理:写时复制就是将共享访问的对象变为只读的,写的时候,再使用锁,保证只有一个线程写,写的线程不是直接修改原对象,而是新创建一个对象,对该对象修改完毕后,再原子性地修改共享访问的变量,让它指向新的对象
    (6)使用ThreadLocal
            每个线程,对同一个变量,都有自己的独有拷贝,每个线程实际访问的对象都是自己的
2.线程的协作机制
    (1)wait/notify
            wait/notify与synchronized配合一起使用
            每个对象都有一把锁和两个等待队列:
                a.锁等待队列:存放等待获取锁的线程                
                b.条件等待队列:存放等待条件的线程
            wait()将自己加入条件等待队列,notify()从条件等待队列上移除一个线程并唤醒,notifyAll移除所有线程并唤醒
            注意:
                wait/notify方法只能在synchronized代码块内被调用
                调用wait时,线程会释放对象锁,被notify/notifyAll唤醒后,要重新竞争对象锁,获取到锁后才会从wait调用中返回,返回后,不代表其等待的条件就一定成立了,需要重新检查其等待的条件
                    synchronized (obj) {
                        while (条件不成立)
                            obj.wait();
                        ... // 执行条件满足后的操作
                    }
                wait/notify与一个共享的条件变量有关,这个条件变量是程序自己维护的,当条件不成立时,线程调用wait进入条件等待队列,另一个线程修改了条件变量后调用notify,调用wait的线程唤醒后需要重新检查条件变量
    (2)显式条件
            显式条件与显式锁配合使用
            与wait/notify相比,可以支持多个条件队列,代码更为易读,效率更高,使用时注意不要将signal/signalAll误写为notify/notifyAll
    (3)线程的中断
            Java中取消/关闭一个线程的方式是中断,中断并不是强迫终止一个线程,它是一种协作机制,是给线程传递一个取消信号,但是由线程来决定如何以及何时退出,线程在不同状态和IO操作时对中断有不同的反应,
            作为线程的实现者,应该提供明确的取消/关闭方法,并用文档清楚描述其行为,作为线程的调用者,应该使用其取消/关闭方法,而不是贸然调用interrupt
    (4)协作工具类
            信号量类Semaphore用于限制对资源的并发访问数
            倒计时门栓CountDownLatch主要用于不同角色线程间的同步(让多个线程同时开始、主线程等待多个线程结束)
            循环栅栏CyclicBarrier用于同一角色线程间的协调一致,所有线程在到达栅栏后都需要等待其他线程,等所有线程都到达后再一起通过,它是循环的,可以用作重复的同步
    (5)阻塞队列
            阻塞队列封装了锁和条件,不需要考虑同步和协作问题
    (6)Future/FutureTask
            异步执行任务(ExecutorService)提交任务后马上得到一个结果,但这个结果不是最终结果,而是一个Future,Future是一个接口,主要实现类是FutureTask
            Future封装了主线程和执行线程关于执行状态和结果的同步,对于主线程而言,它只需要通过Future就可以查询异步任务的状态、获取最终结果、取消任务等,不需要再考虑同步和协作问题
3.容器类
    (1)同步容器:
            Collections类中有一些静态方法,可以基于普通容器返回线程安全的同步容器
            给所有容器方法都加上synchronized来实现安全,性能比较低
    (2)并发容器:
            线程安全、并发度更高、性能更高、迭代不会抛出ConcurrentModificationException、很多容器以原子方式支持一些复合操作
            a.写时拷贝的List和Set:采用了写时拷贝,适用于读远多于写,集合不太大的场合
            b.ConcurrentHashMap:分段锁和其他技术实现了高并发,读操作完全并行,写操作支持一定程度的并行,以原子方式支持一些复合操作,迭代不用加锁,不会抛出ConcurrentModificationException
            c.基于SkipList的Map和Set:TreeMap/TreeSet对应的并发版本是ConcurrentSkipListMap和ConcurrentSkipListSet,没有使用锁,所有操作都是无阻塞的,所有操作都可以并行,包括写
            d.各种队列:各种阻塞队列主要用于协作,非阻塞队列适用于多个线程并发使用一个队列的场合(ConcurrentLinkedQueue和ConcurrentLinkedDeque、无界,这两个类最基础的实现原理是循环CAS,没有使用锁)
4.任务执行服务
    Runnable和Callable:表示要执行的异步任务
    Executor和ExecutorService:表示执行服务
    Future:表示异步任务的结果
    线程池ThreadPoolExecutor实现了生产者/消费者模式,工作者线程就是消费者,任务提交者就是生产者,线程池自己维护任务队列。当我们碰到类似生产者/消费者问题时,应该优先考虑直接使用线程池

84.反射

1.每个已加载的类在内存都有一份类信息(java.lang.Class),每个对象都有指向它所属类信息的引用
2.获取Class对象:
    (1)实例对象.getClass()
    (2)类名.class、基本数据类型.class 、void.class:
            Class<Date> cls = Date.class;
            Class<Integer> intCls = int.class;
            Class<Byte> byteCls = byte.class;
            Class<Void> voidCls = void.class;
         对于数组,每种类型都有对应数组类型的Class对象,每个维度都有一个,即一维数组有一个,二维数组有一个不同的:
            String[] strArr = new String[10];
            int[][] twoDimArr = new int[3][2];
            int[] oneDimArr = new int[10];
            Class<? extends String[]> strArrCls = strArr.getClass();
            Class<? extends int[][]> twoDimArrCls = twoDimArr.getClass();
            Class<? extends int[]> oneDimArrCls = oneDimArr.getClass();
    (3)Class.forName
3.Class信息
    getName():返回Java内部使用的真正的名字 ,前缀[表示数组,有几个[表示是几维数组,数组的类型用一个字符表示,I表示int,L表示类或接口,
              其他: boolean(Z), byte(B), char(C), double(D), float(F), long(J), short(S),对于引用类型的数组,注意最后有一个分号";"
    getSimpleName():返回不带包信息
    getCanonicalName():返回的名字更为友好
image.png
    public Field[] getFields() :返回所有的public字段,包括其父类的,如果没有字段,返回空数组
    public Field[] getDeclaredFields():返回本类声明的所有字段,包括非public的,但不包括父类的
    Field:
          isAccessible():当前程序是否有该字段的访问权限
          setAccessible(boolean flag):flag设为true表示忽略Java的访问检查机制,以允许读写非public的字段
          public int getModifiers():
            Field f = Student.class.getField("MAX_NAME_LEN");
            int mod = f.getModifiers();
            System.out.println(Modifier.toString(mod));
            System.out.println("isPublic: " + Modifier.isPublic(mod));
            System.out.println("isStatic: " + Modifier.isStatic(mod));
            System.out.println("isFinal: " + Modifier.isFinal(mod));
            System.out.println("isVolatile: " + Modifier.isVolatile(mod));
    Method:
              public Object invoke(Object obj, Object... args):如果是静态方法,obj可以传null
4.类型检查和转换:
    Class.isInstance(Obejct) //类似instanceof 关键字
    Class.cast(Obejct) //强转

    public static <T> T toType(Object obj, Class<T> cls){
        return cls.cast(obj);
    }
    isInstance/cast描述的都是对象和类之间的关系,Class还有一个方法,可以判断Class之间的关系:
    public native boolean isAssignableFrom(Class<?> cls)//检查参数类型cls能否赋给当前Class类型的变量
    Object.class.isAssignableFrom(String.class)//true
    String.class.isAssignableFrom(String.class)//true
    List.class.isAssignableFrom(ArrayList.class)//true
5.类的加载:
    public static Class<?> forName(String className)//相当于Class.forName(className, true, currentLoader)
    public static Class<?> forName(String name, boolean initialize, ClassLoader loader)//initialize表示加载后,是否执行类的初始化代码(如static语句块)
    这里className与Class.getName的返回值是一致的,比如,对于String数组:
    String name = "[Ljava.lang.String;";
    Class cls = Class.forName(name);
    System.out.println(cls == String[].class);/true
  需要注意的是,基本类型不支持forName方法
    getComponentType()返回数组元素的类型

85.注解

1.@Target:表示注解的目标,取值如下:(不声明默认为适用于所有类型)
    TYPE:表示类、接口(包括注解),或者枚举声明
    FIELD:字段,包括枚举常量
    METHOD:方法
    PARAMETER:方法中的参数
    CONSTRUCTOR:构造方法
    LOCAL_VARIABLE:本地变量(局部变量)
    ANNOTATION_TYPE:注解类型
    PACKAGE:包
2.@Retention:表示注解信息保留到什么时候,取值如下:(默认为CLASS)
    SOURCE:只在源代码中保留,编译器将代码编译为字节码文件后就会丢掉
    CLASS:保留到字节码文件中,但Java虚拟机将class文件加载到内存时不一定会在内存中保留
    RUNTIME:一直保留到运行时
3.定义参数
  对于public @interface SuppressWarnings {
        String[] value();
     }
  使用:@SuppressWarnings(value={"deprecation","unused"})
  当只有一个参数,且名称为value时,提供参数值时可以省略"value=",即:
      @SuppressWarnings({"deprecation","unused"})
  参数定义时可以使用default指定一个默认值(注意:String的默认值不能为null)
4.@Inherited
    注解不能继承,但可以使用@Inherited来实现:
    public class InheritDemo {
        @Inherited
        @Retention(RetentionPolicy.RUNTIME)
        static @interface Test {
        }
        @Test
        static class Base {
        }
        static class Child extends Base {
        }
        public static void main(String[] args) {
            System.out.println(Child.class.isAnnotationPresent(Test.class));//输出true
        }
    }
5.注解与反射
    public Annotation[] getAnnotations()//获取所有的注解
    public Annotation[] getDeclaredAnnotations()    //获取所有本元素上直接声明的注解,忽略inherited来的
    public <A extends Annotation> A getAnnotation(Class<A> annotationClass)//获取指定类型的注解,没有返回null
    public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)//判断是否有指定类型的注解

86.类加载机制

1.类加载的基本机制和过程
    a.类加载器:(输入是完全限定的类名,输出是Class对象)
      (1)启动类加载器(Bootstrap ClassLoader):负责加载Java的基础类,主要是<JAVA_HOME>/lib/rt.jar
      (2)扩展类加载器(Extension ClassLoader):负责加载Java的一些扩展类,一般是<JAVA_HOME>/lib/ext目录中的jar包
      (3)应用程序类加载器(Application ClassLoader):实现类AppClassLoader,它负责加载应用程序的类(也被称为系统类加载器)
    b.双亲委派:Application ClassLoader的父亲是Extension ClassLoader,Extension的父亲是Bootstrap ClassLoader,注意不是父子继承关系,而是父子委派关系,
      子ClassLoader有一个变量parent指向父ClassLoader,在子ClassLoader加载类时,一般会首先通过父ClassLoader加载
      1.判断是否已经加载过了,加载过了,直接返回Class对象,一个类只会被一个ClassLoader加载一次。
      2.如果没有被加载,先让父ClassLoader去加载,如果加载成功,返回得到的Class对象。
      3.在父ClassLoader没有加载成功的前提下,自己尝试加载类
2.ClassLoader.getSystemClassLoader():获取默认的系统类加载器
3.ClassLoader vs Class.forName:
    public static Class<?> forName(String className)//使用系统类加载器加载
    public static Class<?> forName(String name, boolean initialize, ClassLoader loader)//指定ClassLoader,参数initialize表示,加载后,是否执行类的初始化代码(如static语句块),没有指定默认为true
    ClassLoader的loadClass方法与forName方法都可以加载类,但是ClassLoader的loadClass不会执行类的初始化代码
4.自定义ClassLoader
image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,874评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,102评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,676评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,911评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,937评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,935评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,860评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,660评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,113评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,363评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,506评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,238评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,861评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,486评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,674评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,513评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,426评论 2 352

推荐阅读更多精彩内容

  • 一.冠词(3个) 1. a 一个(件/ 只……) 2. an 一个(件/ 只……) 3. the 这;这个;那;那...
    呇吉阅读 906评论 0 7
  • 1. Typical of the grassland dwellers of the continent is ...
    Irinaa阅读 594评论 1 11
  • 1. 慈悲是你最好的武器。 2. 学佛就是在学做人而已。 3. 沈默是毁谤最好的答覆。 4. 你要感谢告诉你缺点的...
    健康育儿心理阅读 539评论 0 0
  • 名言 《诗经》 1. 关关雎鸠,在河之洲;窈窕淑女,君子好逑。 2. 昔我往矣,杨柳依依;今我来思,雨雪霏霏。 3...
    7955661f2df9阅读 125评论 0 0
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,520评论 16 22