多线程 + JUC

线程

线程分守护线程和用户线程,虚拟机不在乎守护线程,如果用户线程停止,守护线程也会停止

public class ThreadDemo {
    public static void main(String[] args) {
        God god = new God();
        People people = new People();
        Thread thread = new Thread(god);
        thread.setDaemon(true);//设置守护线程

        //理论上god线程应该不会停止,但是设置成了守护线程
        //在people线程停止后,守护线程也会停止
        thread.start();
        new Thread(people).start();
    }
}

class God implements Runnable{
    @Override
    public void run() {
        while (true){
            System.out.println("守护线程");
        }
    }
}

class People implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("用户线程:" + i);
        }
    }
}

线程创建的三种方式

  • 继承Thread类
    class PrimeThread extends Thread {
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }
    
         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
     
    PrimeThread p = new PrimeThread(143);
    p.start();
    
  • 实现Runnable接口
    class PrimeRun implements Runnable {
         long minPrime;
         PrimeRun(long minPrime) {
             this.minPrime = minPrime;
         }
    
         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
     
     PrimeRun p = new PrimeRun(143);
     //静态代理模式
     new Thread(p).start();
    
  • 实现Callable接口
    public class CallableDemo implements Callable<Boolean> {
        private String name;
    
        public CallableDemo(String name) {
            this.name = name;
        }
    
        @Override
        public Boolean call() throws Exception {
            System.out.println("call...... " + name);
            return true;
        }
    
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            CallableDemo zhangsan = new CallableDemo("zhangsan");
            CallableDemo lisi = new CallableDemo("lisi");
            CallableDemo wangwu = new CallableDemo("wangwu");
            ExecutorService service = Executors.newFixedThreadPool(3);
            Future<Boolean> submit1 = service.submit(zhangsan);
            Future<Boolean> submit2 = service.submit(lisi);
            Future<Boolean> submit3 = service.submit(wangwu);
    
            Boolean aBoolean = submit1.get();
    
            service.shutdownNow();
        }
    }
    

调用start方法启动线程,由cpu决定何时运行

线程方法

方法说明
setPriority(int newPriority)更改线程优先级
static native void sleep(long millis)指定毫秒数内当前线程休眠
void join()等待该线程终止,主线程在for循环,A线程调用join,则主线程阻塞,等待A线程执行完毕。
static native void yield();暂停当前正在执行的线程,并执行其他线程
public void interrupt()中断线程

同步方法和同步代码块

同步方法:

public class ThreadDemo {
    public synchronized void method1(){
     	Thread.sleep(1000);
        System.out.println("同步方法");
    }
        public synchronized void method2(){
        System.out.println("同步方法");
    }
}    

同步代码块


	synchronized(Object obj){
		 System.out.println("同步代码块");
	}
  	

synchronized 默认锁的是this。使用同步代码块可以指定锁定的对象。
也就是说
ThreadDemo demo = new ThreadDemo();
多个线程调用demo的method()方法,无论是method1还是method2,先拿到锁的先调用

public static void main(String[] args) {
ThreadDemo demo = new ThreadDemo();
        new Thread(() -> {
                demo.method1();
        }, "A").start();
        new Thread(() -> {
                demo.method2();
        }, "B").start();
        // 无论如何都会先执行method1
}

如果是static方法,锁的是Class
没有的static,锁的是new ThreadDemo()这个具体的类

wait和sleep

  • wait是object类的,sleep是Thread类的
  • wait会释放锁,sleep不会释放锁
  • wait必须在同步代码块中使用,sleep任何地方都可以使用

死锁

public class ThreadDemo {
    public static void main(String[] args) {
        Death zhangsan = new Death(0, "zhangsan");
        Death lisi = new Death(1, "lisi");
        //都拿自己的锁,去尝试获得对方的锁。结果造成死锁
        zhangsan.start();
        lisi.start();
    }

}

class God {

}

class People {

}

class Death extends Thread {
    static God god = new God();
    static People people = new People();
    int choice;
    String name;

    public Death(int choice, String name) {
        this.choice = choice;
        this.name = name;
    }

    @SneakyThrows
    @Override
    public void run() {
        choice();
    }

