分类 JavaWeb 下的文章

本文采用知识共享 署名-相同方式共享 4.0 国际 许可协议进行许可。
访问 https://creativecommons.org/licenses/by-sa/4.0/ 查看该许可协议。

Concurrency

CountDownLatch

    CountDownLatch countdownlatch = new CountDownLatch(1); // The difinition count is 1
    System.out.println("Count: " + countdownlatch.getCount()); // 1

    new Thread(() -> {
        try {
            countdownlatch.await(); // Current thread enter await, until count reduced to 0
            System.out.println("Count: " + countdownlatch.getCount()); // 0
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();

    Thread.sleep(1000);
    System.out.println("Count down");
    countdownlatch.countDown(); // down 1

CyclicBarrier

    public class CyclicBarrierExample {

        private static LongAdder count = new LongAdder();
        private static ExecutorService executorService = Executors.newFixedThreadPool(5);
        private static CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> System.out.println(count.longValue()));

        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                executorService.execute(CyclicBarrierExample::add);
            }
            executorService.shutdown();
        }

        private static void add() {
            try {
                count.increment();
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

Semaphore

    Semaphore semaphore = new Semaphore(5);
    for (int i = 0; i < 5; i++) {
        new Thread(() -> {
            try {
                semaphore.acquire(); // Get a semaphore
                semaphore.release(); // Release a semaphore
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
    Thread.sleep(1000);
    System.out.println("Available semaphore: " + semaphore.availablePermits()); // 5
    Semaphore semaphore = new Semaphore(5);
    for (int i = 0; i < 5; i++) {
        new Thread(() -> {
            try {
                semaphore.acquire();
                //semaphore.release(); // Not release
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
    Thread.sleep(1000);
    System.out.println("Available semaphore: " + semaphore.availablePermits()); // 0
    Semaphore semaphore = new Semaphore(5);
    for (int i = 0; i < 5; i++) {
        new Thread(() -> {
            try {
                semaphore.acquire();
                //semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
    Thread.sleep(1000);
    System.out.println("Available semaphore: " + semaphore.availablePermits()); // 0
    semaphore.acquire(); // Because available semaphore is 0, threa enters await status, until available gt 0
    System.out.println("END"); // Always not output END

Atomic

AtomicLong and LongAdder

Jdk8 update LongAdder and DoubleAdder

Under low update contention, the two classes have similar characteristics. But under
high contention, expected throughput of this class is significantly higher, at the expense of higher space consumption.

Recommend use 'LongAdder' and 'DoubleAdder',
But may not be safe of reading(update value when reading), AtomicLong is safer, its principle is CAS(compareAndSwap)

AtomicXX

@Slf4j
@ThreadSafe
    public class AtomicTest {

        private final static int threadTotal = 200;
        private final static int clientTotal = 5000;
        // private static AtomicLong count = new AtomicLong(0);
        private static LongAdder count = new LongAdder(); //Initial value is 0

        public static void main(String[] args) throws Exception {
            ExecutorService executor = Executors.newCachedThreadPool();
            Semaphore semaphore = new Semaphore(threadTotal);
            CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
            log.info("CountdownLatch count: {}", countDownLatch.getCount());

            new Thread(() -> {
                try {
                    countDownLatch.await();
                    executor.shutdown();
                    log.info("CountdownLatch count: {}", countDownLatch.getCount());
                    log.info("count: {}", count.longValue());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }).start();

            Thread.sleep(1000);
            for (int i = 0; i < clientTotal; i++) {
                executor.execute(() -> {
                    try {
                        semaphore.acquire();
                        add();
                        semaphore.release();
                        countDownLatch.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
        }

        private static void add() {
            count.increment();
        }
    }

AtomicReference

An object reference that may be updated atomically.


public class AtomicReferenceTest {
    @Data
    private static class User {
        private String name;
    }

    public static void main(String[] args) {
        AtomicReference<User> longAtomicReference = new AtomicReference<>();
        User oldUser = new User();
        oldUser.setName("old");
        longAtomicReference.set(oldUser);

        User userBak = oldUser; // Copy user addr
        oldUser = new User();

        boolean b = longAtomicReference.compareAndSet(oldUser, userBak); // Check addr value, if equals, update it
        System.out.println(b);
    }
}

### AtomicReferenceFieldUpdater
Modify object field
``` java
    public class AtomicReferenceFieldUpdaterTest {

        @Data
        private static class User {
            volatile String name;
        }

        public static void main(String[] args) {
            AtomicReferenceFieldUpdater<User, String> atomicReferenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(User.class, String.class, "name");
            User user = new User();
            user.setName("Wars");

            atomicReferenceFieldUpdater.compareAndSet(user, user.getName(), "Cat");
            System.out.println(user.getName());
        }
    }

AtomicStampedReference

AtomicStampedReference vs. AtomicReference added one version number param, fix ABA bug

    public class AtomicStampedReferenceTest {

        private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(0, 0);

        public static void main(String[] args) {
            atomicStampedReference.compareAndSet(0, 1, 0, 1);
        }
    }

AtomicLongArray

AtomicLongArray through the index operation array

    public class AtomicLongArrayTest {

        private static AtomicLongArray atomicLongArray = new AtomicLongArray(new long[]{1L, 2L, 3L});

        public static void main(String[] args) {
            atomicLongArray.incrementAndGet(0); // index
        }
    }

Synchronized

  • Code scope
    • If synchronized(this), not unique: The current instance is valid
    • If synchronized(A.class), unique: The all instance is valid
  • Function
    • equals synchronized(this){} this function content
    • The current instance is valid
  • Static function
    • equals synchronized(This.class){} this function content
    • The all instance is valid
  • Class
    • equals synchronized(This.class){} all function content of this class
    • The all instance is valid

volatile

Guaranteed single thread, sequential execution of reads and writes

  1. Wrong usage
    @NotThreadSafe
    public class VolatileTest {

        private static final int MAX_THREAD = 50;
        private static final int EXECUTE_COUNT = 5000;
        private static volatile int count = 0;

        public static void main(String[] args) {
            CountDownLatch countDownLatch = new CountDownLatch(EXECUTE_COUNT);
            // Executed out
            new Thread(() -> {
                try {
                    countDownLatch.await();
                    System.out.println("count: " + count);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();

            // Executor
            ExecutorService executor = Executors.newCachedThreadPool();
            Semaphore semaphore = new Semaphore(MAX_THREAD);
            for (int i = 0; i < EXECUTE_COUNT; i++) {
                executor.execute(() -> {
                    try {
                        semaphore.acquire();
                        count++;
                        semaphore.release();
                        countDownLatch.countDown();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    }
  1. Recommended usage

     public class VolatileTest2 {
    
         private static volatile boolean semaphore = false;
    
         public static void main(String[] args) throws InterruptedException {
             new Thread(() -> {
                 try {
                     while (!semaphore) Thread.sleep(1000);
                     System.out.println("Finish");
                 } catch (Exception e) {
                     e.printStackTrace();
                 }
             }).start();
    
             Thread.sleep(10000);
             semaphore = true;
         }
     }

Singleton publish

  1. Dual if lazy mode

     @ThreadSafe
     public class A_Singleton {
    
         private volatile static A_Singleton instance;
    
         private A_Singleton() {
         }
    
         public static A_Singleton getInstance() {
             if (null == instance)
                 synchronized (A_Singleton.class) {
                     instance = new A_Singleton(); // If no volatile, it may lead to Out-of-order execution initialization
                 }
    
             return instance;
         }
     }
  2. Static scope Hungry mode

     @ThreadSafe
     public class B {
    
         private static B instance;
    
         static {
             instance = new B();
         }
    
         private B() {
         }
    
         public static B getInstance() {
             return instance;
         }
     }
  3. Enum mode

     enum E {
         INSTANCE;
    
         private C_EnumMode instance;
    
         E() {
             instance = new C_EnumMode();
         }
    
         public C_EnumMode getInstance() {
             return instance;
         }
     }

本文采用知识共享 署名-相同方式共享 4.0 国际 许可协议进行许可。
访问 https://creativecommons.org/licenses/by-sa/4.0/ 查看该许可协议。

ApiGateway/Api网关的两种实现

  • Zuul实现

  • 比较新的Gateway实现

1) Zuul实现

1.引入依赖

compile('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client')

2.配置Application类

// 标注当前应用为路由
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {
    public static void main(String[] args) {
    SpringApplication.run(GatewayApplication.class, args);
    }
}

3.配置application.yml

server:
    port: 8080
# 服务注册者配置
eureka:
    client:
        fetch-registry: true
        register-with-eureka: true
        service-url:
            defaultZone: http://localhost:8761/eureka
# 路由配置
zuul:
    routes:
      - path: /qc/**    # 127.0.0.1:8080/qc/**的所有请求
        serviceId: qc   # 路由到qc服务上,对应服务注册者的spring.application.name
        retryable: true # 重试
      - path: /tdp/**
        serviceId: tdp
        retryable: true

2) Gateway实现

1.引入依赖

compile('org.springframework.cloud:spring-cloud-starter-gateway')

2.配置application.yml

server:
  port: 8080
# 服务注册者配置
eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://localhost:8761/eureka
# Gateway配置
spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: false    # 开启则可以以下格式访问服务'http://网关地址/服务名称(大写)/**'
      routes:       # 配置路由
        - id: qc        # 可选,默认值为随机字符串
          uri: lb://qc/ # 转发到qc,lb为eureka服务协议,qc对应服务注册者spring.application.name
          predicates:   # 配置捕获地址,predicates是一个List,需传入'Path=xxx'格式的字符串
            - Path=/qc/**
        - id: tdp
          uri: lb://tdp/
          predicates:
            - Path=/tdp/**

3) 差别

使用上呢以上两种配置

  • 前者发起/qc/qc请求会路由到qc服务的/qc上
  • 后者发起/qc/qc请求会路由到qc服务的/qc/qc上

本文采用知识共享 署名-相同方式共享 4.0 国际 许可协议进行许可。
访问 https://creativecommons.org/licenses/by-sa/4.0/ 查看该许可协议。

通过查看ArrayList源码发现:

private static final int DEFAULT_CAPACITY = 10;

默认容量为10,我们用的最多的add函数

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

add函数第一行调用了ensureCapacityInternal(确保在容量内)

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

先来看calculateCapacity(计算容量),传入了ArrayList内部保存数据的数组、需要确保的容量。
1.如果elementData为空,拿需要确保的容量 和 默认的容量(10)中大的那个返回
,反之返回需要确保容量

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

ensureCapacityInternal还没完,紧接着来到ensureExplicitCapacity(确保明确的容量)

1.操作数+1,迭代时通过modCount确定有没产生并发修改,迭代是改变则抛ConcurrentModificationException
2.需要确保容量如果大于elementData长度,则调用grow(扩容),反之不扩容

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

grow

  1. 默认newCapacity(新容量)为原来elementData长度1.5倍(二进制右移一位为原来的1/2,加上本身1)
  2. 默认容量和需求容量取最大值
  3. 如果newCapacity超过MAX_ARRAY_SIZE(最大数组大小,默认为Integer.MAX_VALUE-8,为什么要减8呢,这篇文章有讲引用类型的开销-From Java code to Java heap),则用hugeCapacity函数尝试获取Integer.MAX_VALUE。
  4. 最后copy一个以newCapacity为容量创建一个新elementData
    private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
    }

    以上就是Java的函数扩容机制。

本文采用知识共享 署名-相同方式共享 4.0 国际 许可协议进行许可。
访问 https://creativecommons.org/licenses/by-sa/4.0/ 查看该许可协议。

运行时期报错为:
The dependencies of some of the beans in the application context form a
异常为
BeanCurrentlyInCreationException

  • 什么是循环依赖?

当我们有两个(当然也可能是多个)类A、B,但是A依赖于B,但B也依赖与A,这就是循环依赖!
A->B->A...

  • 为什么会发生?

当程序启动,Spring Context加载所有的Bean时,会尝试按他们运行的工作顺序创建Bean,
没发生循环依赖的情况:
A->B->C
这里C依赖于B,B依赖于A,不会发生异常,程序正常编译。
循环依赖的发生于
你使用构造函数注入的时候,其他注入方式不会发生此问题,
因为其他方式会在程序用到这个依赖时,才会注入,而不是发生在Spring Context加载阶段。

  • 处理方法
  1. 重新设计你的程序:
    请不要质疑,你的程序设计发生了问题!,分层问题没有处理好,请重新设计它。
    当然,你还可以尝试以下常用的两种解决方案。

  2. 使用@Lazy注解:
    第一种方法是标记@Lazy注解到你的构造函数的参数内,让Spring懒惰的初始化
    这个Bean,即给这个Bean创建一个代理,当真正使用到这个Bean时才会完全创建。

     @Component
     public class CircularDependencyA {   private CircularDependencyB circB; 
          @Autowired
          public CircularDependencyA(@Lazy CircularDependencyB circB) {
              this.circB = circB;
          }
      }
  3. 使用Seter(Spring文档提出的)或者Field方式注入它:
    同样是依赖在使用到的时候才会创建这个依赖。
    Seter:

      @Component
      public class CircularDependencyA { 
         private CircularDependencyB circB;
    
          @Autowired
          public void setCircB(CircularDependencyB circB) {
              this.circB = circB;
          }
      }

    Field:

      @Component
      public class CircularDependencyA {
          @Autowired
          private CircularDependencyB circB;
     }

- 阅读剩余部分 -

Title - Artist
0:00