并发编程

2021/4/27 20:25:26

本文主要是介绍并发编程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

笔记

 

JUC并发编程1

 

1.线程和进程

 

 进程:一个程序,QQ...等

 

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

 

 

 

 java默认有几个线程?

 

 两个 main和GC

 

 

 

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

 

 对于java而言:Thread,Runnable,Callable

 

 

 

 java真的可以开启线程吗?不可以
//本地方法,调用底层C++,java无法直接操作硬件
private native void start0();

 

2.并发和并行  并发编程   并发(多线程操作同一个资源-不同时刻同时运行):     单核CPU,模拟出来多条线程,天下武功,为快不破,快速交替 并行(多个人一起行走-同一时刻同时运行):     多核CPU,多个线程可以同时执行;线程池   3.Lock锁 传统Synchronize
Interface Lock

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

 

 

 

公平锁:十分公平,可以先来后到

 

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

 

 

Lock lock = new ReentrantLock(); 创建锁
lock.lock();加锁
业务
lock.unlock();解锁

 

 

Synchronized和Lock的区别 1.Synchronized 内置的java关键字,Lock是一个java类 2.Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁 3.Synchronized 会自动释放锁,lock必须要手动释放锁!如果不释放锁,会产生死锁 4.Synchronized 线程1(获得锁,阻塞),线程2(等待,傻傻的等);lock锁就不一定会等待下去
lock.tryLock(); 尝试获得锁
5.Synchronized 可重入锁,不可以中断的,非公平;Lock,可重入锁,可以判断锁,非公平锁(可以自己设置) 6.Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码   4.生产者消费者模式   生产者和消费和问题Synchronized版 判断等待 (业务) 通知   5.锁是什么?如何判断锁的是谁?       8锁问题...  

 

 

class Phone{
    /**
     * synchronize锁的对象是方法的调用者
     * 两个方法用的是同一个锁,谁先拿到谁执行
     */
    public synchronized void sendSms() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("sendSms");
    }

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

}

 

 

public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        Phone1 phone = new Phone1();
        Phone1 phone2 = new Phone1();    两个不同的对象,没有关系,分开执行,先输出call
        //锁的存在
        new Thread(()->{
            try {
                phone.sendSms();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();


        TimeUnit.SECONDS.sleep(1);


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

class Phone1{
    /**
     * synchronize锁的对象是方法的调用者
     * 两个方法用的是同一个锁,谁先拿到谁执行
     */
    public synchronized void sendSms() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("sendSms");
    }

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

    //添加一个普通方法,没有锁
    public void hello(){
        System.out.println("hello");
    }

}

 

class Phone3{
    /**
     * synchronize锁的对象是方法的调用者
     * 两个方法用的是同一个锁,谁先拿到谁执行
     * 添加一个static,锁的是class
     */
    public static synchronized void sendSms() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("sendSms");
    }


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


    //添加一个普通方法,没有锁
    public void hello(){
        System.out.println("hello");
    }


}

 

public class Test3 {
    public static void main(String[] args) throws InterruptedException {
        Phone3 phone = new Phone3();
        Phone3 phone2 = new Phone3();
        //锁的存在
        new Thread(()->{
            try {
                phone.sendSms();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

class Phone3{
    /**
     * synchronize锁的对象是方法的调用者
     * 两个方法用的是同一个锁,谁先拿到谁执行
     * 添加一个static,锁的是class   谁先拿到锁谁先执行
     */
    public static synchronized void sendSms() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("sendSms");
    }

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

    //添加一个普通方法,没有锁
    public void hello(){
        System.out.println("hello");
    }

}

 

public class Test4 {
    public static void main(String[] args) throws InterruptedException {
        Phone4 phone = new Phone4();
        Phone4 phone2 = new Phone4();
        //锁的存在
        new Thread(()->{
            try {
                phone.sendSms();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

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

class Phone4{
    /**
     * synchronize锁的对象是方法的调用者
     * 锁的不是同一个对象,先输出call,再输出sendSms
     * 
     */
    public static synchronized void sendSms() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("sendSms");
    }

    //添加一个普通同步方法,没有锁
        public synchronized void call(){
        System.out.println("call");
    }

}

  重点理解锁的对象是谁

 

 

6.集合类不安全

 

/**
* 报错:ConcurrentModificationException
*/
public class ListTest {
    public static void main(String[] args) {
//        List<String> list = Arrays.asList("1","2","3");
//        list.forEach(System.out::println);
        //不安全的集合
//        List<String> list1 = new ArrayList<>();
//        解决方案
//        1.Vector
//        Vector<String> list1 = new Vector<>();
//        2.Collections.synchronizedxxx
//        List<String> list1 = Collections.synchronizedList(new ArrayList<>());
//        3.CopyOnWriteArrayList(写入时复制 COW)
        // 在写入的时候避免覆盖,造成数据问题
        List<String> list1 = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                list1.add(UUID.randomUUID().toString().substring(0,5));
//                list1.forEach(System.out::println);
                System.out.println(list1);
            },String.valueOf(i)).start();

        }

    }

}

 

 

7.Callable(简单)  1.可以有返回值  2.可以排除异常  3.方法不同,run()/call()    减法计数器CountDownLatch
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(7);

        for (int i = 0; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + "go to");
                countDownLatch.countDown(); //数量减一
            },String.valueOf(i)).start();
        }

        countDownLatch.await(); //等待计数器归零,然后再向下执行

        System.out.println("close door");

    }
}

 

 加法计数器CyclicBarrier

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("召唤神龙成功!");
        });
        for (int i = 1; i < 8; 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 < 7; 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();
                }
            },String.valueOf(i)).start();
        }
    }
}

 

