JUC(自用)【未完成】_如何下载未编译juc-程序员宅基地

技术标签: JAVA  java  多线程  并发编程  

JUC

java.util.concurrent

目录

多线程

多线程

线程和进程

  • Java默认两个线程
    • main
    • GC

Java本身不可以开启线程

并发和并行

  • 并发
    • 单核CPU,模拟出多条线程,快速交替
  • 并行
    • 多核CPU,多条线程同时进行

并发编程的原因:充分利用CPU资源

获取当前核数

Runtime.getRuntime().availableProcessors();

线程的状态

6个

  • NEW:新生
  • RUNNABLE:运行
  • BLOCKED:阻塞
  • WAITING:等待
  • TIMED_WATITING:超时等待
  • TERMINATED:终止

wait/sleep

wait sleep
属于Object 属于Thread
会释放锁 不释放锁
在同步代码块中 在任意地方

java.util.concurrent.locks

LOCK & synchronized

lock和synchronized的区别

synchronized lock
内置关键字 Java类
无法判断锁 可判断锁
自动释放锁 手动释放锁
可重入锁,非公平 可重入锁,公平/非公平
适合少量代码 适合大量代码
  • lock java.util.concurrent.locks

    • ReentrantLock:可重入锁
    • ReentrantReadWriteLock.ReadLock:读锁
    • ReentrantReadWriteLock.WriteLock:写锁
  • lock三部曲

    1. new ReentrantLock();
    2. lock.lock()
    3. finally=>lock.unlock()

Condition & 生产者/消费者

synchroniazed Lock
wait Condition.await
notify Condition.signal
  • synchronized版

    if可能产生虚假唤醒情况,判断等待应写在while循环中
    if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait

    class Data{
          
      private int number = 0;
    
      public  synchronized void increment(){
          
        while(number!=0){
          //判断
          //等待
          this.wait();
        }
    
        //业务
        number++;
    
        //通知其他线程
        this.notifyAll();
      }
    }
    

-JUC版

class Data{
    
  private int number = 0;
  private Lock lock = new ReentrantLock();
  private Condition condition=lock.newCondition();

  public void increment(){
    
    lock.lock();
    try{
    
      while(number!=0){
    //判断
        //等待
        condition.await();
      }

      //业务
      number++;

      //通知其他线程
      condition.signalAll();
      //conditionX.signal==>唤醒指定的监视器

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

  }
}

ReadWriteLock & 缓存

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

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

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

  class MyCacheLock {
    
      private volatile Map<String, Object> map = new HashMap<>();

      private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

      public void put(String key, Object value) {
    
          readWriteLock.writeLock().lock();
          try {
    
              System.out.println(Thread.currentThread().getName() + "put" + key);
              map.put(key, value);
              System.out.println(Thread.currentThread().getName() + "put done");
          } catch (Exception e) {
    
              e.printStackTrace();
          } finally {
    
              readWriteLock.writeLock().unlock();
          }
      }

      public void get(String key) {
    
          readWriteLock.readLock().lock();
          try {
    
              System.out.println(Thread.currentThread().getName() + "get" + key);
              Object o = map.get(key);
              System.out.println(Thread.currentThread().getName() + "get done");
          } catch (Exception e) {
    
              e.printStackTrace();
          } finally {
    
              readWriteLock.readLock().unlock();
          }
      }

  }

  class MyCache {
    
      private volatile Map<String, Object> map = new HashMap<>();

      public void put(String key, Object value) {
    
          System.out.println(Thread.currentThread().getName() + "put" + key);
          map.put(key, value);
          System.out.println(Thread.currentThread().getName() + "put done");
      }

      public void get(String key) {
    
          System.out.println(Thread.currentThread().getName() + "get" + key);
          Object o = map.get(key);
          System.out.println(Thread.currentThread().getName() + "get done");
      }

  }

八锁现象-synchronized锁的对象

  • 锁的对象: synchronized的调用者

八锁现象

集合类不安全

ArrayList不安全

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

java.util.ConcurrentModificationException 并发修改异常

  • 解决方案:

    1. Vector默认是安全的 List<String> list = new Vector<>();