    private void choice() throws InterruptedException {
        if (choice == 0){
            synchronized (god){
                System.out.println("获得 god 的锁");
                Thread.sleep(1000);
                synchronized (people){
                    System.out.println("获得 people 的锁");
                }
            }
        }else {
            synchronized (people){
                System.out.println("获得 people 的锁");
                Thread.sleep(1000);
                synchronized (god){
                    System.out.println("获得 god 的锁");
                }
            }
        }
    }
}

解决方法

    private void choice() throws InterruptedException {
        if (choice == 0) {
            synchronized (god) {
                System.out.println("获得 god 的锁");
                Thread.sleep(1000);
            }
            synchronized (people) {
                System.out.println("获得 people 的锁");
            }
        } else {
            synchronized (people) {
                System.out.println("获得 people 的锁");
                Thread.sleep(1000);
            }
            synchronized (god) {
                System.out.println("获得 god 的锁");
            }
        }
    }

JUC

synchronized

public class ThreadDemo {
    public static void main(String[] args) {
        Product product = new Product();
        new Thread(()-> {
            for (int i = 0; i < 60; i++) {
                product.sell();
            }
        },"A").start();
        new Thread(()-> {
            for (int i = 0; i < 60; i++) {
                product.sell();
            }
        },"B").start();
        new Thread(()-> {
            for (int i = 0; i < 60; i++) {
                product.sell();
            }
        },"C").start();
    }

}

class Product {

    private Integer num = 50;

    public synchronized void sell(){
        if (num > 0){
            System.out.println(Thread.currentThread().getName() + "卖出了:1张 ,还剩:" + (--num));
        }
    }
}

