JUC多线程

 

业务:普通的线程代码 Thread

Runnable 没有返回值、效率相比于Callable较低!

 

 2.线程与进程

线程进程如果不能用一句话说出来的技术,不扎实!

进程:一个程序,QQexe Music.exe 程序的集合;

一个进程往往可以包含多个线程,至少包含一个!

java默认有几个线程?2个 main、GC

线程:开了一个进程Typora,写字,自动保存(线程负责的)

Thread、Runnable/Callable

Java真的可以开启线程吗?

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    //本地方法,底层的C++,Java无法直接操作硬件
    private native void start0();

开发、并行

 开发编程:并发、并行

开发(多线程操作同一个资源)

  • cpu一核,模拟出来多条线程,快速交替

并行(多个人一起走)

  • cpu多核,多个线程可以同时执行
public class Test1  {

    public static void main(String[] args) {
        //获取cpu的核数
        //cpu密集型,IO密集型
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

并发编程的本质:充分利用CPU的资源

所有的公司都很看重效率!

线程有几个状态?

        //新生
        NEW
        
        //运行
        RUNNABLE

        //阻塞
        BLOCKED

        //等待
        WAITING

        //超时等待
        TIMED_WAITING,

        //终止
        TERMINATED;
    }

wait/sleep区别

 1.来自不同的类

wait--->Object

sleep--->Thread

2.关于锁的释放

wait会释放锁,sleep不释放锁。

3.使用的范围是不同的

wait须在同步代码块中

4.是否需要捕获异常

wait不需要捕获异常

sleep必须捕获异常

3.Lock锁(重点)

Lock接口

 

 公平锁:十分公平:先来后到

非公平锁:十分不公平:可以插队(默认)

synchronized与lock的区别

 1.synchronized内置的java关键字,Lock是一个java类。

2.synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁。

3.synchronized会自定释放锁,lock必须要手动释放锁。如果不释放锁,会出现"死锁"

4.synchronized线程1(获得锁、阻塞)、线程2(等待傻傻的等)Lock锁就不一定会一直等下去

5.synchronized可重入锁,不可以中断的,非公平;Lock可重入锁,可以判断锁,非公平锁(自己设置)

6.synchronized适合锁少量的代码同步问题,Lock锁适合锁大量的同步代码

锁是什么,如何判断锁的是谁?

4.生产者和消费者问题

面试:单例模式、排序算法、生产者消费者、死锁

生产者和消费者问题synchronized

/**
 * 线程之间的通信问题:生产者和消费者问题
 * 线程交替执行 A  B 操作同一个变量 num=0
 * A num+1
 * B num-1
 */
public class A {

    public static void main(String[] args) {
        thread thread = new thread();
        new Thread(()->{for(int i=0;i<20;i++) {thread.add();}},"A").start();

        new Thread(()->{for(int i=0;i<20;i++) {thread.decrement();}},"B").start();}
}

class thread{

    private int i=0;