8.读写锁ReentrantReadWriteLock

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

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

        for (int i = 0; i < 6; i++) {
        final int temp = i;
        new Thread(()->{
            myCache.get(temp+"");
        }).start();
    }
}

}

/*
   自定义缓存
*/
class MyCache{
    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() + "写入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();
        }

    }
}

 

JUC并发编程2

 

 

9.阻塞队列 BlockingQueue

 

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

 

 

 

学会使用队列

 

添加,移除

 

 

 

四组API

 

1)抛出异常 add  - remove

 

 

/**
     * 抛出异常
     */
    public static void test1(){
        //队列的大小
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.add("a");
        blockingQueue.add("b");
        blockingQueue.add("c");
//        blockingQueue.add("d");


        //FIFO 先进先出
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
    }

 

 

2)不会抛出异常 offer - poll

public static void test2(){
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);


    blockingQueue.offer("a");
    blockingQueue.offer("b");
    System.out.println(blockingQueue.offer("c"));
    //返回false,不抛出异常
    System.out.println(blockingQueue.offer("d"));


    blockingQueue.poll();
    
    //两种取出队首元素的方法
    System.out.println(blockingQueue.peek());
    System.out.println(blockingQueue.element());

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


}

 

3)阻塞等待 put  - take

public static void test3() throws InterruptedException {
        ArrayBlockingQueue 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());
    }

 

4)超时等待  offer - poll  的重载方法

public static void test4() throws InterruptedException {
    ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
    blockingQueue.offer("a");
    blockingQueue.offer("b");
    blockingQueue.offer("c");
    //超时退出
    blockingQueue.offer("d",2, TimeUnit.SECONDS);


    blockingQueue.poll();
    blockingQueue.poll();
    blockingQueue.poll();
    blockingQueue.poll(2, TimeUnit.SECONDS);
}

 

 

 

 

 

 

10.SynchronousQueue 同步队列

 

没有容量

 

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

 

put,take
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");
                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 {
                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();


    }
}

 

11.线程池  池化技术 程序的运行,本质:占用系统的资源!优化资源的使用=>池化技术 线程池,连接池,内存池,对象池...   池化技术:事先准备好一些资源,有人要用就来拿,用完就还回去.   线程池的好处  1.降低资源的消耗  2.提高响应的速度  3.方便管理   线程复用,可以控制最大并发数,管理线程   线程池:三大方法,7大参数,四种拒绝策略    1.三大方法
//单个线程
        Executors.newSingleThreadExecutor();
        //创建一个固定的线程池的大小