    2. 用工具类Collections使Arraylist安全 List<String> list = Collections.synchronizedList(new ArrayList<>());

    3. JUC List<String> list = new CopyOnWriteArrayList<>();

    CopyOnWrite -> COW写入时复制

  • CopyOnWriteList对比Vector -> synchronized效率低

    • Vector

        public synchronized boolean add(E e) {
              
            modCount++;
            ensureCapacityHelper(elementCount + 1);
            elementData[elementCount++] = e;
            return true;
        }
      
    • CopyOnWriteList

        public boolean add(E e) {
              
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
              
                Object[] elements = getArray();
                int len = elements.length;
                Object[] newElements = Arrays.copyOf(elements, len + 1);
                newElements[len] = e;
                setArray(newElements);
                return true;
            } finally {
              
                lock.unlock();
            }
        }
      

HashMap不安全

public class HasMapTest {
    
    public static void main(String[] args) {
    
        Map<String, String> map = new HashMap<>();
        for (int i = 1; i < 10; i++) {
    
            new Thread(() -> {
    
                map.put(Thread.currentThread().getName(),UUID.randomUUID().toString());
                System.out.println(map);
            }, String.valueOf(i)).start();
        }
    }
}
  • 解决方法
    1. Collections Map<String, String> map = Collections.synchronizedMap(new HashMap<>());

    2. JUC Map<String, String> map = new ConcurrentHashMap<>();

Callable

Runable Callable
无返回值 返回值
无异常抛出 抛出异常
run() call()

new Thread()中只接收Runnable,FutrueTask是Runable的子类,用于适配Callable

  • 传统Runnable

      public class RunnableTest {
          
          public static void main(String[] args) {
          
              new Thread(new MyThreadRun(), "Runnable").start();
          }
      }
    
      class MyThreadRun implements Runnable {
          
          @Override
          public void run() {
          
              System.out.println("run()");
          }
      }
    
  • Callable

      public class CallableTest {
          
          public static void main(String[] args) throws ExecutionException, InterruptedException {
          
    
              MyThreadCall callableThread = new MyThreadCall();
              FutureTask futureTask = new FutureTask(callableThread);
    
              new Thread(futureTask, "Callable").start();
    
              System.out.println((String) futureTask.get());
          }
    
      }
    
      class MyThreadCall implements Callable<String> {
          
          @Override
          public String call() throws Exception {
          
              System.out.println("call()");
              return "call done";
          }
      }
    
    1. get()会产生阻塞 -> 1. 需要等待 2. 使用异步处理
    2. 结果会被缓存,提高效率->两个线程输出一个结果

常用的辅助类

CountDownLatch

public class CountDownLatchDemo {
    
    public static void main(String[] args) throws InterruptedException {
    
        CountDownLatch countDownLatch = new CountDownLatch(6);

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

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

        System.out.println("done");

    }
}

CyclicBarrier

  public class CyclicBarrierDemo {
    
      public static void main(String[] args) {
    
          CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
    
              System.out.println("done");
          });

          for (int i = 1; i <= 7; i++) {
    
              int temp = i;
              new Thread(()->{
    
                  System.out.println(Thread.currentThread().getName());
                  try {
    
                      cyclicBarrier.await();
                  } catch (InterruptedException e) {
    
                      e.printStackTrace();
                  } catch (BrokenBarrierException e) {
    
                      e.printStackTrace();
                  }
              }).start();

          }
      }
  }

Semaphore

信号量

  public class SemaphoreDemo {
    
      public static void main(String[] args) {
    
          Semaphore semaphore = new Semaphore(6);
          for (int i = 1; i <= 12; i++) {
    
              new Thread(() -> {
    
                  try {
    
                      semaphore.acquire();
                      System.out.println(Thread.currentThread().getName() + "run");
                      TimeUnit.SECONDS.sleep(2);
                      System.out.println(Thread.currentThread().getName() + "done");
                  } catch (InterruptedException e) {
    
                      e.printStackTrace();
                  } finally {
    
                      semaphore.release();
                  }
              }, String.valueOf(i)).start();
          }
      }
  }
  1. 共享资源互斥时
  2. 并发限流,控制最大的线程数 -> 安全,高可用