   public synchronized void add(){

           if (i<1){
               //当i<1时加1
               i+=1;
               System.out.println(Thread.currentThread().getName()+"--->"+i);
               //通知其他线程我处理完毕了
             this.notifyAll();
           }else {
               try {
                   //当不满足i<1时,进程进入等待
                   this.wait();
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
   }

   public synchronized void decrement(){

           if (i>0) {
               i -= 1;
               System.out.println(Thread.currentThread().getName() + "--->" + i);
               //处理完毕,通知其他线程
               this.notifyAll();
           }else {
               try {
                   //当不满足i>0时,进程进入等待
                   this.wait();
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
   }
}

 问:上面这种写法,当存在四个线程时,是否能不出错?答:会出现虚假唤醒问题!

 将if改为while判断 if只会判断一次,两个线程有可能会同时进入方法

 

public class A {

    public static void main(String[] args) {
        thread thread = new thread();
        new Thread(()->{for(int i=0;i<50;i++) {thread.add();}},"A").start();

        new Thread(()->{for(int i=0;i<50;i++) {thread.decrement();}},"B").start();

        new Thread(()->{for(int i=0;i<50;i++) {thread.add();}},"C").start();

        new Thread(()->{for(int i=0;i<50;i++) {thread.decrement();}},"D").start();
    }
}

class thread{

    private int i=0;

   public synchronized void add(){

           while (i!=0){
               try {
                   this.wait();
               } catch (InterruptedException e) {
               }
           }
       //当i<1时加1
       i+=1;
       System.out.println(Thread.currentThread().getName()+"--->"+i);
       //通知其他线程我处理完毕了
       this.notifyAll();
   }

   public synchronized void decrement(){

           while (i!=1) {
               try {
                   this.wait();
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
       i -= 1;
       System.out.println(Thread.currentThread().getName() + "--->" + i);
       //处理完毕,通知其他线程
       this.notifyAll();
   }
}

JUC版本的生产者消费者

 

 

通过Lock找到Condition 

 

 代码实现:

public class B {

    public static void main(String[] args) {
        thrad thrad = new thrad();
        new Thread(()->{for(int i=0;i<50;i++){thrad.add();}},"A").start();
        new Thread(()->{for(int i=0;i<50;i++){thrad.decrement();}},"B").start();
    }
}

class thrad {

    int i=0;
    Lock lock=new ReentrantLock();
    Condition condition=lock.newCondition();

    public void add(){

        try {
            lock.lock();
            while(i!=0){
                    condition.await();
            }
            i+=1;
            condition.signalAll();
            System.out.println(Thread.currentThread().getName()+"---i--->"+i);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    public void decrement(){

        lock.lock();
        try {
            while (i!=1){
                condition.await();
            }
            i-=1;
            condition.signalAll();
            System.out.println(Thread.currentThread().getName()+"---i--->"+i);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
}

任何一个新的技术,绝对不是仅仅只是覆盖了原来的技术,一定会有优势和补充!

Condition精准的通知和唤醒线程

代码测试:

public class C {

    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{for(int i=0;i<20;i++)data.printA();},"A").start();
        new Thread(()->{for(int i=0;i<20;i++)data.printB();},"B").start();
        new Thread(()->{for(int i=0;i<20;i++)data.printC();},"C").start();
    }
}

class Data{//资源类

    private Lock lock=new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int number=1;

    public void printA(){
        lock.lock();

        try {
            while (number!=1){
                condition1.await();
            }
            number=2;
            condition2.signal();
            System.out.println("--->AAA");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    public void printB(){
        lock.lock();

        try {
            while (number!=2){
                condition2.await();
            }
            number=3;
            condition3.signal();
            System.out.println("--->BBB");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    public void printC(){
        lock.lock();

        try {
            while (number!=3){
                condition3.await();
            }
            number=1;
            condition1.signal();
            System.out.println("--->CCC");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
}

5、8锁现象

如何判断锁的是谁?

/**
 * 8锁就是关于锁的8个问题
 * 1.标准情况下,两个线程先打印发短信还是打电话? 1.sendSms 2.call
 * 2.sendSms等待4秒,两个线程先打印发短信还是打电话? 1.sendSms 2.call
 */
public class Test1 {
    public static void main(String[] args) {

        Phone phone = new Phone();
        new Thread(()->{phone.sendSms();}).start();

        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{phone.call();}).start();
    }
}
class Phone{

    //synchronized锁的对象是方法的调用者!
    //两个方法用的是同一把锁,谁先拿到谁先执行
    public synchronized void sendSms(){

        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("sendSms");
    }

    public synchronized void call(){
        System.out.println("call");
    }
}
/**
* 3.增加一个普通方法并调用,两个线程先打印发短信还是hello? 1.hello 2.sendSms
* 4.两个对象,两个同步方法,两个线程先打印发短信还是打电话? 1.call 2.sendSms
 */
public class Test2 {
    public static void main(String[] args) {
        //两个对象,两把锁
        Phone2 phone = new Phone2();
        Phone2 phone2 = new Phone2();
        new Thread(()->{phone.sendSms();}).start();

        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{phone2.call();}).start();
    }
}
class Phone2{

    //synchronized锁的对象是方法的调用者!
    //两个方法用的是同一把锁,谁先拿到谁先执行
    public synchronized void sendSms(){

        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("sendSms");
    }

    public synchronized void call(){
        System.out.println("call");
    }

    public void hello(){
        System.out.println("hello");
    }
}
/**
 * 5.增加两个静态同步方法,只有一个对象,先打印哪个? 1.sendSms 2.call
 * 6.两个对象,两个方法均为静态同步方法,先打印哪个? 1.sendSms 2.call
 */
public class Test3 {
    public static void main(String[] args) {

        //两个对象的class类模板只有一个,static锁的是Class类本身
        Phone3 phone = new Phone3();
        Phone3 phone2 = new Phone3();
        new Thread(()->{phone.sendSms();}).start();

        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{phone2.call();}).start();
    }
}

//唯一的Class对象
class Phone3{

    //synchronized锁的对象是方法的调用者!
    //static 随着类的加载而加载,锁的是Class
    public static synchronized void sendSms(){

        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("sendSms");
    }

    public static synchronized void call(){
        System.out.println("call");
    }
}
/**
 * 7.调用一个静态同步方法一个普通同步方法,先调用谁? 1.call 2.sendSms
 * 8.两个对象,分别调用一个静态同步方法一个普通同步方法,先调用谁? 1.call 2.sendSms
 */
public class Test4 {
    public static void main(String[] args) {

        //两个对象的class类模板只有一个,static锁的是Class类本身
        Phone4 phone = new Phone4();
        Phone4 phone2 = new Phone4();
        new Thread(()->{phone.sendSms();}).start();

        //捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        new Thread(()->{phone2.call();}).start();
    }
}

//唯一的Class对象
class Phone4{


    //static 随着类的加载而加载,锁的是Class
    public static synchronized void sendSms(){

        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("sendSms");
    }

    //synchronized锁的对象是方法的调用者!
    public synchronized void call(){
        System.out.println("call");
    }
}

小结

new  当调用的方法为普通方法时,锁锁的是new出来的对象

static 当调用的方法为静态同步方法时,锁锁的是Class 类本身

6.集合的安全性

List不是线程安全的,同理Set也不是同样建议使用CopyOnWriteArraySet<>()

/**
 * java.util.ConcurrentModificationException 并发修改异常
 */
public class ListTest {
    public static void main(String[] args) {
        //并发下ArrayList不安全,synchronized
        /**
         * 解决方案:
         * 1.List<String> list1 = new Vector<>();
         * 2.List<String> list1 = Collections.synchronizedList(list);
         * 3.List<String> list1 =new CopyOnWriteArrayList<>();
         */
        
        //CopyOnWrite写入时复制 COW 计算机设计领域的一种优化策略;
        //多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
        //在写入的时候避免覆盖,造成数据问题!
        //读写分离
        //CopyOnWriteArrayList 比 Vector 厉害在哪里?效率更高
        
        List<String> list1 =new CopyOnWriteArrayList<>();

        for (int i=0;i<10;i++){
            new Thread(()->{list1.add(UUID.randomUUID().toString().substring(1,8));
            System.out.println(list1);},String.valueOf(i)).start();
        }
    }
}

 HashSet的底层是什么?

    public HashSet() {
        map = new HashMap<>();
    }

    //add Set 本质就是 map key是无法重复的!
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
    
    private static final Object PRESENT = new Object();//常量

HashMap

/**
 * java.util.ConcurrentModificationException 并发修改异常
 */
public class MapTest {
    public static void main(String[] args) {

        //map是这样用的吗? 工作中不用HashMap
        //默认等价于什么? 影响因子 0.75 默认容量16
        ConcurrentHashMap<String, String> map1 = new ConcurrentHashMap<>();

        for (int i=0;i<60;i++){
            new Thread(()->{
                map1.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(1,6));
                System.out.println(map1.toString());
            },String.valueOf(i)).start();
        }
    }
}

7、Callable()

 1.可以有返回值

2.可以抛出异常

3.方法不同,run{}/call{}

代码测试

 

public class CallableTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //new Thread(new Runnable).start();
        //new Thread(new FutureTask<V>()).start();
        //new Thread(new FutureTask<V>(Callable)).start();
        //new Thread().start();//怎么启动Callable

        MyThread thread = new MyThread();
        FutureTask futuretask = new FutureTask(thread);
        new Thread(futuretask,"A").start();
        new Thread(futuretask,"B").start();//结果会被缓存,效率高

        Integer o = (Integer) futuretask.get();//get方法可能会产生阻塞!把他放到最后或者通过异步通信来处理
        System.out.println(o);
    }
}

class MyThread implements Callable<Integer> {


    @Override
    public Integer call() throws Exception {
        System.out.println("--->call");//会打印几个call
        return 1024;
    }
}

 细节:

1.有缓存

2.结果可能需要等待,会阻塞!

8、常用的辅助类(必会)

8.1CountDownLatch

 

//计数器
public class countDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {
        //倒计时,必须要执行任务的时候,再使用!
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i=0;i<6;i++){
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" Go Out");
                countDownLatch.countDown();//计数器-1
            }, String.valueOf(i)).start();
        }

        countDownLatch.await();//等待计数器归零,继续执行

        System.out.println("关门");
    }
}

原理:

countDownLatch.countDawn();//数量-1

countDownLatch.await();//等待计数器归零,技续执行

每次有线程调用countDown()数量-1,假设计数器变为0,countDownLatch.await()就会被唤醒,继续执行。

8.2CycliBarrier

public class CyclicBarrierDemo {

    public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
        /**
         * 加法计数器
         */

        CyclicBarrier cyclicBarrier = new CyclicBarrier(6,()->{
            System.out.println("guanmen");
        });

        for (int i = 1; i < 7; i++) {
            final int temp=i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+" go room  "+temp);
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } catch (BrokenBarrierException e) {
                    throw new RuntimeException(e);
                }

            }).start();
        }
    }
}