//        Executors.newFixedThreadPool(5);
        //可伸缩的线程池(可变)
//        Executors.newCachedThreadPool();

 

public class Demo1 {
    public static void main(String[] args) {
        //单个线程
//        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //创建一个固定的线程池的大小
//        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        //可伸缩的线程池(可变)
        ExecutorService threadPool = Executors.newCachedThreadPool();


        try {
            for (int i = 0; i < 100; i++) {
                //使用线程池之后,使用线程池来创建线程
                threadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"OK");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }


    }
}

 

 2.7大参数

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

 

 3.四种拒绝策略

 

 

corePoolSize: 线程池维护线程的最少数量
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime: 线程池维护线程所允许的空闲时间
unit: 线程池维护线程所允许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列
handler: 线程池对拒绝任务的处理策略

 

1 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。 2  如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。 3  如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。 4  如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。   当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。   当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize时,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略: ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务  ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务   最大线程到底该如何定义 1.CPU 密集型,几核就是几,保持Cpu的效率最高
Runtime.getRuntime().availableProcessors()
2.IO 密集型 程序 15个大型任务 io十分占用资源     12.函数型接口   1.Function 函数型接口,只有一个方法的接口 参数类型T,返回类型R
public interface Function<T, R> {


    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);


Function<String, String> function = (str)->{
    return str;
};

System.out.println(function.apply("ad"));

 

 2.Predicate 断定型接口,有一个输入参数,返回值只能是->布尔值

public interface Predicate<T> {


    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

public static void main(String[] args) {
//        Predicate<String> predicate = new Predicate<String>() {
//            @Override
//            public boolean test(String str) {
//                return str.isEmpty();
//            }
//        };
        Predicate<String> predicate = String::isEmpty;


        System.out.println(predicate.test(""));
    }

 

 3.Consumer 消费性接口 有参数,没有返回值

public interface Consumer<T> {


    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

public static void main(String[] args) {
//        Consumer<String> consumer = new Consumer<String>() {
//            @Override
//            public void accept(String str) {
//                System.out.println(str);
//            }
//        };


//        Consumer<String> consumer = (str)->{
//            System.out.println(str);
//        };
        Consumer<String> consumer = System.out::println;


        consumer.accept("oi");
    }

 

4.供给型接口 没有参数,只有返回值  
public interface Supplier<T> {


    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();

 

 

13.流式计算
/**
* 只能用一行代码实现
* 五个用户
* 1.ID 必须是偶数
* 2.年龄必须大于23
* 3.用户名转为大写
* 4.用户名字符倒序
* 5.只输出一个用户
*/
public class Test {
    public static void main(String[] args) {
        User user1 = new User(1,"a",21);
        User user2 = new User(2,"b",22);
        User user3 = new User(3,"c",23);
        User user4 = new User(4,"d",24);
        User user5 = new User(6,"e",25);


        List<User> list = Arrays.asList(user1,user2,user3,user4,user5);
        list.stream()
                .filter(u->{return u.getId()%2==0;})
                .filter(u->{return u.getAge()>23;})
                .map(u->{return u.getName().toUpperCase();})
                .sorted((uu1,uu2)->{return uu2.compareTo(uu1);})
                .limit(1)
                .forEach(System.out::println);


    }
}
    14.ForkJoin 什么是ForkJoin? ForkJoin在JDK1.7,并发执行任务!提高效率,大数据量 大数据:Map Reduce(把大人物拆分为小任务)   ForkJoin的工作特点:工作窃取 A和B同时执行5个任务,当A执行完5个任务后,会把B没完成的任务偷过来执行,提高效率.

 

 

public class ForkJoin extends RecursiveTask<Long> {

    private Long start;
    private Long end;
    private Long temp = 10000L;
    public ForkJoin(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{
            long middle = (start+end)/2;
            ForkJoin task1 = new ForkJoin(start, middle);
            task1.fork();   //拆分任务,把任务压如线程队列
            ForkJoin task2 = new ForkJoin(middle, end);
            task2.fork();   //拆分任务,把任务压如线程队列

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

    }

}

 

 

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        test1();    //669
        test2();    //10952
        test3();    //503
    }