BlockingQueue 阻塞队列

线程池,多线程使用
BlockingDueue双端队列

方式 抛异常 有返回值,不抛异常 阻塞等待 超时等待
添加 add() offer() put() offer(,)
移除 remove() poll() take() poll(,)
判断队列首 element() peek() - -

在队列元素为空的情况下,element() 方法会抛出NoSuchElementException异常,peek() 方法只会返回 null

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

        System.out.println(blockingQueue.add("A"));
        System.out.println(blockingQueue.add("B"));
        System.out.println(blockingQueue.add("C"));
        /// java.lang.IllegalStateException: Queue full
        //System.out.println(blockingQueue.add("D"));

        System.out.println("---");

        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        System.out.println(blockingQueue.remove());
        /// java.util.NoSuchElementException
        //System.out.println(blockingQueue.remove());
    }
    public static void test2() {
    
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.offer("A"));
        System.out.println(blockingQueue.offer("B"));
        System.out.println(blockingQueue.offer("C"));
        //队列满则返回false
        System.out.println(blockingQueue.offer("D"));

        System.out.println("---");

        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        //无对象则返回null
        System.out.println(blockingQueue.poll());

    }
    public static void test3() throws InterruptedException {
    
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        blockingQueue.put("A");
        blockingQueue.put("B");
        blockingQueue.put("C");
        ///队列满则阻塞等待
        //blockingQueue.put("D");

        System.out.println("---");

        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        ///队列空则阻塞等待
        //System.out.println(blockingQueue.take());
    }
    public static void test4() throws InterruptedException {
    
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        System.out.println(blockingQueue.offer("A"));
        System.out.println(blockingQueue.offer("B"));
        System.out.println(blockingQueue.offer("C"));
        //队列满则阻塞等待2s
        System.out.println(blockingQueue.offer("D", 2, TimeUnit.SECONDS));

        System.out.println("---");

        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        System.out.println(blockingQueue.poll());
        //队列空则阻塞等待2s
        System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
    }
    public static void test5() {
    
        ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);

        //为空则返回null
        System.out.println(blockingQueue.peek());

        //java.util.NoSuchElementException
        System.out.println(blockingQueue.element());
    }

SychronousQueue

同步队列
每次put之后必须先take,才可继续put

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

        new Thread(() -> {
    
            try {
    
                System.out.println(Thread.currentThread().getName() + "put 1");
                synchronousQueue.put("1");
                System.out.println(Thread.currentThread().getName() + "put 2");
                synchronousQueue.put("2");
                System.out.println(Thread.currentThread().getName() + "put 3");
                synchronousQueue.put("3");
            } catch (InterruptedException e) {
    
                e.printStackTrace();
            } finally {
    
                System.out.println("T1 over");
            }

        }, "T1").start();

        new Thread(() -> {
    
            try {
    
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "take " + synchronousQueue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "take " + synchronousQueue.take());
                TimeUnit.SECONDS.sleep(2);
                System.out.println(Thread.currentThread().getName() + "take " + synchronousQueue.take());
            } catch (InterruptedException e) {
    
                e.printStackTrace();
            } finally {
    
                System.out.println("T2 over");
            }

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

线程池

  • 3大方法
    • Executors.newSingleThreadExecutor();
    • Executors.newFixedThreadPool(5);
    • Executors.newCachedThreadPool();
  • 7大参数
    • int corePoolSize 核心线程池大小
    • int maximumPoolSize 最大核心线程池大小
    • long keepAliveTime 等待超时时间
    • TimeUnit unit 超时单位
    • BlockingQueue<Runnable> workQueue 阻塞队列
    • ThreadFactory threadFactory 线程工厂
    • RejectedExecutionHandler handler 拒绝策略
  • 4种拒绝策略
    • AbortPolicy() :默认,不处理新的任务且抛出异常
    • CallerRunsPolicy() :原路返回,返回给请求的线程处理
    • DiscardPolicy() :队列满了后丢弃任务,不抛异常
    • DiscardOldestPolicy() :队列满了后,丢弃最老的任务,不抛异常
  • 最大线程池大小
    • CPU密集型 -> CPU核数 Runtime.getRuntime().availableProcessors();
    • IO密集型 -> 判断十分耗IO的线程数
public class PoolDemo1 {
    
    public static void main(String[] args) {
    
        ///单线程,最多1个线程
        //ExecutorService pool = Executors.newSingleThreadExecutor();

        ///固定线程,最多n个线程
        //ExecutorService pool = Executors.newFixedThreadPool(5);

        ///可变线程,任意个线程
        //ExecutorService pool = Executors.newCachedThreadPool();

        //本质都是调用ThreadPoolExecutor
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()
        );
        /**
         * 1. 核心线程池大小    -> 默认为0
         * 2. 最大核心线程池大小 -> 默认为Integer.MAX_VALUE => 会造成OOM
         * 3. 等待超时时间
         * 4. 超时单位
         * 5. 阻塞队列
         * 6. 线程工厂(一般不动)
         * 7. 拒绝策略
         *      AbortPolicy() :默认,不处理新的任务且抛出异常
         *      CallerRunsPolicy() :原路返回,返回给请求的线程处理
         *      DiscardPolicy() :队列满了后丢弃任务,不抛异常
         *      DiscardOldestPolicy() :队列满了后,丢弃最老的任务,不抛异常
         */

        try {
    
            for (int i = 0; i < 20; i++) {
    
                pool.execute(() -> {
    
                    System.out.println(Thread.currentThread().getName()+"done");
                });
            }
        } catch (Exception e) {
    
            e.printStackTrace();
        } finally {
    
            pool.shutdown();
        }
    }
}