8.3、Semaphore

信号量

 抢车位!

6辆车---3个车位

public class SemaphoreDemo {

    public static void main(String[] args) {
        //线程数量:限流
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 6; i++) {
            final int temp1=i;
            new Thread(()->{
                //acquire()得到
                //release()释放
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+" P ");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+" go out");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }finally{

                    //release()释放
                    semaphore.release();
                }

            }, String.valueOf(i)).start();
        }
    }
}

原理:

semaphore.acquire() 获得,假设已经满了,等待,等到被释放为止!

semaphore.release() 释放,会将当前的信号量释放+1,然后唤醒等待的线程!

作用:多个共享资源互斥的使用!并发限流的最大线程数。

9、读写锁

ReadWriteLock

 读的时候可以多个线程读,写的时候只能由一个线程写。

测试代码:

public class ReadWriteLockDemo {

    public static void main(String[] args) {
        MyCacheLock myCache = new MyCacheLock();

        for (int i = 0; i <5; i++) {
            final int temp=i;
            new Thread(()->{myCache.put(temp+"",temp+"");}, String.valueOf(i)).start();
        }

        for (int i = 0; i < 5; i++) {

            final int temp=i;
            new Thread(()->{myCache.get(temp+"");}, String.valueOf(i)).start();
        }

    }
}