    //普通方式
    public static void test1(){

        long sum = 0L;
        long start = System.currentTimeMillis();

        for (long i = 1L; i <=10_0000_0000 ; i++) {
            sum += i;
        }

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

    //使用ForkJoin
    public static void test2() throws ExecutionException, InterruptedException {


//        long sum = 0L;
        long start = System.currentTimeMillis();

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoin task = new ForkJoin(0L, 10_0000_0000L);
//        forkJoinPool.execute(task);//执行任务,没有返回值
        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();

        long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);

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

}

 

 

 

15.异步回调

 

 

 

没有返回值的回调
/**
* 异步执行:CompletableFuture
* 成功回调
* 失败回调
*/
public class Demo1 {
    public static void main(String[] args) throws Exception {
        //发起一个请求

        //没有返回值的supplyAsync异步回调
        CompletableFuture<Void> runAsync = CompletableFuture.runAsync(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (Exception e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName()+"runAsync=>Void");
        });

        System.out.println("111");

        runAsync.get(); //获取阻塞执行结果
    }
}

 

有返回值的回调

CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(()->{
    System.out.println(Thread.currentThread().getName()+"supplyAsync=>Integer");
    int i=10/0;
    return 1024;
});


System.out.println(completableFuture.whenComplete((t,u)->{
    System.out.println("t->"+t);    //正常的返回结果
    System.out.println("u->"+u);    //错误信息
}).exceptionally((e)->{
    System.out.println(e.getMessage());
    return 100; //可以获取到错误的返回结果
}));

 

16.JMM 请你谈谈你对Volatile的理解 Volatile是java虚拟机提供轻量级的同步机制   1.保证可见性 2.不保证原子性 3.禁止指令重排   什么是JMM? java内存模型,不存在的东西,概念,约定. 关于JMM的同步约定: 1.线程在解锁前,必须把共享变量立刻刷会主存 2.线程加锁前,必须读取主存中的最新值到工作内存中 3.加锁和解锁是同一把锁   线程  工作内存 主内存   8个操作(4组)

 

 

 

 

 

https://www.cnblogs.com/lewis0077/p/5143268.html

https://www.jianshu.com/p/8a58d8335270

 

17.Volatile

 

private static int num = 0;

public static void main(String[] args) throws InterruptedException {
    new Thread(()->{    //线程1 对主内存的变化不知道
        while (num == 0){

        }
    }).start();

    TimeUnit.SECONDS.sleep(1);

    num = 1;
    System.out.println(num);

}

 

输出num为1,但是线程1没有停止,因为线程1不知道主内存的变化     保证可见性
private volatile static int num = 0;


public static void main(String[] args) throws InterruptedException {
    new Thread(()->{    //线程1 对主内存的变化不知道
        while (num == 0){


        }
    }).start();


    TimeUnit.SECONDS.sleep(1);


    num = 1;
    System.out.println(num);


}
加上volatile,使得内存可见,保证可见性 不保证原子性    
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 = 1; j <= 1000; j++) {
                add();
            }
        }).start();
    }

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

    System.out.println(Thread.currentThread().getName()+"main "+num);
}
结果小于2万     使用原子类底层(AtomicXXX)AtomicInteger
//    private volatile static int num = 0;
    private volatile static AtomicInteger num = new AtomicInteger();


    public static void add(){
//        num++;  //不是一个原子操作
        num.getAndIncrement();  //+1方法
    }


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


        }


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


        System.out.println(Thread.currentThread().getName()+"main "+num);


    }

 

 

类的底层和操作系统相关,在内存中修改值!Unsafe类是一个很特殊的类   指令重排 什么是指令重排:你写的程序,计算器并不是按照你写的那样去执行的. 源代码->编译器优化的重排->指令并行也可能会重排->内存系统也会重排->执行   volatile可以避免指令重排 1.保证特定的操作和执行顺序 2.可以保证某些变量的内存可见性(利用这些特性volatile实现了可见性)  

 

 

 

 

 

 

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

 

 

 

 

 

18.玩转单例模式
//饿汉式单例
public class Hungry {