四大函数接口

  • Function 函数型接口

        public class FunctionTest1 {
          
            public static void main(String[] args) {
          
        //        Function<String, String> function = new Function<String, String>() {
          
        //            @Override
        //            public String apply(String s) {
          
        //                return s;
        //            }
        //        };
    
                Function<String, String> function = (str) -> {
          
                    return str;
                };
    
                System.out.println(function.apply("hahaha"));
            }
        }
    
    
  • Predicate 断定型接口

        public class PredicateTest {
          
            public static void main(String[] args) {
          
        //        Predicate<String> predicate = new Predicate<String>() {
          
        //            @Override
        //            public boolean test(String s) {
          
        //                return s.isEmpty();
        //            }
        //        };
    
                Predicate<String> predicate = (s) -> {
          
                    return s.isEmpty();
                };
    
                System.out.println(predicate.test("hahaha"));
            }
        }
    
    
  • Consumer 消费型接口

        public class ConsumerTest {
          
            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 = (s) -> {
          
                    System.out.println(s);
                };
    
                consumer.accept("hahaha");
    
            }
        }
    
  • Supplier 供给型接口

        public class SupplierTest {
          
            public static void main(String[] args) {
          
        //        Supplier<String> supplier = new Supplier<String>() {
          
        //            @Override
        //            public String get() {
          
        //                return "hahaha";
        //            }
        //        };
                Supplier<String> supplier = () -> {
          
                    return "hahaha";
                };
    
                System.out.println(supplier.get());
            }
        }
    

Stream流式计算

  • filter 过滤器

    Predicate断定接口

  • map 映射

    Function函数型接口

  • sorted 排序

    Comparator -> Function函数型接口

  • limit 分页

    Long

  • forEach 遍历

    Consumer消费型接口

    public class StreamTest {
    
        public static void main(String[] args) {
    
            User u1 = new User(1, "a", 20);
            User u2 = new User(2, "b", 21);
            User u3 = new User(3, "c", 22);
            User u4 = new User(4, "d", 23);
            User u5 = new User(5, "e", 24);
            User u6 = new User(6, "f", 25);

            List<User> users = Arrays.asList(u1, u2, u3, u4, u5, u6);

            /**
            * 1. ID为偶数
            * 2. 年龄大于20
            * 3. 用户名转大写
            * 4. 倒序
            * 5. 只输出2个
            */
            users.stream()
                    .filter((u) -> {
    
                        return u.getId() % 2 == 0;
                    })
                    .filter((u) -> {
    
                        return u.getAge() > 20;
                    })
                    .map((u) -> {
    
                        return u.getName().toUpperCase();
                    })
                    .sorted((user1, user2) -> {
    
                        return user2.compareTo(user1);
                    })
                    .limit(2)
                    .forEach(System.out::println);

            /**
            * filter 过滤器,Predicate断定接口
            * map 映射,Function函数型接口
            * sorted 排序,Comparator -> Function函数型接口
            * limit 分页,Long
            * forEach 遍历,Consumer消费型接口
            */
        }
    }