Lock
格式:

 Lock l = ...; l.lock(); try { // access the resource protected by this lock } finally { l.unlock(); } 
public class ThreadDemo {
    public static void main(String[] args) {
        Product product = new Product();
        new Thread(() -> {
            for (int i = 0; i < 60; i++) {
                product.sell();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 60; i++) {
                product.sell();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 60; i++) {
                product.sell();
            }
        }, "C").start();
    }

}

class Product {

    private Integer num = 50;

    private Lock lock = new ReentrantLock();

    public void sell() {
        lock.lock();
        try {
            if (num > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出了:1张 ,还剩:" + (--num));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

区别:

  • synchronized内置java关键字,Lock是一个java类
  • synchronized无法判断获得锁状态,Lock可以判断是否获取了锁
  • synchronized会自动释放锁,Lock必须手动释放锁
  • synchronized其他线程尝试获得锁会等待,Lock比一定会等待下去
  • synchronized可重入锁,不可中断,非公平锁;Lock,可重入锁,可以判断中断,默认非公平
  • synchronized适合锁少量代码同步问题;Lock适合锁大量代码块

lock精确唤醒:Condition

/*
 * A 执行完调用B,B执行完调用C,C执行完调用A
 */
public class C {
    public static void main(String[] args) {
        Data3 data = new Data3();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printA();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printB();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printC();
            }
        }, "C").start();
    }
}
class Data3 {
     // 资源类 Lock
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int number = 1; 
    // number=1 A执行  number=2 B执行 number=3 C执行
    public void printA() {
        lock.lock();
        try {
            // 业务,判断-> 执行-> 通知
            while (number != 1) {
                // A等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>AAAAAAA");
            // 唤醒,唤醒指定的人,B
            number = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printB() {
        lock.lock();
        try {
            // 业务,判断-> 执行-> 通知
            while (number != 2) {
                // B等待
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>BBBBBBBBB");
            // 唤醒,唤醒指定的人,c
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printC() {
        lock.lock();
        try {
            // 业务,判断-> 执行-> 通知
            // 业务,判断-> 执行-> 通知
            while (number != 3) {
                // C等待
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>CCCCC ");
            // 唤醒,唤醒指定的人,A
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

公平锁、非公平锁
公平锁: 不能够插队,必须先来后到
非公平锁:可以插队

ReentrantLock

可重入锁(递归锁)
拿到外边的锁,就可以拿到里边的锁

public class Demo01 {
    public static void main(String[] args) {
        Phone phone = new 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() 
                                                           + "sms");
        call(); // 这里也有锁(sms锁 里面的call锁)
    }
    public synchronized void call(){
        System.out.println(Thread.currentThread().getName() 
                                                           + "call");
    }
}

自旋锁

在这里插入图片描述

Callable

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // new Thread(new Runnable()).start();// 启动Runnable
        // new Thread(new FutureTask<V>()).start();
        // new Thread(new FutureTask<V>( Callable )).start();
        //FutureTask是Runnable的实现类,可以传入Callable

        // new 一个MyThread实例
        MyThread thread = new MyThread();
        // MyThread实例放入FutureTask
        FutureTask futureTask = new FutureTask(thread); // 适配类
        new Thread(futureTask,"A").start();
        new Thread(futureTask,"B").start(); // call()方法结果会被缓存,提高效率,因此只打印1个call
        // 这个get 方法可能会产生阻塞!把他放到最后
        Integer o = (Integer) futureTask.get(); 
        // 或者使用异步通信来处理!
        System.out.println(o);// 1024
    }
}
class MyThread implements Callable<Integer> {
    @Override
    public Integer call() {
        System.out.println("call()"); // A,B两个线程会打印几个call?(1个)
        // 耗时的操作
        return 1024;
    }
}

常用的辅助类

CountDownLatch

减法计数器: 实现调用几次线程后 再触发某一个任务

public class CountDownLatchDemo {
    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();
        }
        countDownLatch.await(); // 等待计数器归零,然后再向下执行
        System.out.println("结束");
    }
}
CyclicBarrier

加法计数器:实现调用几次线程后 再触发某一个任务

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

        CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {
            System.out.println("线程执行10次后执行!");
        });
        for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 执行");
                try {
                    cyclicBarrier.await(); // 等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
Semaphore

计数信号量

public class SemaphoreDemo {
    public static void main(String[] args) {
        // 线程数量:停车位! 限流!、
        // 如果已有3个线程执行(3个车位已满),则其他线程需要等待‘车位’释放后,才能执行!
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i <=6 ; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();//获得,假设如果已经满了,等待,等待被释放为止
                    System.out.println(Thread.currentThread().getName()+"抢到车位");
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName()+"离开车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); // release() 释放 ,会将当前的信号量释放 + 1
                }
            },String.valueOf(i)).start();
        }
    }
}

读写锁 ReadWriteLock

/**
 * 独占锁(写锁) 一次只能被一个线程占有
 * 共享锁(读锁) 多个线程可以同时占有
 * ReadWriteLock
 * 读-读  可以共存!
 * 读-写  不能共存!
 * 写-写  不能共存!
 */
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCacheLock myCacheLock = new MyCacheLock();
        // 写入
        for (int i = 1; i <= 5 ; i++) {
            final int temp = i;
            new Thread(()->{
                myCacheLock.put(temp+"",temp+"");
            },String.valueOf(i)).start();
        }
        // 读取
        for (int i = 1; i <= 5 ; i++) {
            final int temp = i;
            new Thread(()->{
                myCacheLock.get(temp+"");
            },String.valueOf(i)).start();
        }
    }
}
/**
 * 自定义缓存
 * 加锁的
 */
class MyCacheLock{
    private volatile Map<String,Object> map = new HashMap<>();
    // 读写锁: 更加细粒度的控制
    private ReadWriteLock readWriteLock = new             
                                    ReentrantReadWriteLock();
    // private Lock lock = new ReentrantLock();
    // 存,写入的时候,只希望同时只有一个线程写
    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()
                                                       +"写入OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock();
        }
    }
    // 取,读,所有人都可以读!
    public void get(String key){
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()
                                                       +"读取"+key);
            Object o = map.get(key);
            System.out.println(Thread.currentThread().getName()
                                                       +"读取OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
    }
}

阻塞队列BlockingQueue

方式抛出异常有返回值,不抛出异常阻塞 等待超时等待
添加addoffer()put()offer(,)
移除removepoll()take()poll(,)
检测队首元素elementpeek()--
public class BlockingDeque {
    public static void main(String[] args) {
        // 队列的大小
        LinkedBlockingQueue<String> blockingQueue = new LinkedBlockingQueue<String>();

        new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName()
                        + " put 1");
                // put进入一个元素
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName()
                        + " put 2");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName()
                        + " put 3");
                blockingQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T1").start();
        new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName()
                        + "=>" + blockingQueue.take());
                System.out.println(Thread.currentThread().getName()
                        + "=>" + blockingQueue.take());
                System.out.println(Thread.currentThread().getName()
                        + "=>" + blockingQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "T2").start();

    }

}