    //可能会浪费空间
    private byte[] data1 = new byte[1024*1024];
    private byte[] data2 = new byte[1024*1024];
    private byte[] data3 = new byte[1024*1024];
    private byte[] data4 = new byte[1024*1024];


    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()+"OK");
    }
//    private static LazyMan lazyMan;
    private volatile static LazyMan lazyMan;


//    public static LazyMan getInstance(){
//        if(lazyMan==null){
//            lazyMan = new LazyMan();
//        }
//        return lazyMan;
//    }


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


        }
        return lazyMan;
    }


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

 

//静态内部类
public class Holder {
    private Holder(){


    }


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


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


}

单例模式 | 菜鸟教程 (runoob.com)

 

 

 

19. 深入理解CAS

 

什么是CAS?

 

 

public class CASDemo {
    // CAS
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(2021);
        //public final boolean compareAndSet(int expect, int update) {
        //如果期望的值达到了,就更新,否则不更新
        atomicInteger.compareAndSet(2021,2022);
        System.out.println(atomicInteger);
        System.out.println(atomicInteger.get());
    }
}

 

 

 

 

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

 

 

 

缺点:

 

1.循环会耗时

 

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

 

3.ABA问题

 

 

 

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

 

 

 

 

// CAS
public static void main(String[] args) {
    AtomicInteger atomicInteger = new AtomicInteger(2021);
    //public final boolean compareAndSet(int expect, int update) {
    //如果期望的值达到了,就更新,否则不更新.CAS是cpu的并发源语

    //捣乱的线程
    System.out.println(atomicInteger.compareAndSet(2021, 2022));
    System.out.println(atomicInteger.get());
    System.out.println(atomicInteger.compareAndSet(2022, 2021));
    System.out.println(atomicInteger.get());

    //期望的线程
    System.out.println(atomicInteger.compareAndSet(2021, 111));
    System.out.println(atomicInteger.get());

}

 

20.原子引用 带版本号的原子操作   解决ABA问题,引入原子引用(乐观锁)  

 

 

// CAS
    public static void main(String[] args) {
//        AtomicInteger atomicInteger = new AtomicInteger(2021);
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(10, 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(10, 20, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
            System.out.println("A2:"+atomicStampedReference.getStamp());


            System.out.println(atomicStampedReference.compareAndSet(20, 10, 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(10, 66, stamp, stamp + 1));


            System.out.println("B2:"+atomicStampedReference.getStamp());


        },"B"
        ).start();

 

 

21.各种锁的理解   1.公平锁,非公平锁 公平锁:非常公平,不能够插队,必须先来后到. 非公平锁:非常不公平,可以插队   默认是非公平锁(源码)
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

new ReentrantLock(true)

 

2.可重入锁 可重入锁(递归锁) 拿到外面的锁,就相当于拿到了里面的锁    1.synchronized版

 

 

public class Demo1 {
    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();//这里也有锁
    }


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

 

固定结果

Asms
Acall
Bsms
Bcall


 2.lock版:lock锁必须配对

public class Demo2 {
    public static void main(String[] args) {
        Phone1 phone = new Phone1();
        new Thread(()->{
            phone.sms();
        },"A").start();


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


    }
}


class Phone1{
    private Lock lock = new ReentrantLock();


    public synchronized void sms(){
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "sms");
            call();//这里也有锁
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }


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




    }
}

 

3.自旋锁Spinlock => Unsafe

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

 

// 自旋锁
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()+"=>>myUnLock");
        atomicReference.compareAndSet(thread,null);
    }
}

class TestSpinLock{
    public static void main(String[] args) {
        SpinlockDemo lock = new SpinlockDemo();

        new Thread(()->{
            try {
                lock.myLock();
                TimeUnit.SECONDS.sleep(6);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.myUnlock();
            }
        },"A").start();

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

        new Thread(()->{
            try {
                lock.myLock();
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.myUnlock();
            }
        },"B").start();

    }
}

输出结果:只有A解锁,B才有资格解锁
A=>>myLock
B=>>my

 

4.死锁

 

1.jps -l          定位进程号

 

 

 

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

 



这篇关于并发编程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程