/**
 * 自定义带锁的缓存
 * ReadWriteLock
 * 读-读 可以共存! 独占锁
 * 读-写 不能共存! 共享锁
 * 写-写 不能共存! 共享锁
 */
class MyCacheLock {

    private volatile Map<String, Object> map = new HashMap<>();
    //读写锁:更加细粒度的操作
    private ReadWriteLock lock=new ReentrantReadWriteLock();

    //存,写入的时候只希望同时只有一个线程
    public void put(String key,Object value) {
        //写锁上锁
        lock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+" Write");
            map.put(key,value);
            System.out.println("Write ok "+value);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            //写锁解锁
            lock.writeLock().unlock();
        }
    }

    //取,读的时候所有人都可以读!
    public void get(String key) {
        System.out.println(Thread.currentThread().getName()+" Read");
        Object o = map.get(key);
        System.out.println(o+" Read ok");
    }
}

10、阻塞队列

 

阻塞队列:

 BlockQueue BlockingQueue 不是新东西,是数组的一种

什么情况下我们会使用 阻塞队列:多线程并发处理,线程池

学会使用队列

四组API

方式抛出异常不会抛出异常阻塞等待超时等待
添加add()offer(Object)put()offer(Object,int,TimeUnit)
移除remove()poll()take()poll(int,TimeUnit)
判断element()peek()

 

public class Test {
    public static void main(String[] args) {

        test1();
    }
        /**
         * 抛出异常
         */
        public static void test1(){
            //队列大小
            Queue abq = new ArrayBlockingQueue<>(3);
            System.out.println(abq.add("a"));
            System.out.println(abq.add("b"));
            System.out.println(abq.add("c"));
            //java.lang.IllegalStateException: Queue full 抛出异常
//            System.out.println(abq.add("d"));
            System.out.println(abq.remove());
            System.out.println(abq.remove());
            System.out.println(abq.remove());
            //java.util.NoSuchElementException
//            System.out.println(abq.remove());
        }
}
    /**
     * 不抛出异常 offer/poll
     */
    public static void test2() {
        //队列大小
        Queue abq = new ArrayBlockingQueue<>(3);

        System.out.println(abq.offer("a"));
        System.out.println(abq.offer("b"));
        System.out.println(abq.offer("C"));
        //false
        System.out.println(abq.offer("d"));

        System.out.println(abq.poll());
        System.out.println(abq.poll());
        System.out.println(abq.poll());
        //null
        System.out.println(abq.poll());
    }
    /**
     * 等待,阻塞(死等)
     */
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue abq = new ArrayBlockingQueue<>(3);
        abq.put("a");
        abq.put("b");
        abq.put("c");
        System.out.println(abq.toString());
//        abq.put("D"); 队列已满,一直阻塞

        System.out.println(abq.take());
        System.out.println(abq.take());
        System.out.println(abq.take());
//        System.out.println(abq.take()); 没有这个元素,一直阻塞
    }
/**
     * 等待,阻塞(超时等待)
     */
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue<Object> abq = new ArrayBlockingQueue<>(3);
        abq.offer("a");
        abq.offer("b");
        abq.offer("c");
        abq.offer("d",2, TimeUnit.SECONDS);//超时两秒将退出

        abq.poll();
        abq.poll();
        abq.poll();
        abq.poll(2,TimeUnit.SECONDS);//超时两秒将退出

    }

SynchronousQueue同步队列

没有容量,进去一个元素,必须等待取出来之后,才能再往里面放一个元素!

put、take

/**
 * 同步队列
 * 和其他BlockQueue不一样,SynchronousQue 不存储元素
 * put了一个元素,必须从里面先take取出来,否则不能再put进去值!
 */
public class SynchronousQueDemo {
    public static void main(String[] args) {

        SynchronousQueue<String> sq = new SynchronousQueue<>();


        new Thread(() -> {
            try {
                sq.put("1");
                System.out.println(sq);
                sq.put("2");
                System.out.println(sq);
                sq.put("3");
                System.out.println(sq);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, String.valueOf("放-->")).start();

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(sq.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(sq.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(sq.take());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, String.valueOf("取-->")).start();
    }
}

11.线程池(重点)

线程池:三大方法、7大参数、4种拒绝策略

池化技术

程序的运行的本质:占用系统的资源 !优化资源的使用!--->池化技术

线程池、连接池、内存池、对象池//.....

池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我

默认大小:2

max:

线程池的好处:

1.降低资源的消耗

2.提高响应的速度

3.方便管理

线程复用、可以控制最大并发数、管理线程

三大方法

//Executors 工具类、三大方法
public class Demo01 {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();//单个线程
        ExecutorService executor1 = Executors.newFixedThreadPool(5);//创建一个固定的线程池的大小
        ExecutorService executor2 = Executors.newCachedThreadPool();//遇强则强,遇弱则弱

        try {
            for (int i = 0; i < 100; i++) {
                //使用了线程池后,使用线程池来创建线程
                executor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"  ok");;});
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            //线程池用完,程序结束,关闭线程池
            executor.shutdown();
        }

    }
}