SynchronousQueue 同步队列

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

public class SynchronousQueueDemo {
    public static void main(String[] args) {
        BlockingQueue<String> blockingQueue =
                                new SynchronousQueue<>(); // 同步队列
        new Thread(()->{
            try {
                System.out.println(Thread.currentThread().getName()
                                                           +" put 1");
                // put进入一个元素
                blockingQueue.put("1");
                System.out.println(Thread.currentThread().getName()
                                                           +" put 2");
                blockingQueue.put("2");
                System.out.println(Thread.currentThread().getName()
                                                           +" put 3");
                blockingQueue.put("3");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T1").start();
        new Thread(()->{
            try {
                // 睡眠3s取出一个元素
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()
                                           +"=>"+blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()
                                           +"=>"+blockingQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()
                                           +"=>"+blockingQueue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T2").start();
    }
}

线程池

线程池的好处:

  • 降低系统资源的消耗
  • 提高响应的速度
  • 方便管理

7大参数

public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小 
                          int maximumPoolSize, // 最大核心线程池大小 
                          long keepAliveTime, // 超时没有人调用就会释放 
                          TimeUnit unit, // 超时单位 
                          // 阻塞队列 
                          BlockingQueue<Runnable> workQueue, 
                          // 线程工厂:创建线程的,一般 不用动
                          ThreadFactory threadFactory,  
                          // 拒绝策略
                          RejectedExecutionHandler handle )

线程池:4种拒绝策略

  • new ThreadPoolExecutor.AbortPolicy()
    队列满了,不处理,抛出异常
  • new ThreadPoolExecutor.CallerRunsPolicy()
    哪来的去哪里!队列满了,拒绝任务,会在提交的主线程执行,可能会阻塞主线程。
  • new ThreadPoolExecutor.DiscardPolicy()
    队列满了,丢掉任务,不会抛出异常!
  • new ThreadPoolExecutor.DiscardOldestPolicy()
    队列满了,尝试去和最早的竞争,也不会抛出异常!

定义最大线程数

CPU密集型:几核就设置为几,可以保持CPU效率最高
IO密集型:判断任务中十分耗费IO的线程数,大于这个数,可以设置为2倍

四大函数式编程

lambda表达式、链式编程、函数式接口、Stream流式计算

函数式接口

只有一个方法的接口。函数式接口都可以用lambda表达式简化

Function 函数式接口:
在这里插入图片描述

		//Function<T, R>  传入参数T,返回参数R

        Function<String,Integer> function = new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
            	//具体实现
                return null;
            }
        };

	 //简化
	 Function<String,String> function = str->{return str;};
	 System.out.println(function.apply("aaaa"));

Predicate 断定型接口:
在这里插入图片描述

		// 传入一个T类型,返回一个boolean
        Predicate<String> predicate =(str) -> {return str.isEmpty();};
        System.out.println(predicate.test(""));//true

Consumer 消费型接口:
在这里插入图片描述

		//只接收参数,处理后不返回
        Consumer<String> consumer = (str)->{System.out.println(str);};
        consumer.accept("sdadasd");

Supplier 供给型接口:
在这里插入图片描述

		// 不传入参数,有返回
        Supplier<Integer> supplier = ()->{return 1024; };
        System.out.println(supplier.get());
Stream 流式计算

Stream 的很多方法都是传入函数式接口
过滤

/**
 * 题目要求:一分钟内完成此题,只能用一行代码实现!
 * 现在有5个用户!筛选:
 * 1、ID 必须是偶数
 * 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;
                })// ID 必须是偶数
                .filter(u -> {
                    return u.getAge() > 23;
                })// 年龄必须大于23岁
                // 用户名转为大写字母
                .map(u -> {
                    return u.getName().toUpperCase();
                })
                // 用户名字母倒着排序
                .sorted((uu1, uu2) -> {
                    return uu2.compareTo(uu1);
                })
                .limit(1)// 只输出一个用户!
                .forEach(System.out::println);
    }
}

计算

        List<Integer> list = Arrays.asList(1,44,165,4,654,5,484,446);

        IntSummaryStatistics summaryStatistics = list.stream().mapToInt((s) -> s).summaryStatistics();
        System.out.println("总和:" + summaryStatistics.getSum());
        System.out.println("平均数:" + summaryStatistics.getAverage());
        System.out.println("总个数:" + summaryStatistics.getCount());
        System.out.println("最大值:" + summaryStatistics.getMax());
        System.out.println("最小值:" + summaryStatistics.getMin());
        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);
        int sum = userList.stream().mapToInt(User::getAge).sum();

ForkJoin:并行执行任务

特点:工作窃取,里面维护的都是双端队列
在这里插入图片描述

**
 * 计算1加到1000000000
 */
public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
         test1(); // 6981
         test2(); // 4900
         test3(); // 196
    }
    //普通for循环
    public static void test1(){
        Long sum = 0L;
        long start = System.currentTimeMillis();
        for (Long i = 1L; i <= 10_0000_0000L; i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("sum="+sum+" 时间:"+(end-start));
    }
    // 使用ForkJoin
    public static void test2() throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> task = new ForkJoinDemo(
                0L, 10_0000_0000L);
        // 提交任务
        ForkJoinTask<Long> submit = forkJoinPool.submit(task);
        Long sum = submit.get();// 获得结果
        long end = System.currentTimeMillis();
        System.out.println("sum="+sum+" 时间:"+(end-start));
    }
    //使用Stream并行流
    public static void test3(){
        long start = System.currentTimeMillis();
        // Stream并行流 ()  (]
        long sum = LongStream
                .rangeClosed(0L, 10_0000_0000L) // 计算范围(,]
                .parallel() // 并行计算
                .reduce(0, Long::sum); // 输出结果
        long end = System.currentTimeMillis();
        System.out.println("sum="+sum+" 时间:"+(end-start));
    }
}
class ForkJoinDemo extends RecursiveTask<Long> {
    private Long start;  // 1
    private Long end;    // 1990900000
    // 临界值
    private Long temp = 500000L;
    public ForkJoinDemo(Long start, Long end) {
        this.start = start;
        this.end = end;
    }
    // 计算方法
    @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; // 中间值
            ForkJoinDemo task1 = new ForkJoinDemo(start, middle);
            task1.fork(); // 拆分任务,把任务压入线程队列
            ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);
            task2.fork(); // 拆分任务,把任务压入线程队列
            return task1.join() + task2.join();
        }
    }
}