ForkJoin

并行执行任务
特点: 任务窃取

以双端队列作线程,在任务完成后会从其他队列的尾端抢夺其他的线程的任务

  1. forkjoinPool
  2. 计算任务 forkjoinPool.execute(ForkJoinTask take)
  3. 计算类要继承ForkJoinTask
  • ForkJoinTask
    • CountedCompleter
    • RecursiveAction
    • RecursiveTask

public class ForkJoinTest {
    
    public static void main(String[] args) {
    
        test1();

        System.out.println("=====");

        try {
    
            test2();
        } catch (ExecutionException | InterruptedException e) {
    
            e.printStackTrace();
        }

        System.out.println("=====");

        test3();
        /*
        sum=500000000500000000,用时:330
                =====
        sum=500000000500000000,用时:294
                =====
        sum=500000000500000000,用时:219
        */

    }

    //普通方法
    public static void test1() {
    
        long sum = 0L;
        long start = System.currentTimeMillis();

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

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

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

        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinTask<Long> forkJoinDemo = new ForkJoinDemo(1L, 10_0000_0000L);
        ForkJoinTask<Long> submit = forkJoinPool.submit(forkJoinDemo);

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

class ForkJoinDemo extends RecursiveTask<Long> {
    
    private final long start;
    private final long end;

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

    @Override
    protected Long compute() {
    

        long temp = 10000L;
        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 task1 = new ForkJoinDemo(start, middle);
            ForkJoinDemo task2 = new ForkJoinDemo(middle + 1, end);

            task1.fork();
            task2.fork();

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

Future

  • Interface Future<V>
    • CompletableFuture<T>

public class FutureTest {
    

    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
        //无返回值
        CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
    
            try {
    
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
    
                e.printStackTrace();
            }

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

        });

        System.out.println("-----");

        completableFuture.get();

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

        System.out.println(completableFuture1.whenComplete((t, u) -> {
    
            System.out.println("t=" + t);//正常结果
            System.out.println("u=" + u);//错误信息
        }).exceptionally((e) -> {
    
            System.out.println(e.getMessage());
            return -1;//错误时的结果
        }).get());

    }
}

JMM

java内存模型(概念,模型)

  • JMM特征

    • 原子性
    • 可见性
    • 有序性
  • JMM操作

    • lock (锁定):作用于主内存的变量,把一个变量标识为线程独占状态

