(源码地址:https://github.com/EduMoral/edu/blob/master/concurrent/src)
1, 同步方法和费同步方法是否可以同时调用
可以!同步方法需要锁,非同步方法不需要锁,既然不需要锁两个方法获取不冲突;
2,对业务写方法加锁,对业务写方法不加锁,容易产生脏读(dirtyRead)
解决脏读的方案 copyOnwrite 牺牲写性能增加读性能
3,一个同步方法调用另一个同步方法,一个线程已拥有某个对象的锁,再次申请的时候任然会得到该对象的锁,也就是说synchronized获得锁是是可重入的
子类的同步方法也可以调用父类的同步方法
4,synchronized 关键字抛出异常会释放锁
5,volatile 关键字使一个变量在多个线程之间可见
volatile不能替代synchonnized,volatile只解决了可见性,synchonnzed既解决了可见性又解决了原子性
6,比较简单的原子性操作问题可以atomInteger 等相关类
7,锁定对象如果的对象的属性发生改变锁没有改变,如果对象的引用指向两一个对象则该锁发生改变
8,不使用字符串作为锁对象,因为有可能用的框架使用的也是该字符串,字符串存在常量池中属于同一个对象,会发生死锁问题;
9,Object.wait() 会释放锁,notify()不会释放锁 使用wait时90%用while,大多数情况下使用notifyAll
10,countdownLanch 门栓
countDownLanch lanch = new CountDownLanch(5);
lanch.await()
每调用countDown() 数值减1;为0时开始执行以下代码
11, reentrantlock用于替代synchronized 由于m1锁定this,只有m1执行完毕的时候,m2才能执行
      使用reentrantlock可以完成同样的功能 需要注意的是,必须要必须要必须要手动释放锁(重要的事情说三遍)
      使用syn锁定的话如果遇到异常,jvm会自动释放锁,但是lock必须手动释放锁,因此经常在finally中进行锁的释放
      
      使用reentrantlock可以进行“尝试锁定”tryLock,这样无法锁定,或者在指定时间内无法锁定,线程可以决定是否继续等待
      
      使用ReentrantLock还可以调用lockInterruptibly方法,可以对线程interrupt方法做出响应,
      在一个线程等待锁的过程中,可以被打断
      
      ReentrantLock还可以指定为公平锁
12,面试题:写一个固定容量同步容器,拥有put和get方法,以及getCount方法,
 * 能够支持2个生产者线程以及10个消费者线程的阻塞调用
 * 
 * 使用wait和notify/notifyAll来实现
 * 
 * 使用Lock和Condition来实现
 * 对比两种方式,Condition的方式可以更加精确的指定哪些线程被唤醒
 * 
 * @author mashibing
 *
package yxxy.c_021;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyContainer2<T> {
    final private LinkedList<T> lists = new LinkedList<>();
    final private int MAX = 10; //最多10个元素
    private int count = 0;
    
    private Lock lock = new ReentrantLock();
    private Condition producer = lock.newCondition();
    private Condition consumer = lock.newCondition();
    
    public void put(T t) {
        try {
            lock.lock();
            while(lists.size() == MAX) { //想想为什么用while而不是用if?
                producer.await();
            }
            
            lists.add(t);
            ++count;
            consumer.signalAll(); //通知消费者线程进行消费
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    public T get() {
        T t = null;
        try {
            lock.lock();
            while(lists.size() == 0) {
                consumer.await();
            }
            t = lists.removeFirst();
            count --;
            producer.signalAll(); //通知生产者进行生产
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return t;
    }
    
    public static void main(String[] args) {
        MyContainer2<String> c = new MyContainer2<>();
        //启动消费者线程
        for(int i=0; i<10; i++) {
            new Thread(()->{
                for(int j=0; j<5; j++) System.out.println(c.get());
            }, "c" + i).start();
        }
        
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        //启动生产者线程
        for(int i=0; i<2; i++) {
            new Thread(()->{
                for(int j=0; j<25; j++) c.put(Thread.currentThread().getName() + " " + j);
            }, "p" + i).start();
        }
    }
}
13,面试题:写一个固定容量同步容器,拥有put和get方法,以及getCount方法,
  能够支持2个生产者线程以及10个消费者线程的阻塞调用
  
  使用wait和notify/notifyAll来实现
  
  使用Lock和Condition来实现
  对比两种方式,Condition的方式可以更加精确的指定哪些线程被唤醒
  
public class MyContainer2<T> {
    final private LinkedList<T> lists = new LinkedList<>();
    final private int MAX = 10; //最多10个元素
    private int count = 0;
    
    private Lock lock = new ReentrantLock();
    private Condition producer = lock.newCondition();
    private Condition consumer = lock.newCondition();
    
    public void put(T t) {
        try {
            lock.lock();
            while(lists.size() == MAX) { //想想为什么用while而不是用if?
                producer.await();
            }
            
            lists.add(t);
            ++count;
            consumer.signalAll(); //通知消费者线程进行消费
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    public T get() {
        T t = null;
        try {
            lock.lock();
            while(lists.size() == 0) {
                consumer.await();
            }
            t = lists.removeFirst();
            count --;
            producer.signalAll(); //通知生产者进行生产
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return t;
    }
    
    public static void main(String[] args) {
        MyContainer2<String> c = new MyContainer2<>();
        //启动消费者线程
        for(int i=0; i<10; i++) {
            new Thread(()->{
                for(int j=0; j<5; j++) System.out.println(c.get());
            }, "c" + i).start();
        }
        
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        //启动生产者线程
        for(int i=0; i<2; i++) {
            new Thread(()->{
                for(int j=0; j<25; j++) c.put(Thread.currentThread().getName() + " " + j);
            }, "p" + i).start();
        }
    }
}
13,并发容器
hashtable,collections.synchtonizedxxx 并发不太高的场景下使用
//static Queue<String> tickets = new ConcurrentLinkedQueue<>();
并发量叫高的情况下使用
currentHashMap 不排序 currentHashSet
ConcurrentSkipListMap<>(); //高并发并且排序ConcurrentSkipListSet
queue 队列
Queue<String> strs = new ConcurrentLinkedQueue<>();
        
        for(int i=0; i<10; i++) {
            strs.offer("a" + i);  //add
        }
        
        System.out.println(strs);
        
        System.out.println(strs.size());
        
        System.out.println(strs.poll()); 取后删除
        System.out.println(strs.size());
          
        System.out.println(strs.peek()); 只取不删除
        System.out.println(strs.size());
无界队列 LinkedBlockingQueue
static BlockingQueue<String> strs = new LinkedBlockingQueue<>();
        static Random r = new Random();
        public static void main(String[] args) {
            new Thread(() -> {
                for (int i = 0; i < 100; i++) {
                    try {
                        strs.put("a" + i); //如果满了,就会等待
                        TimeUnit.MILLISECONDS.sleep(r.nextInt(1000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "p1").start();
            for (int i = 0; i < 5; i++) {
                new Thread(() -> {
                    for (;;) {
                        try {
                            System.out.println(Thread.currentThread().getName() + " take -" + strs.take()); //如果空了,就会等待
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }, "c" + i).start();
            }
        }
    }
有界队列
static BlockingQueue<String> strs = new ArrayBlockingQueue<>(10);
===========================
static BlockingQueue<MyTask> tasks = new DelayQueue<>();
线程池
当洗个线程需要返回值时用callable 接口,runnable没有返回值
Executor 
    ExecutorService submit
    Callable = Runnable
    Executors 
    ThreadPool
    Future  拿到未来的返回值,调用future时会阻塞,直到任务执行结束
    fixed cached single scheduled workstealing forkjoin
    ThreadpoolExecutor
    PStreamAPI
Executors.newFixedThreadPool(5);  固定线程池
===========
ExecutorService service = Executors.newCachedThreadPool(); 可缓存线程池 没有空闲线程就新建一个线程,有空闲线程就复用。默认时间为60s,60秒后线程自动死亡
===========
ExecutorService service = Executors.newSingleThreadExecutor(); 单线程池
===========
ScheduledExecutorService service = Executors.newScheduledThreadPool(4); 周期线程 定时任务,好处:线程可以重复使用
    service.scheduleAtFixedRate(()->{
        try {
            TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName());
}, 0, 500, TimeUnit.MILLISECONDS);
=============
ExecutorService service = Executors.newWorkStealingPool(); 任务执行完自动找活干 工作窃取 CPU是几核默认启用几个线程,守护线程,实现方式是ForkJoinPool
=============
分叉合并 递归调用线程
public class T12_ForkJoinPool { 
    static int[] nums = new int[1000000];
    static final int MAX_NUM = 50000;
    static Random r = new Random();
    
    static {
        for(int i=0; i<nums.length; i++) {
            nums[i] = r.nextInt(100);
        }
        
        System.out.println(Arrays.stream(nums).sum()); //stream api 
    }
    
    /*
    static class AddTask extends RecursiveAction { 
        
        int start, end;
        
        AddTask(int s, int e) {
            start = s;
            end = e;
        }
        @Override
        protected void compute() {
            
            if(end-start <= MAX_NUM) {
                long sum = 0L;
                for(int i=start; i<end; i++) sum += nums[i];
                System.out.println("from:" + start + " to:" + end + " = " + sum);
            } else {
            
                int middle = start + (end-start)/2;
                
                AddTask subTask1 = new AddTask(start, middle);
                AddTask subTask2 = new AddTask(middle, end);
                subTask1.fork();
                subTask2.fork();
            }
            
            
        }
        
    }
    */
    
    static class AddTask extends RecursiveTask<Long> { 
        
        private static final long serialVersionUID = 1L;
        int start, end;
        
        AddTask(int s, int e) {
            start = s;
            end = e;
        }
        @Override
        protected Long compute() {
            
            if(end-start <= MAX_NUM) {
                long sum = 0L;
                for(int i=start; i<end; i++) sum += nums[i];
                return sum;
            } 
            
            int middle = start + (end-start)/2;
            
            AddTask subTask1 = new AddTask(start, middle);
            AddTask subTask2 = new AddTask(middle, end);
            subTask1.fork();
            subTask2.fork();
            
            return subTask1.join() + subTask2.join();
        }
        
    }
    
    public static void main(String[] args) throws IOException {
        ForkJoinPool fjp = new ForkJoinPool();
        AddTask task = new AddTask(0, nums.length);
        fjp.execute(task);
        long result = task.join();
        System.out.println(result);
        
        //System.in.read();
        
    }
}
=================
线程池核心类 ThreadPoolExecutor 可以实现自定义线程池
| service.scheduleAtFixedRate(()->{ | 
| try { | 
| TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000)); | 
| } catch (InterruptedException e) { | 
| e.printStackTrace(); | 
| } | 
| System.out.println(Thread.currentThread().getName()); | 
}, 0, 500, TimeUnit.MILLISECONDS);
| ScheduledExecutorService service = Executors.newScheduledThreadPool(4); 
 | 
线程池工作原理:
1,查看核心线程是否已满,如果没有创建新线程执行任务;
2,如果核心线程达到数量,则把任务丢进队列,如果队列队列没满,则等待被调用,如果队列已满则执行第三步
3,查看最大线程数是否已达到数量,如果没有则新建线程执行任务,如果达到最大线程则执行拒绝策略
线程池的拒绝策略有
1,丢掉任务,抛出异常
2,丢掉任务,不抛出异常
3,把队列里较早的任务移除,尝试丢进队列
4,把任务丢给主线程
阻塞队列有:
1,new ArrayBlockingQueue<>(5) 基于数组的先进先出队列 有界
2,new LinkedBlockingQueue<>();基于链表的先进先出队列
3,new Synchronous<>()无缓冲的等待队列,无界
 
                    
                 