异步回调Future

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

        System.out.println("1111");
        completableFuture1.get(); // 获取阻塞执行结果
        
        // 有返回值的 supplyAsync 异步回调
        // ajax,成功和失败的回调
        // 返回的是错误信息;
        CompletableFuture<Integer> completableFuture2 =
                CompletableFuture.supplyAsync(() -> {
                    System.out.println(Thread.currentThread().getName() + " ;异步回调,有返回值");
                    int i = 10 / 0;
                    return 1024;
                });
        System.out.println(completableFuture2.whenComplete((t, u) -> {
            System.out.println("t=>" + t); // 正常的返回结果
            System.out.println("u=>" + u); // 错误信息
        }).exceptionally((e) -> {
            System.out.println(e.getMessage());
            return 233; // 可以获取到错误的返回结果
        }).get());
    }

JMM

Java内存模型(java memory model)本身是一种抽象的概念,并不真实存在
内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的

  • lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态
  • unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
  • read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用
  • load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中
  • use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令
  • assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中
  • store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用
  • write (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中

JMM 对这八种指令的使用,制定了如下规则:

  • 不允许read和load、store和write操作之一单独出现。即使用了read必须load,使用了store必须write
  • 不允许线程丢弃他最近的assign操作,即工作变量的数据改变了之后,必须告知主存
  • 不允许一个线程将没有assign的数据从工作内存同步回主内存
  • 一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施use、store操作之前,必须经过assign和load操作
  • 一个变量同一时间只有一个线程能对其进行lock。多次lock后,必须执行相同次数的unlock才能解锁
  • 如果对一个变量进行lock操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新load或assign操作初始化变量的值
  • 如果一个变量没有被lock,就不能对其进行unlock操作。也不能unlock一个被其他线程锁住的变量
  • 对一个变量进行unlock操作之前,必须把此变量同步回主内存

Volatile

Volatile 是 Java 虚拟机提供轻量级的同步机制,类似于synchronized 但是没有其强大。

  • 保证可见性:让线程知晓主内存的变化
  • 不保证原子性:
  • 防止指令重排

单例模式

单例模式都要将构造器私有化

饿汉式

// 饿汉式单例
public class Hungry {
    // 可能会浪费空间
    private byte[] data = new byte[1024*1024];
    private Hungry(){
    }
    private final static Hungry HUNGRY = new Hungry();
    public static Hungry getInstance(){
        return HUNGRY;
    }
}

DCL(双重检测锁模式) 懒汉式

public class LazyMan {
    private static boolean csp = false;// 标志位
    // 单例不安全,因为反射可以破坏单例,如下解决这个问题:
    private LazyMan(){
        synchronized (LazyMan.class){
            if (csp == false){
                csp = true;
            }else {
                throw new RuntimeException("不要试图使用反射破坏异常");
            }
        }
    }

    // 原子性操作:避免指令重排
    private volatile static LazyMan lazyMan;
    // 双重检测锁模式的 懒汉式单例  DCL懒汉式
    public static LazyMan getInstance(){
        if (lazyMan==null){
            synchronized (LazyMan.class){
                if (lazyMan==null){
                    lazyMan = new LazyMan(); // 不是一个原子性操作
                }
            }
        }
        return lazyMan;
    }
}

单例不安全,因为反射可以破坏单例
枚举

public enum Direction {
    FRONT() {
        public void fun() {
            System.out.println("FROND:重写了fun()方法");
        }
    }, 
    BEHIND() {
        public void fun() {
            System.out.println("BEHIND:重写了fun()方法");
        }
    }, 
    LEFT() {
        public void fun() {
            System.out.println("LEFT:重写了fun()方法");
        }
    },
    RIGHT() {
        public void fun() {
            System.out.println("RIGHT:重写了fun()方法");
        }
    };
    
    public abstract void fun()[只需要把fun()方法修改为抽象方法,但不可以把Direction类声明为抽象类。];
}

CAS:比较并交换

AtomicInteger自增方法
atomicInteger.getAndIncrement();

    public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
		//比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!
        return var5;
    }