7大参数        默认线程池大小        最大线程池大小        超时等待时间        时间单位        阻塞队列        线程工厂、创建线程的,一般不用动        拒绝策略

源码分析

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,//约为21亿,会导致内存溢出OOM
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

//本质ThreadPoolExecutor

 public ThreadPoolExecutor(int corePoolSize,//默认线程池大小
                              int maximumPoolSize,//最大线程池大小
                              long keepAliveTime,//超时等待时间
                              TimeUnit unit,//时间单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory,//线程工厂、创建线程的一般不用动
                              RejectedExecutionHandler handler) {//拒绝策略
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

 

手动创建一个线程池 

四种拒绝策略 

/**
 * new ThreadPoolExecutor.AbortPolicy());//请求>最大+队列,抛出异常 RejectedExecutionException
 * new ThreadPoolExecutor.CallerRunsPolicy());//哪来的回哪去,这里将由mian处理
 *new ThreadPoolExecutor.DiscardPolicy());//丢弃超额任务,不抛异常
 *new ThreadPoolExecutor.DiscardOldestPolicy());//尝试与第一个线程竞争,失败丢则放弃超额任务
 */

小结和拓展:池的大小如何设置 性能调优

public class Demo01 {
    public static void main(String[] args) { 
        //自定义线程池!工作中只会使用ThreadPoolExecutor

        //最大线程到底该如何定义?
        //1.CPU 密集型,几核就是几,可以保持Cpu的效率最高
//        Runtime.getRuntime().availableProcessors();
        //2.IO密集型,判断你程序中十分耗IO的线程,设置为耗时IO线程的2倍
        ExecutorService threadPool=new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors(),//获取逻辑CPU个数
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy());//尝试与第一个线程竞争,失败丢则抛弃超额任务

        try {
            for (int i = 0; i < 100; i++) {
                //使用了线程池后,使用线程池来创建线程
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + "  ok");
                    ;
                });
            }
        } catch (
                Exception e) {
            throw new RuntimeException(e);
        } finally {
            //线程池用完,程序结束,关闭线程池
            threadPool.shutdown();
        }
    }
}

12、四大函数式接口(必须掌握)

新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流

函数式接口:只有一个方法的接口

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

//超级多FunctionalInterface
//简化编程模型,在新版本的框架底层大量应用!
//foreach(消费者类的函数式接口)

代码测试:

Function函数式接口

import java.util.function.Function;
/**
 *function 函数式接口,有一个输入参数,有一个输出结果
 * 只要是函数式接口,就可以用lambda表达式简化
 */
public class Demo01 {
    public static void main(String[] args) {
        Function<String, Integer> functionT = new Function<String, Integer>() {

            @Override
            public Integer apply(String o) {
                return Integer.parseInt(o);
            }
        };

        Function function=(str)->{return str;};
        System.out.println(functionT.apply(String.valueOf(123)));
    }
}

 断定型接口:有一个输入参数,返回值只能是 布尔值!

/**
 * 断定型接口:有一个输入参数,返回值只能是 布尔值!
 */
public class Demo02 {
    public static void main(String[] args) {

//        Predicate<String> predicate = new Predicate<String>() {
//            @Override
//            public boolean test(String o) {
//
//                   return o.isEmpty();
//            }
//        };

        Predicate<String> pre=(str)->{return str.isEmpty();};
        boolean str = pre.test("");
        System.out.println(str);
    }
}

Consumer 消费型接口:

Supplier 供给型接口:

public class Demo03 {

    public static void main(String[] args) {
//        Consumer<String> consumer = new Consumer<String>() {
//            @Override
//            public void accept(String o) {
//                System.out.println(o);
//            }
//        };
//
//        Supplier supplier = new Supplier<String>() {
//            @Override
//            public String get() {
//                return 1024+"";
//            }
//        };

//        consumer.accept("1");
        Consumer con=(o)->{System.out.println(o);};
//        System.out.println(supplier.get());
        Supplier sup=()->{return 1024;};
        con.accept("o");
        System.out.println(sup.get());

    }
}

13、Stream流式计算

什么是Stream流式计算

存储+计算

集合:MySQL本质就是存储东西打的

计算都应该交给流来操作!

/**
 * 题目要求:一分钟内完成此题,只能用一行代码实现!
 * 现在有五个用户,筛选:
 * 1.列出id为2的倍数的对象
 * 2.年龄必须大于23岁
 * 3.用户名转为大写字母
 * 4.用户名字母倒着排序
 * 5.只输出一个用户
 */
