JUC笔记

线程状态

public enum State {
    // 新建
    NEW,

    // 运行
    RUNNABLE,

    // 阻塞
    BLOCKED,

   // 等待
    WAITING,

    // 超时等待
    TIMED_WAITING,

    // 终止
    TERMINATED;
}

wait和sleep

  1. 来自不同的类

    wait--Object

    sleep--Thread

  2. 关于锁的释放

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

  3. 使用范围

    wait要在同步代码块中

    sleep可以在任何地方

Lock锁

synchronized

public class SaleTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }

            },"A").start();
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 30; i++) {
                ticket.sale();
            }
        },"C").start();

    }
}

class Ticket {
    private int num = 20;

    public synchronized void sale() {
        if (num > 0) {
            System.out.println(Thread.currentThread().getName()+"卖出"+num--);
        }
    }
}

Lock接口

ReentrantLock的构造函数

无参构造是非公平锁,默认的

lock的使用

  1. Lock lock = new ReentrantLock();
    
  2. public void sale() {
        // 加锁
        lock.lock();
        try{
            if (num > 0) {
                System.out.println(Thread.currentThread().getName()+"卖出"+num--);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 解锁
            lock.unlock();
        }
    
    }
    

synchronized和lock的区别

  1. synchronized是java的关键字,Lock是一个java接口
  2. synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁
  3. synchronized会自动释放锁,Lock必须手动释放
  4. synchronized适合锁少量的代码同步问题,Lock都可以使用

生产者和消费者问题

synchronized

public class TestPC {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"N").start();
    }
}

class Data {
    private int num = 0;

    // +1
    public synchronized void increment() throws InterruptedException {
        if (num !=0 ) {
            // 等待
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"=="+num);
        // 通知其他线程
        this.notifyAll();
    }

    // -1
    public synchronized void decrement() throws InterruptedException {
        if(num == 0) {
            // 等待
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName()+"=="+num);
        this.notifyAll();
    }
}

如果有A、B、C、D四个线程会存在问题

在减一之后会唤醒线程,有两个生产者,所以这两个生产者都被唤醒后不会重新判断if条件,直接执行if后面的+1,用while的话会重新判断是否需要等待

if改为while

JUC的生产者和消费者问题

public class B {
    public static void main(String[] args) {
        Data2 data2 = new Data2();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data2.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data2.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"B").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data2.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"C").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data2.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"D").start();
    }
}

class Data2 {
    private int num = 0;

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

    // +1
    public  void increment() throws InterruptedException {
        lock.lock();

        try {
            while (num !=0 ) {
                // 等待
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName()+"=="+num);
            // 通知其他线程
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    // -1
    public  void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (num == 0) {
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+"=="+num);
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

Condition精准的通知和唤醒线程

public class C {
    public static void main(String[] args) {
        Data3 data3 = new Data3();
        // A执行完--B--C,顺序执行
        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                data3.printA();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                data3.printB();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 5; i++) {
                data3.printC();
            }
        },"C").start();
    }
}

class Data3 {
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int flag = 1;