在这里插入图片描述

ABA问题:
现在有数据a = 1
A线程修改a 修改为2,
B线程要修改a 为3,
但是A线程执行的比较快,先将a 修改为2,之后又将a 修改为1
B线程仍能修改成功
解决ABA 问题,引入原子引用! 对应的思想:乐观锁!

public class CASDemo {
    /*
     * AtomicStampedReference 原子引用,
     * 如果泛型是一个包装类,注意对象的引用问题
     * 正常在业务操作,这里面比较的都是一个个对象
     */
    // 可以有一个初始对应的版本号 1
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1);

    // CAS compareAndSet : 比较并交换!
    public static void main(String[] args) {
        new Thread(() -> {
            // 获得版本号
            int stamp = atomicStampedReference.getStamp();
            System.out.println("a1=>" + stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet(1, 2,
                    atomicStampedReference.getStamp(), // 最新版本号
                    // 更新版本号
                    atomicStampedReference.getStamp() + 1);
            System.out.println("a2=>" + atomicStampedReference.getStamp());
            System.out.println(
                    atomicStampedReference.compareAndSet(
                            2,
                            1,
                            atomicStampedReference.getStamp(),
                            atomicStampedReference.getStamp() + 1) + " a");
            System.out.println("a3=>" + atomicStampedReference.getStamp());
        }, "a").start();
        // 乐观锁的原理相同!
        new Thread(() -> {
            // 获得版本号
            int stamp = atomicStampedReference.getStamp();
            System.out.println("b1=>" + stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(
                    atomicStampedReference.compareAndSet(
                            1, 5, stamp, stamp + 1) + " b");
            System.out.println("b2=>"
                    + atomicStampedReference.getStamp());
        }, "b").start();
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值