JAVA学习笔记25——JUC

JUC

Lock锁

Lock接口

ReentrantLock:可重入锁
ReentrantReadWriteLock.ReadLock:读锁
ReentrantReadWriteLock.WriteLock:写锁

    public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
    // 默认非公平锁
public class SaleTicketDemo02 {
    public static void main(String[] args) {
        // 并发,多线程操作同一个资源类
        Ticket2 ticket = new Ticket2();
        // lambda表达式(参数)->{代码}
        new Thread(()->{ for (int i = 0;i < 40;i++) ticket.sale();},"A").start();
        new Thread(()->{ for (int i = 0;i < 40;i++) ticket.sale();},"B").start();
        new Thread(()->{ for (int i = 0;i < 40;i++) ticket.sale();},"C").start();
    }
}

class Ticket2 {
    private int number = 30;
    Lock lock = new ReentrantLock();
    public synchronized void sale() {
        lock.lock();
        try {
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出了第" + (number--) + "张票,剩余:" + number);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Synchronized锁

public class SaleTicketDemo01 {
    public static void main(String[] args) {
        // 并发,多线程操作同一个资源类
        Ticket ticket = new Ticket();
        // lambda表达式(参数)->{代码}
        new Thread(()->{ for (int i = 0;i < 40;i++) ticket.sale();},"A").start();
        new Thread(()->{ for (int i = 0;i < 40;i++) ticket.sale();},"B").start();
        new Thread(()->{ for (int i = 0;i < 40;i++) ticket.sale();},"C").start();
    }
}

class Ticket {
    private int number = 30;
    public synchronized void sale() {
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出了第" + (number--) + "张票,剩余:" + number);
        }
    }
}

Synchronized锁与Lock锁区别
1.Synchronized是内置关键字,Lock是一个Java类
2.Synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁
3.Synchronized会自动释放锁,Lock需要手动释放锁
4.Synchronized线程一获得锁阻塞后,线程二会一直等待;Lock锁不一定会等待下去
5.Synchronized是可重入锁,不可以中断,非公平;Lock,可重入锁,可以判断锁,非公平
6.Synchronized适合少量的代码同步问题,Lock适合锁大量的同步代码

生产者消费者问题

Synchronized

public class Test1 {
    public static void main(String[] args) {
        Resource resource = new Resource();
        new Thread(()->{
            for (int i = 0;i < 10;i++) {
                try {
                    resource.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0;i < 10;i++) {
                try {
                    resource.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0;i < 10;i++) {
                try {
                    resource.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0;i < 10;i++) {
                try {
                    resource.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
class Resource {
    private int num = 0;
    public synchronized void increment() throws InterruptedException {
        while (num != 0) {
            // 等待
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"=>"+num);
        // 通知线程+1完毕
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException {
        while (num == 0) {
            // 等待
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName()+"=>"+num);
        // 通知线程-1完毕
        this.notifyAll();
    }
}

Lock

public class Test2 {
    public static void main(String[] args) {
        Resource resource = new Resource();
        new Thread(()->{
            for (int i = 0;i < 10;i++) {
                try {
                    resource.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0;i < 10;i++) {
                try {
                    resource.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0;i < 10;i++) {
                try {
                    resource.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0;i < 10;i++) {
                try {
                    resource.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}
class Resource2 {
    private int num = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    public synchronized void increment() throws InterruptedException {
        lock.lock();
        try {
            while (num != 0) {
                // 等待
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName()+"=>"+num);
            // 通知线程+1完毕
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public synchronized void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (num == 0) {
                // 等待
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+"=>"+num);
            // 通知线程-1完毕
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Condition精准的通知唤醒线程

// A执行完调用B,B执行完调用C,C执行完调用A
public class Test3 {
    public static void main(String[] args) {
        Resource3 resource3 = new Resource3();
        new Thread(()->{
            for (int i = 0;i < 10;i++) {
                resource3.printA();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0;i < 10;i++) {
                resource3.printB();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0;i < 10;i++) {
                resource3.printC();
            }
        },"C").start();
    }
}

class Resource3 {
    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();
            }
            System.out.println(Thread.currentThread().getName()+"=>AAA");
            number = 2;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printB() {
        lock.lock();
        try {
            while (number != 2) {
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>BBB");
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printC() {
        lock.lock();
        try {
            while (number != 3) {
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"=>CCC");
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

Callable

1.可以有返回值
2.可以抛出异常
3.方法不同

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        new Thread().start();
        MyThread thread = new MyThread();
        FutureTask<Integer> futureTask = new FutureTask<>(thread);
        new Thread(futureTask,"A").start();
        Integer integer = futureTask.get();
        System.out.println(integer);
    }
}
class MyThread implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("call方法");
        return 1024;
    }
}

辅助类

CountDowanLatch

// 减法计数器
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(5);
        for (int i = 0;i < 5;i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"go out");
                countDownLatch.countDown(); // -1
            },String.valueOf(i)).start();
        }
        countDownLatch.await(); // 等待计数器归零再向下执行
        System.out.println("finish");
    }
}

CyclicBarrier

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5,()->{
            System.out.println("finish");
        });
        for (int i = 0;i < 5;i++) {
            final int temp = i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"=>"+temp);
                try {
                    cyclicBarrier.await();  // 等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

Semaphore

信号量

public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0;i < 6;i++) {
            new Thread(()->{
                // acquire()得到
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+" in");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(Thread.currentThread().getName()+" out");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // release()释放
                    semaphore.release();
                }
            }).start();
        }
    }
}

原理:
acquire:获得,假设已经满了,等待被释放位置
release:释放,会将当前的信号量+1,然后唤醒等待的线程

读写锁

/**
 * 独占锁(写锁):一次只能被一个线程私有
 * 共享锁(读锁):多个线程可以同时占有
 */
public class ReadWriteLockTest {
    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 = 1;
            new Thread(()->{
                myCache.get(temp+"");
            },String.valueOf(i)).start();
        }
    }
}

class MyCacheLock{
    private volatile Map<String, Object> map = new HashMap<String,Object>();
    //读写锁
    private ReadWriteLock 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()+"写入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();
        }

    }
}

阻塞队列

写入:如果队列满了,就必须阻塞等待
读取:如果队列是空的,必须阻塞等待生产

//什么时候使用阻塞队列? 1、多线程并发处理  2、线程池
public class BlockingQueueTest {
    public static void main(String[] args) throws InterruptedException {
        //四组api
        //test1();
        //test2();
        //test3();
        test4();
    }

    //第一组 : add() remove()  抛出异常
    public static void test1() {
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(arrayBlockingQueue.add("a"));
        System.out.println(arrayBlockingQueue.add("b"));
        System.out.println(arrayBlockingQueue.add("c"));
        //发生异常:java.lang.IllegalStateException: Queue full
        //System.out.println(arrayBlockingQueue.add("d"));

        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        System.out.println(arrayBlockingQueue.remove());
        //java.util.NoSuchElementException
        //System.out.println(arrayBlockingQueue.remove());
    }

    //第二组: offer() poll() 返回false null
    public static void test2(){
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(arrayBlockingQueue.offer("a"));
        System.out.println(arrayBlockingQueue.offer("b"));
        System.out.println(arrayBlockingQueue.offer("c"));
        //返回false
        //System.out.println(arrayBlockingQueue.offer("d"));

        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        //返回null
        //System.out.println(arrayBlockingQueue.poll());
    }

    //第三组:  put() take()  一直阻塞
    public static void test3() throws InterruptedException {
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        arrayBlockingQueue.put("a");
        arrayBlockingQueue.put("b");
        arrayBlockingQueue.put("c");
        //一直阻塞
        //arrayBlockingQueue.put("d");

        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        System.out.println(arrayBlockingQueue.take());
        //一直阻塞
        //System.out.println(arrayBlockingQueue.take());
    }

    //第四组: offer() poll()
    public static void test4() throws InterruptedException {
        ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3);

        arrayBlockingQueue.offer("a");
        arrayBlockingQueue.offer("b");
        arrayBlockingQueue.offer("c");
        //超时退出
        //arrayBlockingQueue.offer("d",5, TimeUnit.SECONDS);

        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        System.out.println(arrayBlockingQueue.poll());
        //超时退出
        System.out.println(arrayBlockingQueue.poll(5,TimeUnit.SECONDS));
    }

}

SynchronousQueue同步队列

public class SynchronousQueueDemo {
    public static void main(String[] args) {
        //同步队列
        SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();

        //放元素
        new Thread(()->{
            try{
                System.out.println(Thread.currentThread().getName()+"放入1");
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName()+"放入2");
                synchronousQueue.put("2");
                System.out.println(Thread.currentThread().getName()+"放入3");
                synchronousQueue.put("3");
            }catch (Exception e){
                e.printStackTrace();
            }

        },"T1").start();

        //取元素
        new Thread(()->{
            try{
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"取到"+synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"取到"+synchronousQueue.take());
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+"取到"+synchronousQueue.take());
            }catch (Exception e){
                e.printStackTrace();
            }
        },"T2").start();
    }
}

线程池

优点

1.降低资源的消耗
2.提高响应效率,不用创建与销毁
3.方便管理

三大方法

newSingleThreadExecutor();// 单个线程
newFixedThreadPool();// 创建固定的线程池大小
newCachedThreadPool();// 创建可以伸缩的线程池

public class Demo01 {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();// 单个线程
        ExecutorService threadPool1 = Executors.newFixedThreadPool(5);// 创建固定的线程池大小
        ExecutorService threadPool2 = Executors.newCachedThreadPool();// 创建可以伸缩的线程池
        try {
            for (int i = 0;i < 10;i++) {
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

七大参数

    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;
    }

corePoolSize
核心线程池大小
maximumPoolSize
最大核心线程池大小
keepAliveTime
存活时间
unit
时间单位
workQueue
阻塞队列
threadFactory
线程工厂,创建线程
handle
拒绝策略

public class Demo01 {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        try {
            for (int i = 0;i < 8;i++){
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+" OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
    }
}

四种拒绝策略

当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务

CPU密集型与IO密集型

CPU密集型

        ExecutorService threadPool = new ThreadPoolExecutor(
                2,
                Runtime.getRuntime().availableProcessors(),
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

Runtime.getRuntime().availableProcessors():获取CPU数
IO密集型
判断程序中十分消耗IO的线程

四大函数式接口

只有一个方法的接口
函数型接口

// 函数型接口
public class Demo01 {
    public static void main(String[] args) {
//        Function function = new Function<String,String>() {
//            @Override
//            public String apply(String s) {
//                return s;
//            }
//        };
        // lambda表达式简化
        Function function = (str)->{return str;};
        System.out.println(function.apply("123"));
    }
}

断定型接口

// 断定型接口:有一个输入参数,返回只能是布尔值
public class Demo02 {
    public static void main(String[] args) {
        // 判断字符串是否为空
//        Predicate<String> predicate = new Predicate<String>() {
//            @Override
//            public boolean test(String s) {
//                return s.isEmpty();
//            }
//        };
        Predicate<String> predicate = (str)->{return str.isEmpty();};
        System.out.println(predicate.test("123"));
    }
}

消费型接口

/*
* 消费型接口,只有输入没有返回值
* */
public class Demo03 {
    public static void main(String[] args) {
//        Consumer<String> consumer = new Consumer<String>() {
//            @Override
//            public void accept(String s) {
//                System.out.println(s);
//            }
//        };
        Consumer<String> consumer = (str)->{ System.out.println(str); };
        consumer.accept("123");
    }
}

供给型接口

/*
* 供给型接口,没有参数只有返回值
* */
public class Demo04 {
    public static void main(String[] args) {
//        Supplier<String> supplier = new Supplier<String>() {
//            @Override
//            public String get() {
//                return "123";
//            }
//        };
        Supplier<String> supplier = ()->{return "123";};
        System.out.println(supplier.get());
    }
}

Stream流式计算

/**
 * 题目要求:一分钟内完成此题,只能用一行代码实现!
 * 现在有5个用户!筛选:
 * 1、密码必须是偶数
 * 2、年龄必须大于23岁
 * 3、用户名转为大写字母
 * 4、用户名字母倒着排序
 * 5、只输出一个用户!
 */
//mysql,集合本质是来放东西  计算的东西应该交给流
public class Stream {
    public static void main(String[] args) {
        User1 u1 = new User1("qk1","1",21);
        User1 u2 = new User1("Hk2","2",22);
        User1 u3 = new User1("kk3","3",23);
        User1 u4 = new User1("Hk4","4",24);
        User1 u5 = new User1("ak5","5",25);
        //集合就是存储
        List<User1> list = Arrays.asList(u1, u2, u3 ,u4 ,u5);
        //流来计算
        list.stream()
                .filter((u)->{ return u.getAge()%2 == 0; })
                .filter((u)->{ return u.getAge() > 23; })
                .map((u)->{return u.getName().toUpperCase();})
                .sorted((o1,o2)->{return o2.compareTo(o1);})
                .limit(1)
                .forEach(System.out::print);
    }
}

ForkJoin

并行执行任务,提高效率
特点
工作窃取

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;
    }
    public static void main(String[] args) {

    }
    @Override
    protected Long compute() {
        if ((end - start) < temp) {
            Long sum = 0L;
            for (Long i = start;i <= end;i++) {
                sum += i;
            }
            return sum;
        }
        else {
            long middle = (start + end) / 2;
            ForkJoinDemo forkJoinDemo1 = new ForkJoinDemo(start, middle);
            forkJoinDemo1.fork();   // 把任务压入线程队列
            ForkJoinDemo forkJoinDemo2 = new ForkJoinDemo(middle + 1, end);
            forkJoinDemo2.fork();
            return forkJoinDemo1.join() + forkJoinDemo2.join();
        }
    }
}

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        test1();
        test2();
        test3();
        // sum=500000000500000000时间4581
        // sum=500000000500000000时间3855
        // sum=500000000500000000时间141
    }
    public static void test1() {
        long start = System.currentTimeMillis();
        Long sum = 0L;
        for (Long i = 1L;i <= 10_0000_0000;i++) {
            sum += i;
        }
        long end = System.currentTimeMillis();
        System.out.println("sum=" + sum + "时间" + (end - start));
    }

    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));
    }

    public static void test3() {
        long start = System.currentTimeMillis();
        long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);
        long end = System.currentTimeMillis();
        System.out.println("sum=" + sum + "时间" + (end - start));
    }
}

JMM

Java内存模型
关于JMM的同步的约定
1.线程解锁前必须把共享变量立刻刷回主存
2.线程加锁前必须读取主存中的最新值到工作内存中
3.加锁与解锁是同一把锁
八种操作
Read(读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用;
load(载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中;
Use(使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令;
assign(赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中;
store(存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用;
write(写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中;
lock(锁定):作用于主内存的变量,把一个变量标识为线程独占状态;
unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定;
相应规定
JMM对这8种操作给了相应的规定:
不允许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虚拟机提供的轻量级同步机制
1.保证可见性
2.不保证原子性
3.禁止指令重排
保证可见性

public class JMMDemo {
    private static volatile int num = 0;
    public static void main(String[] args) {
        new Thread(()->{
            while (num == 0);
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        num = 1;
        System.out.println(num);
    }
}

不加volatile程序会死循环
不保证原子性

public class VDemo02 {
    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) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+" "+num);
    }
}

如果不加lock与synchronized如何保证原子性
使用原子类

public class VDemo02 {
    private volatile static AtomicInteger num = new AtomicInteger();
    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) {
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+" "+num);
    }
}

禁止指令重排
源代码=》编译器优化的重排=》指令并行也可能重排=》内存系统重排
volatile避免指令重排
内存屏障作用:
1.保证特定的操作的执行顺序
2.可以保证某些变量的内存可见性

单例模式

饿汉式

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

懒汉式

// 懒汉式单例,单线程下可行,多线程并发
public class Lazy {
    private Lazy() {
        System.out.println(Thread.currentThread().getName()+" OK");
    }
    private volatile static Lazy LAZY;
    // 双重检测锁模式的懒汉式单例
    public static Lazy getInstance() {
        if (LAZY == null) {
            synchronized (Lazy.class) {
                if (LAZY == null) {
                    LAZY = new Lazy();  // 不是原子性操作
                    /*
                    * 可能出现问题
                    * 1.分配内存空间
                    * 2.执行构造方法
                    * 3.把这个对象指向这个空间
                    * 
                    * 若132执行 A
                    * B 此时Lazy还没有完成构造
                    * */
                }
            }
        }
        return LAZY;
    }
    public static void main(String[] args) {
        for (int i = 0;i < 10;i++) {
            new Thread(()->{
                Lazy.getInstance();
            }).start();
        }
    }
}

CAS

public class CASDemo {
    // CAS:compareAndSet 比较再交换
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2020);
        // 如果期望的值达到了,那么就更新,否则不更新
        System.out.println(atomicInteger.compareAndSet(2020, 2021));
        System.out.println(atomicInteger.get());
    }
}

原理

    public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {
            v = getIntVolatile(o, offset);
        } while (!weakCompareAndSetInt(o, offset, v, v + delta));
        return v;
    }

获取内存地址中的值为v,当内存地址值还是v,v更新为v+delta;自旋锁
CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作,;否则循环(自旋锁)
缺点
1.自旋锁循环会浪费时间
2.一次性只能保证一个共享变量的原子性
3.ABA问题

原子引用

带版本号的原子操作

public class CASDemo {
    // CAS:compareAndSet 比较再交换
    public static void main(String[] args) {
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(99, 1);
        new Thread(()->{
            int stamp = atomicStampedReference.getStamp();   // 获得版本号
            System.out.println("A1=>"+stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicStampedReference.compareAndSet(99, 100,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("A2=>"+atomicStampedReference.getStamp());
            System.out.println(atomicStampedReference.compareAndSet(100, 99,
                    atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            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(99, 1,
                    stamp, stamp + 1));
            System.out.println("B1=>"+atomicStampedReference.getStamp());
        },"B").start();
    }
}

公平锁,非公平锁
公平锁:先来后到
非公平锁:可以允许插队
可重入锁
递归锁
自旋锁

    public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {
            v = getIntVolatile(o, offset);
        } while (!weakCompareAndSetInt(o, offset, v, v + delta));
        return v;
    }

死锁

public class DeadLockDemo {
    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";
        new Thread(new MyThread(lockA,lockB),"thread1").start();
        new Thread(new MyThread(lockB,lockA),"thread2").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+"=>"+lockB);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lockB) {
                System.out.println(Thread.currentThread().getName()+" lock:"+lockB+"=>"+lockA);

            }
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值