public class Test {
    public static void main(String[] args) {
        User u1=new User(1,"a",21);
        User u2=new User(2,"b",22);
        User u3=new User(3,"c",23);
        User u4=new User(4,"d",24);
        User u5=new User(6,"e",25);

        //集合就是存储
        List<User> list = Arrays.asList(u1, u2, u3, u4, u5);

        //计算交给Stream流
        //lambda表达式,链式编程、函数式接口、Stream链式计算
        list.stream()
                .filter(u->{return u.getId()%2==0;})
                .filter(u->{return u.getAge()>23;})
                .filter(u->{ u.setName(u.getName().toUpperCase());return true;})
                .sorted((u,uu)->{return uu.getName().compareTo(u.getName());})
                .limit(1)
                .forEach(System.out::println);
    }
}

 14、ForkJoin

什么是ForkJoin

ForkJoin在JDK1.7中用于:大数据量时并行执行任务,,提高效率!

大数据:Map Reduce (把大任务拆分为小任务)

forkjoin工作特点:工作窃取

这里维护的都是双端队列

 

 ForkJoin的操作

/**
 * 求和计算的任务!
 * <p>
 * 3000 6000(ForkJoin) 9000(Stream并行流)
 * 如何使用ForkJoinPool
 * 1.ForkJoinPool
 * 2.计算任务ForkJoinPool.execute(ForkJoinTask<?> task)
 */
public class ForkJoinDemo extends RecursiveTask<Long> {

    private long start;
    private long end;

    //临界值
    private long temp = 10000L;

    public ForkJoinDemo(long start, long end) {
        this.start = start;
        this.end = end;
    }
    @Override
    protected Long compute() {
        if ((end - start) > temp) {
            long middle = (start + end) / 2;//中间值
            ForkJoinDemo task = new ForkJoinDemo(start, middle);
            ForkJoinDemo task1 = new ForkJoinDemo(middle+1, end);
            task.fork();
            task1.fork();
            return task.join()+task1.join();

        } else {
            long sum = 0L;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        }
    }
}
public class Test {
    public static void main(String[] args) {
//        test1();//用时:--->272ms
//
//        test2();//用时:--->229ms

        test3();//用时:  --->137ms
    }

    public static void test1(){
            //计算1-1_0000_0000的和
            long sum = 0L;
            long l = System.currentTimeMillis();
            for (int i = 0; i < 10_0000_0000; i++) {
                sum += i;
            }
            long l2 = System.currentTimeMillis();
            System.out.println("用时:--->" + (l2 - l) + "ms" + " 结果为:" + sum);//用时:--->262ms 结果为:499999999500000000
    }

    public static void test2(){
        ForkJoinDemo forkJoinDemo = new ForkJoinDemo(0, 10_0000_0000-1);
        long l = System.currentTimeMillis();
        Long compute = forkJoinDemo.compute();
        System.out.println("结果为:--->"+compute+"  用时:--->"+ (System.currentTimeMillis()-l)+"ms");//结果为:--->499999999500000000  用时:--->227ms
    }

    public static void test3(){
        long start = System.currentTimeMillis();
        //start并行流()  (]
        long sum = LongStream.rangeClosed(0L, 10_0000_0000L-1).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println(end-start+"ms--->"+sum);

    }
}

15、异步回调

Future设计的初衷:对将来的某个事件的结果进行建模

/**
 * 异步调用:CompletableFuture
 * 异步执行
 * 成功回调
 * 失败回调
 */
public class Demo01 {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
            //没有返回值的runAsync 异步回调
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName() + "runAsync=>Void");
        });

        System.out.println("111");

        completableFuture.get();//获取执行结果

        //有返回值的supplyAsync 异步回调
        //aj
        CompletableFuture<String> runnable = CompletableFuture.supplyAsync(() -> {
            int i = 10 / 0;
            return "1024";
        });

        System.out.println(runnable.whenComplete((u, t) -> {
            System.out.println("u--->" + u);//正常结果
            System.out.println("t--->" + t);//错误信息
        }).exceptionally((e) -> {
            System.out.println(e.getMessage());
            return String.valueOf(233);//可以获得错误的返回结果
        }).get());
    }
}

16.JMM

请你谈谈对Volatile的理解

Volatile是java虚拟机提供的轻量级的同步机制

1.保证可见性

2.不保证原子性

3.禁止指令重排

什么是JMM

JMM:java的内存模型,是一种概念、约定

关于JMM的一些同步的约定:

1.线程的解锁前,必须把共享变量立刻刷回主存。

2.线程加锁前,必须读取主存中的最新值到工作内存中!

3.加锁和解锁是同一把锁

线程  工作内存、主内存 

 

17.Volatile 

1、保证可见性 

public class VolatileTest1 {

    //保证可见性
    private volatile static int num=0;