    • unlock (解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定

    • read (读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用

    • load (载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中

    • use (使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令

    • assign (赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中

    • store (存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用

    • write  (写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中

  • JMM的约定

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9TSEyfPP-1606395141515)(images/JMM模型.png)]

主内存对应的是Java堆中的对象实例部分,工作内存对应的是栈中的部分区域,从更底层的来说,主内存对应的是硬件的物理内存,工作内存对应的是寄存器和高速缓存。

Happen-Before(先行发生规则)

Volatile

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

  1. 保证可见性
  2. 不保证原子性
  3. 禁止指令重排
    • volatile标记的指令前后添加内存屏障(同时实现可见性)

指令重排:编译器优化,指令并行,内存系统


public class VolatileTest {
    
    //private static int num = 0;
    //private static volatile int num = 0;//加了volatile后,线程可以感知num的变化,但无法保证原子性
    private static volatile AtomicInteger num = new AtomicInteger();
    /*
    1. lock
    2. sychronized
    3. 使用Atomic类
     */

    public static void main(String[] args) {
    
        testVisibility();

        System.out.println("======");

        testAtomic();
    }

    static void testVisibility(){
    
        Thread thread = new Thread(new Runnable() {
    
            @Override
            public void run() {
    
//                while (num == 0) {
    
//
//                }
                while (num.get()==0){
    //AtomicInteger不能直接等于

                }
                System.out.println("线程感知num值改变");
            }
        });

        thread.start();

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

        //num = 1;
        num.set(1);
        System.out.println(num);
    }



    public static void testAtomic(){
    
        for (int i = 0; i < 20; i++) {
    
            Thread thread = new Thread(new Runnable() {
    
                @Override
                public void run() {
    
                    for (int j = 0; j < 1000; j++) {
    
                        //num++;
                        num.getAndIncrement();//AtomicInteger + 1,方法CAS
                    }
                }
            });

            thread.start();
        }

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

        System.out.println(num);

    }
}

单例模式

  • 饿汉式单例

    
    //饿汉式单例:在加载时就创建对象,可能会浪费内存
    public class HungryManTest {
          
        private HungryManTest() {
          
            System.out.println(Thread.currentThread().getName());
        }
    
        private final static HungryManTest HUNGRY_MAN_TEST = new HungryManTest();
    
        public static HungryManTest getInstance(){
          
            return HUNGRY_MAN_TEST;
        }
    
        public static void main(String[] args) {
          
            for (int i = 0; i < 10; i++) {
          
                Thread thread = new Thread(new Runnable() {
          
                    @Override
                    public void run() {
          
                        HungryManTest.getInstance();
                    }
                });
    
                thread.start();
            }
        }
    }
    
    
  • 懒汉式单例

    package com.checker.singleton;
    
    public class LazyManTest {
          
        private LazyManTest() {
          
            System.out.println(Thread.currentThread().getName());
        }
    
        private static volatile LazyManTest lazyManTest;
    
        public static LazyManTest getInstance(){
          
    //        if (lazyManTest == null){
          
    //            lazyManTest = new LazyManTest();
    //        }
            ///使用双重检测模式:DCL懒汉式
            if (lazyManTest == null){
          
                synchronized (LazyManTest.class){
          
                    if (lazyManTest == null){
          
                        lazyManTest = new LazyManTest();
                    }
                }
            }
    
            return lazyManTest;
        }
    
        public static void main(String[] args) {
          
            //多线程下单例模式失效
            for (int i = 0; i < 10; i++) {
          
                Thread thread = new Thread(new Runnable() {
          
                    @Override
                    public void run() {
          
                        LazyManTest.getInstance();
                    }
                });
    
                thread.start();
            }
        }
    }
    
    
  • 静态内部类

    public class StaticInnerTest {
          
        private StaticInnerTest(){
          
    
        }
    
        public static class InnerClass{
          
            private static final StaticInnerTest staticInnerTest = new StaticInnerTest();
        }
    
        //利用静态内部类返回单例对象
        public static StaticInnerTest getInstance(){
          
            return InnerClass.staticInnerTest;
        }
    }
    
    
  • 反射破坏单例 与 枚举

    public class SingletonAndReflection {
          
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
          
            //普通方法获取单例
            LazyManTest instance1 = LazyManTest.getInstance();
    
            //用反射破坏单例
            Constructor<LazyManTest> declaredConstructor = LazyManTest.class.getDeclaredConstructor(null);
            declaredConstructor.setAccessible(true);
            //通过反射获取的构造器创建单例
            LazyManTest instance2 = declaredConstructor.newInstance();
    
            System.out.println(instance1);
            System.out.println(instance2);
            /*
            解决方法: 在构造方法中,先将class锁住,判断类模板是否存在,存在则抛出异常
    
            破解方法: 所有对象都为反射创建,不加载类模板
    
            解决方法2: 添加一个flag判断,在对象创建的时候判断flag的值
    
            破解方法2: 用反射再获取修改这个flag
    
            最终方法: 用枚举类创建单例
            */
    
            Constructor<EnumSingle> enumSingleConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
            enumSingleConstructor.setAccessible(true);
            //报错: 无法通过反射创建枚举对象
            EnumSingle instance3 = enumSingleConstructor.newInstance();
    
            System.out.println(instance3);
        }
    }
    
    enum EnumSingle{
          
        INSTANCE;
    
        public EnumSingle getInstance(){
          
            return INSTANCE;
        }
    }
    
    

CAS

compare and swap

比较并交换

public class CASTest {
    
    //CAS compare and swap

    public static void main(String[] args) {
    
        AtomicInteger atomicInteger = new AtomicInteger(10);

        System.out.println(atomicInteger);

        //如果当前对象值为 expect:10 则将对象设置为 update:11
        atomicInteger.compareAndSet(10,12);
        System.out.println(atomicInteger);

        //CAS自增1,底层为自旋锁
        atomicInteger.getAndIncrement();
        System.out.println(atomicInteger);
    }
}

ABA问题

  • 原子引用
public class ABATest {
    

    public static void main(String[] args) {
    
        AtomicInteger atomicInteger = new AtomicInteger(10);
        AtomicStampedReference<Integer> atomicReference = new AtomicStampedReference<>(10, 0);

        new Thread(new Runnable() {
    
            @Override
            public void run() {
    
                //获取原本的版本号
                int stamp = atomicReference.getStamp();
                System.out.println("stamp:" + stamp);

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

                System.out.println("带时间戳");
                System.out.println(atomicReference.compareAndSet(10, 100, stamp, stamp + 1));
                System.out.println(atomicReference.getReference());

                System.out.println("不带时间戳");
                System.out.println(atomicInteger.compareAndSet(10, 100));
                System.out.println(atomicInteger);
            }
        }, "T1").start();

        new Thread(new Runnable() {
    
            @Override
            public void run() {
    
                int stamp = atomicReference.getStamp();
                atomicReference.compareAndSet(10, 30, stamp, stamp + 1);

                stamp = atomicReference.getStamp();
                atomicReference.compareAndSet(30, 10, stamp, stamp + 1);

                atomicInteger.compareAndSet(10, 30);

                atomicInteger.compareAndSet(30, 10);
            }
        }, "T2").start();
    }

}

int在大小超过-127~127时,会new新的对象,应该用Interger类进行操作
//获取原本的版本号
int stamp = atomicReference.getStamp();
System.out.println(“stamp:” + stamp);

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

            System.out.println("带时间戳");
            System.out.println(atomicReference.compareAndSet(10, 100, stamp, stamp + 1));
            System.out.println(atomicReference.getReference());

            System.out.println("不带时间戳");
            System.out.println(atomicInteger.compareAndSet(10, 100));
            System.out.println(atomicInteger);
        }
    }, "T1").start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            int stamp = atomicReference.getStamp();
            atomicReference.compareAndSet(10, 30, stamp, stamp + 1);

            stamp = atomicReference.getStamp();
            atomicReference.compareAndSet(30, 10, stamp, stamp + 1);

            atomicInteger.compareAndSet(10, 30);

            atomicInteger.compareAndSet(30, 10);
        }
    }, "T2").start();
}

}


