JUC常用知识小结

本文是参照b站狂神说java总结的一些知识点

JUC

synchronize八锁问题

  1. synchronize 的六种锁的情况 详情请看代码里面的注释

第一种

package lock;

import java.util.concurrent.TimeUnit;

/**
 * synchronize 锁住两个方法
 * 结果先发短信,在打电话
 * 原因: 因为synchronize 在方法上,锁住的是对象,你new了几个对象就几个锁
 * 那么也就是说现在只有一个锁,而且程序顺序执行,发短信的线程先拿到锁,因为此时不是异常,
 * synchronize只有在异常的时候才会释放锁,所以不会释放锁,执行完之后第二个在执行
 */
public class test1 {
    public static void main(String[] args) throws InterruptedException {
        Phone p = new Phone();
        new Thread(() -> {
            p.send();
        }, "A").start();

        TimeUnit.SECONDS.sleep(1);

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

class Phone {
    public synchronized void send() {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("发短信");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

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

第二种

package lock;

import java.util.concurrent.TimeUnit;

/**
 * 还是synchronizie 锁住了两个方法,但是new了两个对象,所以说就有两个锁,那么就会自己管自己的
 * 结果是  打电话,发短信
 */
public class test2 {
    public static void main(String[] args) throws InterruptedException {
        Phone1 p = new Phone1();
        Phone1 p2 = new Phone1();
        new Thread(() -> {
            p.send();
        }, "A").start();

        TimeUnit.SECONDS.sleep(1);

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

class Phone1 {
    public synchronized void send() {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("发短信");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

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

第三种

package lock;

import java.util.concurrent.TimeUnit;

/**
 * 一个被synchronize锁住,一个没锁,所以就是正常执行
 * 打电话,发短信
 */
public class test3 {
    public static void main(String[] args) throws InterruptedException {
        Phone3 p = new Phone3();
        new Thread(() -> {
            p.send();
        }, "A").start();

        TimeUnit.SECONDS.sleep(1);

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

class Phone3 {
    public synchronized void send() {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("发短信");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

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

第四种

package lock;

import java.util.concurrent.TimeUnit;

/**
 * synchronize锁住静态方法
 * 发短信,打电话
 * synchronize锁住静态方法的时候其实是锁住的Class模板,锁住的是这个类,那么此时就只有一个锁
 */
public class test4 {
    public static void main(String[] args) throws InterruptedException {
        Phone4 p = new Phone4();
        new Thread(() -> {
            p.send();
        }, "A").start();

        TimeUnit.SECONDS.sleep(1);

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

class Phone4 {
    public static synchronized void send() {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("发短信");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

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

第五种

package lock;

import java.util.concurrent.TimeUnit;

/**
 * synchronize锁住的还是static 但是现在new了两个对象,但是只有一个Class模板。所以说仍然只有一把锁
 * 发短信,打电话
 */
public class test5{
    public static void main(String[] args) throws InterruptedException {
        Phone5 p = new Phone5();
        Phone5 p2 = new Phone5();
        new Thread(() -> {
            p.send();
        }, "A").start();

        TimeUnit.SECONDS.sleep(1);

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

class Phone5 {
    public static synchronized void send() {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("发短信");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

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



第六种

package lock;

import java.util.concurrent.TimeUnit;

/**
 * 一个锁住了static方法,一个锁住了普通方法, 所以说一个锁在了Class  一个锁住了对象,有两把锁
 * 打电话,发短信
 */
public class test6{
    public static void main(String[] args) throws InterruptedException {
        Phone6 p = new Phone6();
        new Thread(() -> {
            p.send();
        }, "A").start();

        TimeUnit.SECONDS.sleep(1);

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

class Phone6 {
    public static synchronized void send() {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("发短信");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

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




ArrayList

线程不安全,在添加的时候

package JUCMY;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.Vector;

public class Arrayscon {
    public static void main(String[] args) {
        List<String> a = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                a.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(a);
            },String.valueOf(i)).start();
            new Thread(() -> {
                a.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(a);
            },String.valueOf(i)).start();
        }
    }
}

这个时候可以用Vector 来解决

Vector是在jdk1.0就提出了,用synchronized 解决的

解决方案

  • 用vertor

  • 用Colloctions.synchronizedList 底层也是synchronize 类似于 CopyOnWriteArrayList

  • 用JUC下的CopyOnWriteArrayList()

    •     final transient Object lock = new Object();
      
          /** The array, accessed only via getArray/setArray. */
          private transient volatile Object[] array;
      
    • 写入时复制,在写入的时候复制一份,避免造成数据覆盖,多线程操作 jdk 12,之前的可能是有一个lock锁,不是synchronize

        public boolean add(E e) {
            synchronized (lock) {
                Object[] es = getArray();
                int len = es.length;
                es = Arrays.copyOf(es, len + 1);
                es[len] = e;
                setArray(es);
                return true;
            }
        }
    

Set

底层其实就是个hashmap用到了 map的key

hashmap

面试

读写锁(共享锁)

ReadWriteLock 是一种锁 粒度更高 相比于普通的锁 ReentrantLock

package JUCMY;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

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

        for(int i=1;i<=5;i++){
            int finalI = i;
            new Thread(()->{
                myLock.put(finalI +"",finalI+"");
            },String.valueOf(i)).start();
        }
        for(int i=1;i<=5;i++){
            int finalI = i;
            new Thread(()->{
                myLock.get(finalI +"");
            },String.valueOf(i)).start();
        }
    }
}
class MyLock{
    private Map<String,String> m = new HashMap<>();

    public void put(String a,String b){
        System.out.println(Thread.currentThread().getName()+"写入");
        m.put(a,b);
        System.out.println(Thread.currentThread().getName()+"写入OK");
    }
    public void get(String key){
        System.out.println(Thread.currentThread().getName()+"读入");
        m.get(key);
        System.out.println(Thread.currentThread().getName()+"读入OK");

    }
}
class MyLock1{
    private Map<String,String> m = new HashMap<>();
    private ReadWriteLock r = new ReentrantReadWriteLock();
    public void put(String a,String b){
        r.writeLock().lock();
        System.out.println(Thread.currentThread().getName()+"写入");
        m.put(a,b);
        System.out.println(Thread.currentThread().getName()+"写入OK");
        r.writeLock().unlock();
    }
    public void get(String key){
        System.out.println(Thread.currentThread().getName()+"读入");
        m.get(key);
        System.out.println(Thread.currentThread().getName()+"读入OK");

    }
}

阻塞队列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F3HyLJCy-1586745559949)(C:\Users\你大爷\AppData\Roaming\Typora\typora-user-images\image-20200408152448236.png)]

四组API

方式 抛出异常 有返回值不抛出异常 阻塞等待 超时等待
取出 add offer put offer
添加 remove poll take poll
查看队首 element peek
package JUCMY;

import java.net.PortUnreachableException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;

public class Bq {
    public static void main(String[] args) throws InterruptedException {
        test4();
    }

    /**
     * 抛出异常
     */
    public  static void  test1(){
        ArrayBlockingQueue<Object> objects = new ArrayBlockingQueue<>(3);
        System.out.println(objects.add(1));
        System.out.println(objects.add(1));
        System.out.println(objects.add(1));
        System.out.println(objects.add(1));
        System.out.println("--------------------");
        System.out.println(objects.remove());
        System.out.println(objects.remove());
        System.out.println(objects.remove());
        System.out.println(objects.remove());
    }

    /**
     * 不抛出异常 会有返回值  根据返回值来判断是不是成功
     */
    public  static void  test2(){
        ArrayBlockingQueue<Object> objects = new ArrayBlockingQueue<>(3);
        System.out.println(objects.offer(1));
        System.out.println(objects.offer(1));
        System.out.println(objects.offer(1));
        System.out.println(objects.offer(1));
        System.out.println("--------------------");
        System.out.println(objects.poll());
        System.out.println(objects.poll());
        System.out.println(objects.poll());
        System.out.println(objects.poll());
    }

    /**
     * 阻塞等待
     *
     */
    public  static void  test3() throws InterruptedException {
        ArrayBlockingQueue<Object> objects = new ArrayBlockingQueue<>(3);
        objects.put(1);
        objects.put(1);
        objects.put(1);

        System.out.println(objects.take());
        System.out.println(objects.take());
        System.out.println(objects.take());
        System.out.println(objects.take());
    }
    /**
     * 超时等待
     */
    public  static void  test4() throws InterruptedException {
        ArrayBlockingQueue<Object> objects = new ArrayBlockingQueue<>(3);
        objects.offer(1,2, TimeUnit.SECONDS);
        objects.offer(1,2, TimeUnit.SECONDS);
        objects.offer(1,2, TimeUnit.SECONDS);


        objects.poll(2,TimeUnit.SECONDS);
        objects.poll(2,TimeUnit.SECONDS);
        objects.poll(2,TimeUnit.SECONDS);
        objects.poll(2,TimeUnit.SECONDS);

    }
}

同步队列

特点就是 只能放一个 存进去了 就一定要取出来 才能再存第二个(在一个线程中),如果是多个线程会发生错误,并且只要是存进去的一定要取出来

package JUCMY;

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

public class SynQueue {
    public static void main(String[] args) throws InterruptedException {
        SynchronousQueue<Integer> integers = new SynchronousQueue<>();
        new Thread(()-> {
            try {
                System.out.println("放入一个 1");
                integers.put(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        ,"T1").start();
        new Thread(()->{
            try {
                System.out.println("2存");
                integers.put(4);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"T2").start();

        new Thread(()->{
            try {
                System.out.println("取出来");
                integers.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        integers.take();
    }
}

线程池

线程池 : 三大方法 七大参数 四种拒绝策略

线程池就是一个池子,为了提高效率,节省资源,只要我有线程的时候,那么就是从池子里面拿

可以线程复用,便于管理

三大方法

package JUCMY;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class PoolTest {
    public static void main(String[] args) {
//        ExecutorService executorService = Executors.newSingleThreadExecutor();
        ExecutorService executorService = Executors.newFixedThreadPool(5);
//        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executorService.execute(()->{
                System.out.println(Thread.currentThread().getName()+"k");
            });

        }
        //线程池用完了要关闭
        executorService.shutdown();
    }
}

七大参数

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}




public ThreadPoolExecutor(int corePoolSize,  //核心线程数
                              int maximumPoolSize,//最大线程数
                              long keepAliveTime,//存活时间  超时了没人调用就释放  在max + queue都满了的时候
                              TimeUnit unit,//单位
                              BlockingQueue<Runnable> workQueue,//阻塞队列
                              ThreadFactory threadFactory,//线程工厂
                              RejectedExecutionHandler handler)//拒绝策略
{
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

四种

四种拒绝策略

/**
 *  new ThreadPoolExecutor.AbortPolicy()//如果超过了 max + queue  就抛出异常
 *  new ThreadPoolExecutor.CallerRunsPolicy()// 超过max + queue 的哪来的回哪去  一般是main线程
 *  new ThreadPoolExecutor.DiscardPolicy()// 超过max + queue 的满了 丢掉任务  不会抛出异常
 *  new ThreadPoolExecutor.DiscardOldestPolicy()// 超过max + queue 的 尝试和最早的那个竞争,竞争不到还是会丢掉
 */

最大线程到底如何定义 (用来调优的)

  1. CPU密集型: 几何就是几
  2. IO密集型 : 看有多少io的任务 比较耗时 一般设置两倍 >这个数

首先要看你的代码是CPU密集型还是IO密集型

如果是cpu密集型 那么肯定是电脑有几个cpu就开几个线程,但是如果线程数远远大于cpu的核心数量,那么反而会效率下降,因为切换线程也是需要消耗资源的

如果是io密集型,那么 看有多少io的任务 比较耗时 一般设置两倍 >这个数 即可以

新时代程序员必会的四项技术

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

四大函数式接口

函数式接口 : 接受参数,返回类型 : 在接口中只有一个函数

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

有了函数式接口,就可以和lambda表达式一起使用简化代码

package JUCMY;

import java.util.function.Function;

public class FunctionTest {
    public static void main(String[] args) {
        Function<String, String> function = new Function<>() {
            @Override
            public String apply(String s) {
                return s;
            }
        };
        Function a = (str)->{return str;
        };
        System.out.println(a.apply("zxc"));
        System.out.println(function.apply("asd"));
    }
}

断定型接口 : 接受参数,返回boolean类型

package JUCMY.InterfaceTest;

import java.util.function.Predicate;

public class PredictTest {
    public static void main(String[] args) {
        Predicate<String> predicate = new Predicate<>() {

            @Override
            public boolean test(String s) {
                return false;
            }
        };

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

消费型接口 : 只接受参数,无返回值

package JUCMY.InterfaceTest;

import java.util.function.Consumer;

public class ConsummerTest {
    public static void main(String[] args) {
        //匿名内部类
        Consumer<String> consumer = new Consumer<>() {

            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        
    }
}

供给型接口不接受参数,返回类型

package JUCMY.InterfaceTest;

import java.util.function.Supplier;

public class SupplyTest {
    public static void main(String[] args) {
        Supplier<String> supplier = new Supplier<>() {

            @Override
            public String get() {
                return "sad";
            }
        };
        System.out.println(supplier.get());
    }
}

Stream 计算 效率很高

package JUCMY.StreamCopu;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Test {
    public static void main(String[] args) {
        User a = new User(1, "a", 21);
        User b = new User(2, "b", 22);
        User c = new User(3, "c", 23);
        User d = new User(4, "d", 24);
        User e = new User(6, "e", 25);

        List<User> users = Arrays.asList(a, b, c, d, e);
        users.stream()
                .filter(u->{return u.getId()%2==0;})
                .filter(u->{return u.getAge()>23;})
                .map(u->{return u.getName().toUpperCase();})
                .sorted((u1,u2)->{return u2.compareTo(u1);})
                .limit(1)
                .forEach(u->{
            System.out.println(u);
        });

    }
}

ForkJoin

分支计算,分解任务,把一个大任务拆分成多个小任务,然后在进行计算,提高效率

eg : 计算1+…+10亿

三种方法

  • 正常算
  • 使用ForkJoin 因为有中间量 temp 可以调优
  • 使用Stream 并行计算 速度是最快的
package JUCMY.ForkJoinTest;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;

public class ThreeCoders {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
//        test1();//7991
//        test2();//4634
        test3();//308
    }

    public static void test1() {
        long l = System.currentTimeMillis();

        Long sum = 0L;
        for (Long i = 0L; i <= 10_0000_0000; i++) {
            sum += i;
        }
        long l1 = System.currentTimeMillis();
        System.out.println(sum + "--------------- " + (l1 - l));
    }

    public static void test2() throws ExecutionException, InterruptedException {
        long l = System.currentTimeMillis();

        Long sum = 0L;
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTa forkJoinTa = new ForkJoinTa(0L, 10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(forkJoinTa);
        Long aLong = submit.get();
        sum = System.currentTimeMillis();
        long l1 = System.currentTimeMillis();
        System.out.println(sum + "--------------- " + (l1 - l));
    }
    public static void test3() {
        long l = System.currentTimeMillis();

        Long sum = 0L;
        long sum1 = LongStream.range(0L, 10_0000_0000).parallel().reduce(0, Long::sum);
        long l1 = System.currentTimeMillis();
        System.out.println(sum1 + "--------------- " + (l1 - l));
    }
}

ForkJoin使用步骤

  • 继承RecursiveTask
  • 重写compute 方法
  • 类似归并的思想,fork() 拆解任务 join()返回结果
  • new ForkJoinTaskPoll submit 异步提交 execute 是执行
package JUCMY.ForkJoinTest;

import java.util.concurrent.RecursiveTask;

public class ForkJoinTa extends RecursiveTask<Long> {
    private Long start ;
    private Long end;

    private Long temp = 10000L;

    public ForkJoinTa(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;
            ForkJoinTa forkJoinTask = new ForkJoinTa(start, middle);
            ForkJoinTa forkJoinTask1 = new ForkJoinTa(middle + 1, end);
            //拆解任务
            forkJoinTask.fork();
            forkJoinTask1.fork();
            //并行计算
            return forkJoinTask.join() + forkJoinTask1.join();
        }
    }
}

JMM(java内存模型 是不存在的东西,是一个约定)

volatile关键字 是java虚拟机提供的轻量级虚拟机制

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

约定~~~ 注意那个图 先store 在write

关于JMM的一些约定

  • 加锁前必须要从内存中读取变量到工作内存
  • 解锁前必须要把线程内存中的变量写回内存
  • 加锁和解锁的是同一把锁

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i8ko8Dih-1586745559956)(C:\Users\你大爷\AppData\Roaming\Typora\typora-user-images\image-20200411083241583.png)]

这里write 和 load 写反了!!

内存交互操作

内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许例外)

    • 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

package JUCMY;

import java.util.concurrent.TimeUnit;

public class kejianxing {
    private static volatile int num = 0;
    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            Thread.currentThread().getName();
            while (num ==0){

            }
        }).start();

        TimeUnit.SECONDS.sleep(3);
        num = 1;
        System.out.println(num + " --"+Thread.currentThread().getName());
    }
}

验证非原子性和原子类

package JUCMY;

public class notAomic {
    private volatile static int num = 0;
    public static void add(){
        num++;

    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 200; j++) {
                    add();
                }

            }).start();
        }
        //java中默认一直存在的的两个线程是 main 和 gc
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() +" "+ num);
    }
}

可以看到加了volatile 之后还是不行 还是不能保证原子性,因为num++就不是

原子性的,并且你也没有加锁,因为只有在加锁和解锁的时候才会进行和内存进行的交互

解决办法

  • 加锁
  • synchronized
package JUCMY;

public class notAomic {
    private volatile static int num = 0;
    public synchronized static void add(){
        num++;

    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 200; j++) {
                    add();
                }

            }).start();
        }
        //java中默认一直存在的的两个线程是 main 和 gc
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() +" "+ num);
    }
}

  • 使用原子类(就不需要使用锁了)
package JUCMY;

public class notAomic {
    private volatile static int num = 0;
    public synchronized static void add(){
        num++;

    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 200; j++) {
                    add();
                }

            }).start();
        }
        //java中默认一直存在的的两个线程是 main 和 gc
        while (Thread.activeCount() > 2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() +" "+ num);
    }
}

指令重排详解

volatile 可以避免指令重排

内存屏障( 内存屏障(memory barrier)是一个CPU指令 ),CPU指令 作用:

  • 保证特定的操作执行顺序
  • 可以保证某些变量的内存可见性(利用这些特性 volatile实现了禁止指令重排)

原理: 其实就是在使用volatile的前后都使用了内存屏障,保证了操作的执行顺序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Eobu6iLi-1586745559958)(C:\Users\你大爷\AppData\Roaming\Typora\typora-user-images\image-20200412094603987.png)]

单例模式

CAS

在面试笔记中有,这里不再赘述

怎么解决ABA问题

加入版本号 java

package JUCMY;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;

public class CAStest {
    public static void main(String[] args) {
        AtomicStampedReference<Integer> integerAtomicReference = new AtomicStampedReference<>(1,1);

        new Thread(()->{
            int stamp = integerAtomicReference.getStamp();
            System.out.println("a0----"+stamp);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(integerAtomicReference.compareAndSet(1, 2,
                    integerAtomicReference.getStamp(), integerAtomicReference.getStamp() + 1));
            System.out.println("a1---"+integerAtomicReference.getStamp());

            System.out.println(integerAtomicReference.compareAndSet(2, 1, integerAtomicReference.getStamp(),
                    integerAtomicReference.getStamp() + 1));
            System.out.println("a2----"+integerAtomicReference.getStamp());

        },"a").start();

        new Thread(()->{
            int stamp = integerAtomicReference.getStamp();
            System.out.println("b----"+stamp);

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

            System.out.println(integerAtomicReference.compareAndSet(1, 3, stamp,
                    integerAtomicReference.getStamp() + 1));

            System.out.println("b2---"+integerAtomicReference.getStamp());

        },"b").start();

    }
}

java手写自旋锁

package JUCMY.PackLock;

import java.util.concurrent.atomic.AtomicReference;

public class Entity {
    AtomicReference<Thread> a = new AtomicReference<>();
    public void mylock(Thread t){
        System.out.println(t.getName()+"---> dedaosuo");
        while (!a.compareAndSet(null,t)){

        }
       
    }
    public void myunlock(Thread t){


        while (!a.compareAndSet(t,null)){

        }
        System.out.println(t.getName()+"----> shifangsuo");

    }
}

package JUCMY.PackLock;

import java.util.concurrent.TimeUnit;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        Entity entity = new Entity();
        new Thread(()->{
            entity.mylock(Thread.currentThread());
            System.out.println(Thread.currentThread().getName());
            entity.myunlock(Thread.currentThread());
        },"a").start();
        TimeUnit.SECONDS.sleep(2);
        new Thread(()->{
            entity.mylock(Thread.currentThread());
            System.out.println(Thread.currentThread().getName());
            entity.myunlock(Thread.currentThread());
        },"b").start();
    }
}

排查死锁

package JUCMY;

public class TestLockDead {
    public static void main(String[] args) {
        String a= "asd";
        String b =  "dsa";
        new Thread(new lockkk(a,b)).start();
        new Thread(new lockkk(b,a)).start();
    }
}
class lockkk implements Runnable{

    private String s1 ;
    private String s2 ;

    public lockkk(String s1, String s2) {
        this.s1 = s1;
        this.s2 = s2;
    }

    @Override
    public void run() {
        synchronized (s1){
            System.out.println(Thread.currentThread().getName() + "huode "+s1 + "wan "+s2);
            synchronized (s2){

            }

        }
    }
}

可以使用 jps -l 来查看进程 找到自己的进程号

在使用jstack + 进程号 来排查死锁

原创文章 57 获赞 9 访问量 5937

猜你喜欢

转载自blog.csdn.net/Cscprx/article/details/105483373
今日推荐