    public void printA() {
        lock.lock();
        try {
            // 判断--执行--通知
            while (flag != 1) {
                // 等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"====AAAA");
            // 唤醒指定的B
            flag = 2;
            condition2.signal();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public void printB() {
        lock.lock();
        try {
            while (flag != 2) {
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"====B");
            flag = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC() {
        lock.lock();
        try {
            while (flag != 3) {
                condition3.await();
            }
            System.out.println("C");
            flag = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

8锁现象

找出锁的是什么

  1. synchronized两个线程用的是同一个锁,先发短信后打电话
public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        new Thread(()->{
            try {
                phone.sendMessage();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            phone.call();
        },"B").start();
    }
}

class Phone {
    // synchronized锁的是Phone的同一个对象
    public synchronized void sendMessage() throws InterruptedException {
        Thread.sleep(2000);
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }
}
  1. 先输出hello,等待2秒发短信,因为hello没有同步锁
public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        new Thread(()->{
            try {
                phone.sendMessage();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            phone.hello();
        },"B").start();
    }
}

class Phone {
    public synchronized void sendMessage() throws InterruptedException {
        Thread.sleep(2000);
        System.out.println("发短信");
    }

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

    public void hello() {
        System.out.println("hello");
    }
}
  1. 输出打电话,发短信
public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->{
            try {
                phone.sendMessage();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        },"A").start();
        //TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

class Phone {
    public synchronized void sendMessage() throws InterruptedException {
        Thread.sleep(2000);
        System.out.println("发短信");
    }

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

    public void hello() {
        System.out.println("hello");
    }
}
  1. static方法

发短信,打电话

public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        Phone2 phone = new Phone2();
        Phone2 phone2 = new Phone2();
        new Thread(()->{
            try {
                phone.sendMessage();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        },"A").start();
        //TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

// Phone2唯一的class
class Phone2 {
    // synchronized锁的是Phone2.class
    public static synchronized void sendMessage() throws InterruptedException {
        Thread.sleep(2000);
        System.out.println("发短信");
    }

    public static synchronized void call() {
        System.out.println("打电话");
    }
}
  1. 一个static同步方法,一个普通的同步方法

先打电话,然后发短信,因为static同步方法锁的是Phone的class,普通的同步方法锁的是调用对象

public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        Phone2 phone = new Phone2();
        Phone2 phone2 = new Phone2();
        new Thread(()->{
            try {
                phone.sendMessage();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        },"A").start();
        //TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            phone.call();
        },"B").start();
    }
}

// Phone2唯一的class
class Phone2 {
    // synchronized锁的是Phone2.class
    public static synchronized void sendMessage() throws InterruptedException {
        Thread.sleep(2000);
        System.out.println("发短信");
    }

    // 普通的同步方法
    public  synchronized void call() {
        System.out.println("打电话");
    }
}

集合类不安全

list不安全

public class ListTest {
    public static void main(String[] args) {
        // java.util.ConcurrentModificationException 并发修改异常
        // 解决方法
        // 1.  List<String> list = new Vector<>();
        // 2. List<String> list = Collections.synchronizedList(new ArrayList<>());
        // 3. List<String> list = new CopyOnWriteArrayList<>();
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 1; i <=20; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

set不安全

public class SetTest {
    public static void main(String[] args) {
        // Set<String> set = new HashSet<>(); 并发修改异常
        /*
        解决方法:
        Set<String> set = Collections.synchronizedSet(new HashSet<>());
        Set<String> set = new CopyOnWriteArraySet();
         */

        Set<String> set = new CopyOnWriteArraySet();
        for (int i = 1; i <= 20; i++) {
            new Thread(()->{
                set.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(set);
            },String.valueOf(i)).start();
        }

    }
}

hashset的底层就是hashmap

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

HashMap

public class MapTest {
    public static void main(String[] args) {
        // 默认容量16,加载因子0.75
        // java.util.ConcurrentModificationException
        // Map<Object, Object> map = new HashMap<>();

        // 解决方法
        Map<Object, Object> map = new ConcurrentHashMap<>();
        for (int i = 1; i < 20; i++) {
            new Thread(()->{
                map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

callable

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // new Thread(new Runnable() {}).start();
        // FutureTask实现了Runnable接口
        // new Thread(new FutureTask<V>() {}).start();
        // FutureTask有个带callable参数的构造函数
        // new Thread(new FutureTask<V>(Callable) {}).start();

        MyThread myThread = new MyThread();
        FutureTask futureTask = new FutureTask(myThread);
        // 输出A
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start();
        Object o = futureTask.get(); // get方法可能会阻塞,放到最后
        System.out.println(o);
    }
}

class MyThread implements Callable<Boolean> {

    @Override
    public Boolean call() throws Exception {
        System.out.println(Thread.currentThread().getName());
        return true;
    }
}
  1. fuctureTask只能使用一次,此时不是new状态,会直接结束线程
  2. get方法可能会阻塞
    常用的辅助类

CountDownLatch

减法计数器

public class CountDownLatchTest {
    public static void main(String[] args) throws InterruptedException {
        // 总数是6
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"出去了");
                countDownLatch.countDown();// -1
            },String.valueOf(i)).start();
        }

        // 等待执行完,计数器归0,再往下执行
        countDownLatch.await();
        System.out.println("执行完毕");
    }
}

CyclicBarrier

加法计数器

public class CyclicBarrierTest {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(6,()->{
            System.out.println("集齐了");
        });
        for (int i = 1; i <= 6; i++) {
            final int temp = i;
            new Thread(()->{
                System.out.println("得到"+temp);
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

Semaphore

计数信号量,多个共享资源互斥使用

semaphore.acquire() 获取信号量,如果没有剩余则等待

semaphore.release() 释放信号量

public class SemaphoreTest {
    public static void main(String[] args) {
        // 可用数量为3
        Semaphore semaphore = new Semaphore(3);

        for (int i = 1; i < 6; i++) {
            new Thread(()->{
                // acquire() 得到
                // release() 释放
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"拿到");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(Thread.currentThread().getName()+"离开了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}

读写锁

ReentrantReadWriteLock

独占锁-写锁 共享锁-读锁

读-读 可以共存 读和写,写和写不能共存

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

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

// 加入读写锁
class MyCacheLock {
    private volatile Map<String ,Object> map = new HashMap<>();
    // 读写锁
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    // 写,写入的时候,同时只有一个线程
    public void put(String key, Object value) {
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+"写入"+key);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入完毕");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }
    // 读
    public void get(String key) {
        // 读锁,防止写的时候进行读,脏读
        readWriteLock.readLock().lock();
        try {
            Object o = map.get(key);
            System.out.println("读取"+o);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

/*
自定义缓存
 */
// 读写混乱
class MyCache {
    private volatile Map<String ,Object> map = new HashMap<>();

    // 写
    public void put(String key, Object value) {
        System.out.println(Thread.currentThread().getName()+"写入"+key);
        map.put(key,value);
        System.out.println(Thread.currentThread().getName()+"写入完毕");
    }
    // 读
    public void get(String key) {
        Object o = map.get(key);
        System.out.println("读取"+o);
    }
}

阻塞队列

写入时,如果队列满了,必须阻塞等待

读取时,如果队列空的,必须阻塞等待

抛出异常

/*
      抛出异常
     */
public static void test1() {
    ArrayBlockingQueue  blockingQueue = new ArrayBlockingQueue<>(3);
    System.out.println(blockingQueue.add("A"));
    System.out.println(blockingQueue.add("B"));
    System.out.println(blockingQueue.add("C"));
    // 再添加会抛出异常java.lang.IllegalStateException: Queue full
    System.out.println(blockingQueue.remove());
    System.out.println(blockingQueue.remove());
    System.out.println(blockingQueue.remove());
    // 再移除会抛出异常java.util.NoSuchElementException

}

有返回值

public static void test2() {
    ArrayBlockingQueue  blockingQueue = new ArrayBlockingQueue<>(3);
    System.out.println(blockingQueue.offer("A"));
    System.out.println(blockingQueue.offer("B"));
    System.out.println(blockingQueue.offer("C"));
    // 不抛出异常,输出false
    System.out.println(blockingQueue.offer("A"));

    System.out.println(blockingQueue.peek());

    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    // 不抛出异常,返回null
    System.out.println(blockingQueue.poll());
}

阻塞等待

/*
    等待,一直阻塞
     */
public static void test3() throws InterruptedException {
    ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
    blockingQueue.put("A");
    blockingQueue.put("B");
    blockingQueue.put("C");
    // 会一直等
    //blockingQueue.put("D");
    System.out.println(blockingQueue.take());
    System.out.println(blockingQueue.take());
    System.out.println(blockingQueue.take());
    // 会一直阻塞
    //System.out.println(blockingQueue.take());
}

等待,超时退出

public static void test4() throws InterruptedException {
    ArrayBlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
    blockingQueue.offer("A");
    blockingQueue.offer("b");
    blockingQueue.offer("c");
    blockingQueue.offer("d",3, TimeUnit.SECONDS);
    System.out.println("============");
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    System.out.println(blockingQueue.poll());
    blockingQueue.poll(2,TimeUnit.SECONDS);
}

SynchronousQueue

进去一个元素,必须等待取出来才能再放入

public class SynchronousQueueTest {
    public static void main(String[] args) {
        SynchronousQueue<String> queue = new SynchronousQueue<>();
        new Thread(()->{

            try {
                System.out.println(Thread.currentThread().getName()+"=="+"1");
                queue.put("A");
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+"=="+"2");
                queue.put("B");
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+"=="+"3");
                queue.put("C");

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t1").start();
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+queue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+queue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName()+queue.take());

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t2").start();
    }
}

线程池(重点)

程序运行会占用系统资源,创建和销毁十分浪费资源,优化资源的使用---池化技术

池化技术:事先准备好一些资源,有人要,从我这里拿

线程池的好处:

  1. 降低资源的消耗
  2. 提高响应的速度
  3. 方便管理

线程池三大方法

 public static void main(String[] args) {
        //ExecutorService pool = Executors.newSingleThreadExecutor();// 单个线程池
        //ExecutorService pool = Executors.newFixedThreadPool(3);// 创建固定大小的线程池
        ExecutorService pool = Executors.newCachedThreadPool(); // 可变的线程池,最大与cpu有关

        try {
            for (int i = 0; i < 10; i++) {
                pool.execute(()->{
                    System.out.println(Thread.currentThread().getName());
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 线程池使用完要关闭
            pool.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,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

本质都是ThreadPoolExecutor
    public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
                              int maximumPoolSize, // 最大线程池大小
                              long keepAliveTime,// 超时时间
                              TimeUnit unit, // 超时单位
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler // 拒绝策略) {

阿里巴巴开发规范,建议使用ThreadPoolExecutor创建

// 自定义线程池
ExecutorService pool = new ThreadPoolExecutor(2,
                                              5,
                                              3,
                                              TimeUnit.SECONDS,
                                              new LinkedBlockingDeque<>(3),
                                              Executors.defaultThreadFactory(),
                                              new ThreadPoolExecutor.DiscardOldestPolicy());


try {
    // 最大承载=maximunPoolsize5+队列的大小3
    for (int i = 1; i <= 10; i++) {
        pool.execute(()->{
            System.out.println(Thread.currentThread().getName());
        });
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    // 线程池使用完要关闭
    pool.shutdown();
}

拒绝策略

new ThreadPoolExecutor.AbortPolicy()  满了还有线程要执行,抛出异常
new ThreadPoolExecutor.CallerRunsPolicy() 哪里来的去哪里调用
new ThreadPoolExecutor.DiscardPolicy() 满了会丢掉任务,不会抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy() 满了会和最早的竞争,成功执行失败丢掉,不会抛出异常

cpu密集型和IO密集型

线程池大小设置

1.cpu密集型 根据cpu的核来,几核为几个 Runtime.getRuntime().availableProcessors()
2. IO密集型  判断程序中十分耗IO的线程

四大函数式接口

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

Function

/*
    Function函数型接口,给什么输出什么
    可以用lamda简化
     */
public static void main(String[] args) {
    Function function = new Function<String,Object>() {
        @Override
        public Object apply(String str) {
            return str;
        }
    };
    Function function2 = (str)->{return str;};
    System.out.println(function2.apply("aaa"));

}

Predicate

返回boolean

/*
        断定型接口
        一个参数,返回Bool
     */
public static void main(String[] args) {
    Predicate<String> predicate = new Predicate<String>() {
        @Override
        public boolean test(String str) {
            return str.isEmpty();
        }
    };
    Predicate<String> predicate2 = (str)->{return str.isEmpty();};
    System.out.println(predicate2.test(""));

}

Consumer

只有输入没有返回值

Consumer<String> consumer = new Consumer<String>() {
    @Override
    public void accept(String s) {
        System.out.println(s);
    }
};

Consumer<String> consumer2 = (s) ->{
    System.out.println(s);
};

Supplier

没有输入,有返回

Supplier<String> supplier = new Supplier<String>() {
    @Override
    public String get() {
        return "A";
    }
};
System.out.println(supplier.get());

Supplier supplier2 = ()->{return "B";};
System.out.println(supplier2.get());

Stream流式计算

 /*
    id大于2
    年龄大于20
    用户名转为大写字母
    用户名字母倒序
    只输出一个用户
     */
    public static void main(String[] args) {
        User u1 = new User(1, "a", 12);
        User u2 = new User(2, "b", 16);
        User u3 = new User(3, "c", 22);
        User u4 = new User(4, "d", 26);
        User u5 = new User(5, "e", 33);

        List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
        // 计算交给stream流
        list.stream()
                .filter((user)->{return user.getId() > 2;})
                .filter(user -> {return user.getAge() > 20;})
                .map(user -> {return user.getName().toUpperCase();})
                .sorted((n1,n2)->{return n2.compareTo(n1);})
                .limit(1)
                .forEach(System.out::println);
    }

ForkJoin

并行执行任务,提高效率,大数据量

ForkJoin特点:工作窃取

image

image

forkjoin测试类

public class ForkJoin extends RecursiveTask<Long> {
    /*
        forkjoinpool来执行
        计算任务forkjoin.execute(Forkjointask)

     */
    private long start;
    private long end;

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

    public ForkJoin(long start, long end) {
        this.start = start;
        this.end = end;
    }

    public void getSum() {
        if((end-start) > temp) {
            // 如果计算的量很大,使用forkjoin分支合并计算
        } else {
            Long sum  = 0L;
            for (Long i = start; i < end ; i++) {
                sum += i;
            }
            System.out.println(sum);
        }
    }

    @Override
    protected Long compute() {
        if((end-start) < temp) {
            Long sum  = 0L;
            for (Long i = start; i < end ; i++) {
                sum += i;
            }
            return sum;
        } else {
            // 如果计算的量很大,使用forkjoin分支合并计算
            Long middle  = (start + end)/2;
            ForkJoin task1 = new ForkJoin(start, middle);
            task1.fork(); // 拆分任务,把任务压入线程队列
            ForkJoin task2 = new ForkJoin(middle + 1, end);
            task2.fork();

            return task1.join()+task2.join();
        }
    }
}

使用forkjion和并行流

// 使用forkjoin
public static void test2() throws ExecutionException, InterruptedException {
    long start = System.currentTimeMillis();

    ForkJoinPool forkJoinPool = new ForkJoinPool();
    ForkJoinTask<Long> task = new ForkJoin(1L,100000000L);
    ForkJoinTask<Long> submit = forkJoinPool.submit(task);
    Long sum = submit.get();

    long end = System.currentTimeMillis();
    System.out.println("sum="+sum+"----时间"+(end-start));
}

public static void test3() {

    long start = System.currentTimeMillis();

    // stream并行流
    long sum;
    sum = LongStream.rangeClosed(0L, 100000000L).parallel().reduce(0, Long::sum);

    long end = System.currentTimeMillis();
    System.out.println("sum=" + sum + "----时间" + (end - start));
}

异步回调

/*
      异步调用:CompletableFucture
      异步执行
      成功回调
      失败回调
     */
public static void main(String[] args) throws ExecutionException, InterruptedException {

    // 发起一个请求
    CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
        System.out.println(Thread.currentThread().getName());
    });
    System.out.println("aaa");
    completableFuture.get();

    // 有返回值的supplyAsync异步回调
    CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread().getName());
        //int a = 12/0;
        return 12;
    });
    Integer result = completableFuture1.whenComplete((t, u) -> {
        System.out.println("t==" + t); // 正常的返回结果
        System.out.println("u==" + u); // 错误的信息
    }).exceptionally((e) -> {
        System.out.println(e.getMessage());
        return 404;  // 可以获取到错误的返回结果
    }).get();
    System.out.println(result);

JMM(java内存模型)

JMM:java内存模型,是概念,约定

关于JMM同步的约定:

  • 线程解锁前,必须把共享变量立刻更新到主存
  • 线程加锁前,必须读取主存中变量最新值到工作内存中
  • 加锁和解锁是同一把锁

8种操作

read读取 load载入 use使用 assign赋值 write写入 store存储 lock加锁 unlock解锁

image

问题

主线程虽然修改了,主存中a为1,但是第一个线程a还是0,程序不会停

private static int a = 0;
public static void main(String[] args) throws InterruptedException {
    new Thread(()->{
        while (a == 0) {

        }
    }).start();

    TimeUnit.SECONDS.sleep(1);
    a = 1; // main线程修改
    System.out.println(a);
}

解决使用Volatile

Volatile

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

  • 保证可见性
  • 不保证原子性
  • 禁止指令重排

保证可见性

// 加volatile可以保证可见性,主线程main修改了a 第一个线程会知道
private volatile static int a = 0;
public static void main(String[] args) throws InterruptedException {
    new Thread(()->{
        while (a == 0) {

        }
    }).start();

    TimeUnit.SECONDS.sleep(1);
    a = 1; // main线程修改
    System.out.println(a);
}

不保证原子性

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

public static void add() {
    num++;
}

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

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

    System.out.println(num);
}

解决:把add方法加synchronized,或者使用lock锁

使用原子类也可以解决

image

private volatile static AtomicInteger num = new AtomicInteger(0);

public static void add() {
    num.getAndIncrement();
}

public static void main(String[] args) {

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

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

    System.out.println(num);
}

指令重排

写的程序,计算机并不是按照写的那样去执行的

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

处理器在进行指令重排的适合,会考虑数据之间的依赖性

int a = 1; // 1
int b = 2; // 2
a = a + 5; // 3
b = a * a; // 4
期望的顺序是1234 
执行的顺序可能是2134  1324,对程序的运行结果没有影响

问题:

设xyab默认为0

线程A 线程B
x=a y=b
b=1 a=2

指令重排的话

线程A 线程B
b=1 a=2
x=a y=b

这样xy的数值会有问题

violate可以避免指令重排

内存屏障

image

单例模式

饿汉式

// 饿汉式单例
public class Hungry {
    private Hungry() {
    }

    // 会浪费空间
    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance() {
        return HUNGRY;
    }
}

懒汉式

// 懒汉式单例
public class LazyMan {
    private LazyMan(){
        System.out.println(Thread.currentThread().getName());
    }

    private volatile static LazyMan lazyMan;

    // 双重检查锁模式的 懒汉式单例 DCL懒汉式
    public static LazyMan getInstance() {
        if(lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null)
                    lazyMan = new LazyMan(); // 不是一个原子性操作
                    /*
                        分配内存空间
                        执行构造方法,初始化对象
                        把这个对象指向这个空间
                     */
                // 使用volatile防止指令重排
            }
        }
        return lazyMan;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10 ; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }
    }
}

静态内部类

public class Holder {
    private Holder() {

    }

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

单例不安全可以通过反射破坏

第一种,使用反射获取构造器

// 懒汉式单例
public class LazyMan {
    private LazyMan() {
        System.out.println(Thread.currentThread().getName());
    }

    private static LazyMan lazyMan;

    public static LazyMan getInstance() {
        if (lazyMan == null) {
            synchronized (LazyMan.class) {
                if (lazyMan == null) {
                    lazyMan = new LazyMan();
                }
            }
        }

        return lazyMan;
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        LazyMan instance = LazyMan.getInstance();
        LazyMan instance2 = LazyMan.getInstance();
        System.out.println(instance == instance2); // 正常,true

        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        // 设置在使用构造器的时候不执行权限检查,破坏程序的封装性和安全性
        declaredConstructor.setAccessible(true);
        LazyMan instance3 = declaredConstructor.newInstance();
        LazyMan instance4 = declaredConstructor.newInstance();
        //问题1:
            System.out.println(instance == instance3); // false

    }
}

问题1解决,修改构造函数,加同步锁

private LazyMan() {
    synchronized (LazyMan.class) {
        if (lazyMan != null) {
            throw new RuntimeException("error");
        }
    }
}

问题2

Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
// 设置在使用构造器的时候不执行权限检查,破坏程序的封装性和安全性
declaredConstructor.setAccessible(true);
LazyMan instance3 = declaredConstructor.newInstance();
LazyMan instance4 = declaredConstructor.newInstance();
System.out.println(instance3 == instance4); // false

问题2,解决

private static boolean henry = false;
private LazyMan() {
    synchronized (LazyMan.class) {
        if (!henry) {
            henry = true;
        } else {
            throw new RuntimeException("error");
        }
    }
}

问题是,如果知道henry变量的名字,还是可以破坏单例

Field field = LazyMan.class.getDeclaredField("henry");
field.setAccessible(true);

Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
// 设置在使用构造器的时候不执行权限检查,破坏程序的封装性和安全性
declaredConstructor.setAccessible(true);
LazyMan instance3 = declaredConstructor.newInstance();
// 修改henry变量值
field.set(instance3,false);
LazyMan instance4 = declaredConstructor.newInstance();
System.out.println(instance3 == instance4); // false

枚举类

public enum  EnumSingle {
    INSTANCE;

    public EnumSingle getInstance() {
        return INSTANCE;
    }
}

class Test{
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        EnumSingle instance1 = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        EnumSingle instance2 = declaredConstructor.newInstance();
        // java.lang.NoSuchMethodException: com.single.EnumSingle.<init>()
        System.out.println(instance1 == instance2);

    }
}

反编译EnumSingle.class,发现也有个无参构造

使用Jad程序EnumSinle.class变为java文件,实际上是有个两个参数的构造

修改

EnumSingle instance1 = EnumSingle.INSTANCE;
Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
declaredConstructor.setAccessible(true);
EnumSingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1 == instance2);

报错,提示不能破坏枚举单例

深入理解CAS

// CAS 比较并交换
public static void main(String[] args) {
    AtomicInteger atomicInteger = new AtomicInteger(20);
    // 期望,更新
    // public final boolean compareAndSet(int expect, int update)
    // 如果期望的值达到了则更新
    atomicInteger.compareAndSet(20, 21);
    System.out.println(atomicInteger.get());
    // 期望20实际上是21,不更新
    System.out.println(atomicInteger.compareAndSet(20, 22));
}

cas底层使用unsafe类

image

image

cas:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,则执行操作,如果不是就一直循环(自旋锁)

缺点:循环会耗时,一次只能保证一个共享变量的原子性,ABA问题

ABA问题

image

AtomicInteger atomicInteger = new AtomicInteger(20);
// 捣乱的线程
atomicInteger.compareAndSet(20, 21);
atomicInteger.compareAndSet(21, 20);

// 正常的线程
atomicInteger.compareAndSet(20,22);
// 虽然这个时候期望值的还是20,但是被动过了
System.out.println(atomicInteger.get());

原子引用

AtomicStampedReference和乐观锁原理类似,使用版本号

public static void main(String[] args) {
    AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(20, 1);
    new Thread(()->{
        // 获得版本号
        int stamp = stampedReference.getStamp();
        System.out.println(Thread.currentThread().getName()+"=="+stamp);

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(stampedReference.compareAndSet(20, 21, stampedReference.getStamp(), stampedReference.getStamp() + 1));
        System.out.println("a2="+stampedReference.getStamp());
        System.out.println(stampedReference.compareAndSet(21, 20, stampedReference.getStamp(), stampedReference.getStamp() + 1));

    },"a").start();

    new Thread(()->{
        // 和乐观锁原理类似
        int stamp = stampedReference.getStamp();
        System.out.println(Thread.currentThread().getName()+"=="+stamp);
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(stampedReference.compareAndSet(20,22,stamp,stamp+1));;

        System.out.println("b2="+stampedReference.getStamp());
    },"a").start();
}

各种锁

公平锁、非公平锁

公平锁:非常公平,不能插队

非公平锁:不公平,可以插队

ReentrantLock lock = new ReentrantLock();
// 实现
public ReentrantLock() {
    sync = new NonfairSync();
}
// 默认是非公平锁
// 设置参数true
ReentrantLock lock = new ReentrantLock(true);

可重入锁

可以重复获取相同的锁,synchronized和ReentranLock都是可重入的

synchronized

public class LockDemo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        // 线程a和b都是用的phone这个对象锁
        new Thread(()->{
            phone.sms();
        },"a").start();

        new Thread(()->{
            phone.sms();
        },"b").start();
    }
}

class Phone{

    public synchronized void sms() {
        System.out.println(Thread.currentThread().getName()+"发短信");
        call();
    }

    public synchronized void call() {
        System.out.println(Thread.currentThread().getName()+"打电话");
    }
}

lock,是两把锁

class Phone{
    Lock lock = new ReentrantLock();
    public void sms() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"发短信");
            call();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public  void call() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()+"打电话");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
 }

自旋锁

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 myUnlock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"==mylock");
        atomicReference.compareAndSet(thread,null);
    }
}

死锁

互相想要对方的资源

public class Slock {
    public static void main(String[] args) {
        String source1 = "s1";
        String source2 = "s2";
        new Thread(new MyThread(source1,source2),"t1").start();
        new Thread(new MyThread(source2,source1),"t2").start();
    }
}

class MyThread implements Runnable{
    String source1 = "s1";
    String source2 = "s2";

    public MyThread(String source1, String source2) {
        this.source1 = source1;
        this.source2 = source2;
    }

    @Override
    public void run() {
        synchronized (source1) {
            System.out.println(Thread.currentThread().getName()+"得到了"+source1+"等待"+source2);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (source2) {
                System.out.println(Thread.currentThread().getName()+"得到了"+source2+"等待"+source1);
            }
        }
    }
}

// t1得到了s1等待s2
// t2得到了s2等待s1

解决死锁问题

1.使用jps -l 定位到死锁的进程

image

2.使用jstack 进程号 找到死锁问题

image

posted @ 2021-03-26 17:09  Henry829  阅读(66)  评论(0)    收藏  举报