    public static void main(String[] args) {

        new Thread(()->{
            while (num==0){
            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        num+=1;
        System.out.println(num);
    }
}

 2、不保证原子性

public class Volatile {

    //volatile不保证原子性
    private volatile static int num=0;

    public  static void add(){
        num++;
    }
    public static void main(String[] args) {

        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){
            Thread.yield();
        }

        System.out.println(num);
    }
}

如果不加lock和synchronized,怎么样保证原子性

 使用原子类,解决原子性

public class VolatileTest2 {

    //volatile不保证原子性
    //原子类的  Integer
    private static AtomicInteger num=new AtomicInteger();

    public  static void add(){
        num.getAndIncrement();//一个原子性操作,CAS
    }
    public static void main(String[] args) {

        for (int i = 0; i < 20; i++) {
            new Thread(()->{
                for (int j = 0; j < 1000; j++) {
                    add();
                }
            }).start();
        }

        while (Thread.activeCount()>2){
            Thread.yield();
        }

        System.out.println(num);
    }
}

这些类的底层都直接和操作系统挂钩!在内存中修改值!Unsafe类是一个很特殊的存在。

 指令重排

什么是指令重排:你写的程序,计算机并不是按照你写的那样去执行的。

源代码-->编译器优化的重排-->指令并行也可能会重排-->内存系统也会重排-->执行

Volatile是可以保持可见性。不能保证原子性,由于内存屏障,可以保证避免指令重排的现象产生!

18、彻底玩转单例模式

 饿汉式单例模式

//饿汉式单例模式
public class Hunger {

    //饿汉式可能会浪费空间
    private byte[] b=new byte[1024*1024];
    private byte[] y=new byte[1024*1024];
    private byte[] t=new byte[1024*1024];
    private byte[] e=new byte[1024*1024];
    private byte[] s=new byte[1024*1024];

    private Hunger() {

    }

    private final static Hunger HUNGER =new Hunger() {};

    public static Hunger getInstance() {

        return HUNGER;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                System.out.println(getInstance() + "--->ok   " + Thread.currentThread().getName());
            }, String.valueOf(i)).start();
        }
    }

}

 懒汉式单例模式

//懒汉式
//道高一尺魔高一丈,反射会破坏单例
public class lazyMan {

    private static boolean qingjiang=false;
    private lazyMan() {

        synchronized (lazyMan.class){
            if (qingjiang==false){
                qingjiang=true;
            }else
                throw new RuntimeException("不要试图用反射破坏单例");
        }
        System.out.println(Thread.currentThread().getName() + "---0k");
    }

    private volatile static lazyMan lazy;

    public static lazyMan getInstance() {
        //双重检测锁的懒汉式单例 DCL懒汉式
        if (lazy == null) {
            synchronized (lazyMan.class) {
                if (lazy == null) {
                    lazy = new lazyMan();//不是一个原子性操作

                }
            }
        }
        return lazy;
    }
    /**
     * 1.分配内存空间
     * 2.执行构造方法,初始化对象
     * 3.把这个对象指向这个空间
     *
     * 123
     * 132 A
     *     B此时LazyMan还没有完成构造
     */

    //多线程并发
    public static void main(String[] args) throws Exception {
//        for (int i = 0; i < 10; i++) {
//            new Thread(() -> {
//                getInstance();
//            }, String.valueOf(i)).start();
//        }

        Field qingjiang = lazyMan.class.getDeclaredField("qingjiang");
        qingjiang.setAccessible(true);

        Constructor<lazyMan> declaredConstructor = lazyMan.class.getDeclaredConstructor(null);

        declaredConstructor.setAccessible(true);
        lazyMan lazyMan = declaredConstructor.newInstance();

        qingjiang.set(lazyMan,false);
        lazyMan lazyMan2 = declaredConstructor.newInstance();
//        com.atguigu.single.lazyMan lazyMan2 = com.atguigu.single.lazyMan.getInstance();


        System.out.println(lazyMan.hashCode()+"--111");
        System.out.println(lazyMan2.hashCode()+"--222");
        System.out.println(lazyMan.equals(lazyMan2));

    }
}

静态内部类

//静态内部类
public class Holder {

    private Holder(){
    };

    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }

    public static class InnerClass{
        private static final Holder HOLDER=new Holder();
    }
}

 反射能够破坏上面的单例模式,于是我们需要一个能不被反射破坏的单例模式:enum

 

 

//enum是一个什么?本身也是一个Class类
public enum EnumSingle {

    INSTANCE;

    public EnumSingle getInstance(){
        return INSTANCE;
    }
}


class Test{

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        EnumSingle instance = EnumSingle.INSTANCE;
        EnumSingle instance2 = EnumSingle.INSTANCE;
        EnumSingle instance3 = EnumSingle.INSTANCE;

        System.out.println(instance.equals(instance2));
        System.out.println(instance2.equals(instance3));

        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);//Cannot reflectively create enum objects

        declaredConstructor.setAccessible(true);
        EnumSingle enumSingle = declaredConstructor.newInstance();//java.lang.NoSuchMethodException 没有找到空参构造器
        EnumSingle enumSingle2 = declaredConstructor.newInstance();

        System.out.println(enumSingle2.equals(enumSingle));
    }
}

 19、深入理解CAS

什么是CAS

 Unsafe类

CAS:比较当前工作内存中的值,如果这个值是期望的,那么则执行 操作,如果不是则一直循环