> int在大小超过-127~127时,会new新的对象,应该用Interger类进行操作
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_44535556/article/details/110204565

智能推荐

leetcode 172. 阶乘后的零-程序员宅基地

文章浏览阅读63次。题目给定一个整数 n,返回 n! 结果尾数中零的数量。解题思路每个0都是由2 * 5得来的,相当于要求n!分解成质因子后2 * 5的数目,由于n中2的数目肯定是要大于5的数目,所以我们只需要求出n!中5的数目。C++代码class Solution {public: int trailingZeroes(int n) { ...

Day15-【Java SE进阶】IO流(一):File、IO流概述、File文件对象的创建、字节输入输出流FileInputStream FileoutputStream、释放资源。_outputstream释放-程序员宅基地

文章浏览阅读992次,点赞27次,收藏15次。UTF-8是Unicode字符集的一种编码方案,采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节。文件字节输入流:每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1。注意1:字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码。定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。UTF-8字符集:汉字占3个字节,英文、数字占1个字节。GBK字符集:汉字占2个字节,英文、数字占1个字节。GBK规定:汉字的第一个字节的第一位必须是1。_outputstream释放

jeecgboot重新登录_jeecg 登录自动退出-程序员宅基地

文章浏览阅读1.8k次,点赞3次,收藏3次。解决jeecgboot每次登录进去都会弹出请重新登录问题,在utils文件下找到request.js文件注释这段代码即可_jeecg 登录自动退出

数据中心供配电系统负荷计算实例分析-程序员宅基地

文章浏览阅读3.4k次。我国目前普遍采用需要系数法和二项式系数法确定用电设备的负荷,其中需要系数法是国际上普遍采用的确定计算负荷的方法,最为简便;而二项式系数法在确定设备台数较少且各台设备容量差..._数据中心用电负荷统计变压器

HTML5期末大作业:网页制作代码 网站设计——人电影网站(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 dreamweaver作业静态HTML网页设计模板_网页设计成品百度网盘-程序员宅基地

文章浏览阅读7k次,点赞4次,收藏46次。HTML5期末大作业:网页制作代码 网站设计——人电影网站(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品 dreamweaver作业静态HTML网页设计模板常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 明星、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 军事、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他 等网页设计题目, A+水平作业_网页设计成品百度网盘

【Jailhouse 文章】Look Mum, no VM Exits_jailhouse sr-iov-程序员宅基地

文章浏览阅读392次。jailhouse 文章翻译,Look Mum, no VM Exits!_jailhouse sr-iov

随便推点

chatgpt赋能python:Python怎么删除文件中的某一行_python 删除文件特定几行-程序员宅基地

文章浏览阅读751次。本文由chatgpt生成,文章没有在chatgpt生成的基础上进行任何的修改。以上只是chatgpt能力的冰山一角。作为通用的Aigc大模型,只是展现它原本的实力。对于颠覆工作方式的ChatGPT,应该选择拥抱而不是抗拒,未来属于“会用”AI的人。AI职场汇报智能办公文案写作效率提升教程 专注于AI+职场+办公方向。下图是课程的整体大纲下图是AI职场汇报智能办公文案写作效率提升教程中用到的ai工具。_python 删除文件特定几行

Java过滤特殊字符的正则表达式_java正则表达式过滤特殊字符-程序员宅基地

文章浏览阅读2.1k次。【代码】Java过滤特殊字符的正则表达式。_java正则表达式过滤特殊字符

CSS中设置背景的7个属性及简写background注意点_background设置背景图片-程序员宅基地

文章浏览阅读5.7k次,点赞4次,收藏17次。css中背景的设置至关重要,也是一个难点,因为属性众多,对应的属性值也比较多,这里详细的列举了背景相关的7个属性及对应的属性值,并附上演示代码,后期要用的话,可以随时查看,那我们坐稳开车了······1: background-color 设置背景颜色2:background-image来设置背景图片- 语法:background-image:url(相对路径);-可以同时为一个元素指定背景颜色和背景图片,这样背景颜色将会作为背景图片的底色,一般情况下设置背景..._background设置背景图片

Win10 安装系统跳过创建用户,直接启用 Administrator_windows10msoobe进程-程序员宅基地

文章浏览阅读2.6k次,点赞2次,收藏8次。Win10 安装系统跳过创建用户,直接启用 Administrator_windows10msoobe进程

PyCharm2021安装教程-程序员宅基地

文章浏览阅读10w+次,点赞653次,收藏3k次。Windows安装pycharm教程新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入下载安装PyCharm1、进入官网PyCharm的下载地址:http://www.jetbrains.com/pycharm/downl_pycharm2021

《跨境电商——速卖通搜索排名规则解析与SEO技术》一一1.1 初识速卖通的搜索引擎...-程序员宅基地

文章浏览阅读835次。本节书摘来自异步社区出版社《跨境电商——速卖通搜索排名规则解析与SEO技术》一书中的第1章,第1.1节,作者: 冯晓宁,更多章节内容可以访问云栖社区“异步社区”公众号查看。1.1 初识速卖通的搜索引擎1.1.1 初识速卖通搜索作为速卖通卖家都应该知道,速卖通经常被视为“国际版的淘宝”。那么请想一下,普通消费者在淘宝网上购买商品的时候,他的行为应该..._跨境电商 速卖通搜索排名规则解析与seo技术 pdf

推荐文章

热门文章

相关标签