缺点:

1.循环会耗时

2.一次性只能保证一个共享变量的原子性

3.ABA问题

 CAS:ABA问题(狸猫换太子)

 

 

public class CASDemo {

    //compareAndSet:比较并交换
    public static void main(String[] args) {
        AtomicInteger atomicInteger=new AtomicInteger(2020);

        //对于我们平时写的SQL : 乐观锁!

        //expect 期望 update 更新
        // public final boolean compareAndSet(int expect, int update)
        //如果我期望的值达到了,那么久更新,否则,就不更新
        //捣乱的线程
        atomicInteger.compareAndSet(2020,2021);
        System.out.println(atomicInteger.get());

        atomicInteger.compareAndSet(2021,2020);
        System.out.println(atomicInteger.get());

        //期望的线程
        atomicInteger.compareAndSet(2020,2222);
        System.out.println(atomicInteger.get());
    }

}

20、原子引用

带版本号的原子操作!

 

public class VolatileABA {

        //compareAndSet:比较并交换
        public static void main(String[] args) {
//        AtomicInteger atomicInteger=new AtomicInteger(2020);

            AtomicStampedReference<String> atomicInteger = new AtomicStampedReference<>("2020",1);

            //对于我们平时写的SQL : 乐观锁!

            //expect 期望 update 更新
            // public final boolean compareAndSet(V expect, V update)
            //如果我期望的值达到了,那么久更新,否则,就不更新
            //捣乱的线程
            atomicInteger.compareAndSet("2020","2021", atomicInteger.getStamp(),atomicInteger.getStamp()+1);
            System.out.println(atomicInteger.getStamp()+"   "+atomicInteger.get(new int[]{atomicInteger.getStamp()}));

            atomicInteger.compareAndSet("2021","2020", atomicInteger.getStamp(),atomicInteger.getStamp()+1);
            System.out.println(atomicInteger.getStamp()+"   "+atomicInteger.get(new int[]{atomicInteger.getStamp()}));

            //期望的线程
            atomicInteger.compareAndSet("2020","2222", atomicInteger.getStamp(),atomicInteger.getStamp()+1);
            System.out.println(atomicInteger.getStamp()+"   "+atomicInteger.get(new int[]{atomicInteger.getStamp()}));
        }

    }

21、各种锁的理解

1.公平锁、非公平锁

公平锁:不能插队 (ReentrantLock lock = new ReentrantLock(true);)

非公平锁:可以插队 (默认非公平锁  synchronized lock)

    public ReentrantLock() {
        sync = new NonfairSync();
    }    
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

2.可重入锁

可重入锁(递归锁)

public class Demo01 {
    public static void main(String[] args) {
        call();
    }


    public static synchronized void call(){
        System.out.println("--->打电话");

        message();//这里也有锁
    }

    public static void message(){
        ReentrantLock lock = new ReentrantLock();
        lock.lock();
        try {
            System.out.println("--->发短信");
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }
}

3.自旋锁

/**
 * 自旋锁
 */
public class SpinlockDemo {

    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    //加锁
    public void mylock() {
        Thread thread = Thread.currentThread();
        System.out.println(thread.currentThread().getName() + "===> mylock");

        //自旋锁
        while (!atomicReference.compareAndSet(null, thread)) {

        }
    }

    //解锁
    public void unlock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "===> unlock");
        atomicReference.compareAndSet(thread, null);
    }
}
public class SpinlockDemoTest {

    static SpinlockDemo  spinlockDemo = new SpinlockDemo();
    public static void main(String[] args) {
        new Thread(()->{
            call();
        },"A").start();

        new Thread(()->{
            message();
        },"B").start();
    }

    public static void call(){
        spinlockDemo.mylock();
        try {
            System.out.println("打电话--->");
            TimeUnit.SECONDS.sleep(3);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            spinlockDemo.unlock();
        }
    }

    public static void message(){
        spinlockDemo.mylock();
        try {
            System.out.println("发短信--->");
            TimeUnit.SECONDS.sleep(1);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            spinlockDemo.unlock();
        }
    }
}

4、死锁

死锁是什么 

 

 死锁测试,怎么排除死锁

public class DeadLockDemo {
    public static void main(String[] args) {

        String lockA="lockA";
        String lockB="lockB";

        new Thread(new MyThread(lockA,lockB),"T1").start();
        new Thread(new MyThread(lockB,lockA),"T2").start();
    }
}

class MyThread implements Runnable{

    private String lockA;
    private String lockB;

    public MyThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {
        synchronized (lockA){
            System.out.println(Thread.currentThread().getName()+"--->lock:"+lockA+"->get->"+lockB);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+"--->lock:"+lockB+"->get->"+lockA);
            }
        }
    }
}

解决问题

1、使用jsp -l定位进程号(若无法使用jps命令可以重新配置一下jre的路径,重启idea再试) 

2.使用jstack 进程号 查看堆栈信息

 

 面试:工作中排查问题

1.日志

2.堆栈

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值