juc和jvm (剖析部分JDK源码)

本文详细探讨了Java并发编程中的JUC组件,包括并发容器、线程状态、锁机制、并发控制(如CountDownLatch、Semaphore、CyclicBarrier)及线程池原理。此外,还分析了JVM的内存模型、类加载机制、垃圾回收和内存调优。内容覆盖了从ArrayList线程不安全到CopyOnWriteArrayList的线程安全解决方案,以及线程池的实现和优化,强调了线程池的七大参数和拒绝策略。最后,文章还讨论了JVM内存结构,如堆、栈、方法区,以及GC算法和内存分配策略。
摘要由CSDN通过智能技术生成

JUC

本文档主要是记录juc和jvm的学习之路,阳哥,永远滴神!

“生死看淡,不服就干”

“基础不牢,地动山摇”

“发财的方法都写在’刑法’上”

“大学大学: 大家自己学”

“没有无所谓,只有做到位”

“树长得高是因为它的根扎得深”

“沉下来,深深的水,静静地流”

“扫帚不倒,灰尘不会自己跑掉”

“万丈高楼平地起,一切承担靠地基”

SaleTickets

并发/并行

concurrent parallel。分门别类两件事,并行。

三个包

  1. Java.util.concurrent
  2. java.util.concurrent.atomic
  3. java.util.concurrent.locks

线程状态

  1. NEW
  2. RUNNABLE
  3. BLOCKED
  4. WAITTING 一直等待
  5. TIMED_WATTING 过时等待,过时不侯
  6. TERMINATED

java代码

Tickets tickets = new Tickets();

        // Thread(Runnable target, String name)
        // 匿名内部类
        new Thread(new Runnable() {
   
            @Override
            public void run() {
   
                for (int i = 1; i < 40; ++i) {
   
                    tickets.sale();
                    try {
   
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
   
                        e.printStackTrace();
                    }
                }
            }
        }, "A").start();

Lambda 表达式版本:

new Thread(() -> {
    for (int i = 1; i < 40; ++i) tickets.sale(); }, "A").start();

Lambda 表达式

函数式编程

int age = 23;
y = kx + 1;
f(x) = kx + 1;  //动态赋值

lambda

//指的是 函数式接口, 只能有一个接口函数,如果不写@FunctionalInterface,默认根据函数数量来判断,如果一个则是函数式接口,超过一个就是普通接口。
@FunctionalInterface
public interface Foo {
   
    //public void sayHello();
    public int add(int x, int y);
}

口诀:拷贝小括号,写死右箭头,落地大括号

  1. 有且仅有一个函数,无返回值,无参数,方法名可以省略。
Foo foo = () -> {
   };
Foo foo = () -> {
    System.out.println("hello"); };
  1. 有且仅有一个函数,有返回值,有参数
Foo foo = (int x, int y) -> {
   
            System.out.println("hello");
            return x + y;
        };
  1. 使用default后,完整定义+实现。可以有多个default方法,仍然是函数式接口。可以使哟过lambda表达式。
//指的是 函数式接口
@FunctionalInterface
public interface Foo {
   
    //public void sayHello();
    public int add(int x, int y);
    
    public default int mul (int x, int y) {
   
        return x * y;
    }
}
  1. 使用static静态方法, 也可以定义多个,仍然是函数式接口,能用lambda表达式。
@FunctionalInterface
public interface Foo {
   
    //public void sayHello();
    public int add(int x, int y);

    public static int div(int x, int y) {
   
        return x / y;
    }
}

Runnable接口

Runnable 也是 @FunctionalInterface

new Thread(() -> {
    System.out.println("hello"); }, "A").start();

集合类不安全

ArrayList

ArrayList 基础

New ArrayList(); 不指定使用对象类型的时候,默认是Object类型,啥都可以往里面装

java8 一开始new是空引用,第一次add 的时候创建list,初始值长度是10,hashmap初始值长度是16.

		/**
     * Default initial capacity. ArrayList() 默认容量
     */
    private static final int DEFAULT_CAPACITY = 10;


		/** 第一个元素被添加的时候,数组从默认空容量扩容到默认容量
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access


		/** hashMap默认初始容量是 1 << 4,也就是16
     * The default initial capacity - MUST be a power of two.
     */
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

ArrayList扩容,扩容原值的一半 = 10 + 5 = 15. 使用arrays.copyOf 来搬家

扩容代码:

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

第二次扩容,15 + 7.5 = 22; hashmap扩容是原值的一倍。

//hashMap resize部分代码:
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
ArrayList 线程不安全

多个线程对一个ArrayList操作的时候,数据会出错。下述代码中,数据会出错;会出现报错情况,不一定每次都会报错。

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

可能的报错:并发修改异常

java.util.ConcurrentModificationException

解决方法:

使用vector,vector add方法加了symchronized,而ArrayList add方法没有加锁,

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

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

vector数据一致性能保证,但是效率低;arrayList效率高,数据不能保证一致性

Collection 和 Collections

Collection是集合的顶级接口,Collections是工具类

List<String> list = Collections.synchronizedList(new ArrayList<>());

把list变成线程安全的

CopyOnWriteArrayList

List<String> list = new CopyOnWriteArrayList();
/**A thread-safe variant of {@link java.util.ArrayList} in which all mutative
 * operations ({@code add}, {@code set}, and so on) are implemented by
 * making a fresh copy of the underlying array.
 * 通过实现每次都copy来保证线程安全,不会出现 ConcurrentModificationException
 */

add 方法实现:

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(); //释放锁
        }
    }

CopyOnWriteArrayList 和 Vector 和 ArrayList 平级

CopyOnWrite并发容器用于读多写少的并发场景。比如白名单,黑名单,商品类目的访问和更新场景,假如我们有一个搜索网站,用户在这个网站的搜索框中,输入关键字搜索内容,但是某些关键字不允许被搜索。这些不能被搜索的关键字会被放在一个黑名单当中,黑名单每天晚上更新一次。当用户搜索时,会检查当前关键字在不在黑名单当中,如果在,则提示不能搜索。

CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。所以如果你希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器。Vector是增删改查方法都加了synchronized,保证同步,但是每个方法执行的时候都要去获得锁,性能就会大大下降,而CopyOnWriteArrayList 只是在增删改上加锁,但是读不加锁,在读方面的性能就好于Vector,CopyOnWriteArrayList支持读多写少的并发情况。

Set

如下代码仍会报 ConcurrentModificationException 异常,所以是线程不安全的。HashSet内部结构是数组+链表(红黑树),内部本质是HashMap。key还是key,value是一个object

		public static void main(String[] args) {
   
        Set<String> set = new HashSet<>();
        for (int i = 0; i <= 30; ++i) {
   
            new Thread(() -> {
   
                set.add(UUID.randomUUID().toString().substring(0, 8));
                System.out.println(set );
            }, String.valueOf(i)).start();
        }
    }

hashset源码:

		public HashSet() {
   
        map = new HashMap<>(); //表明它是一个hashmap 本质上
    }

		// Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object(); //这个Object会被当作value处理
		public boolean add(E e) {
   
        return map.put(e, PRESENT) == null; //放入key 和 value,value是一个Object
    }

可以使用 CopyOnWriteSet

		Set<String> set = new CopyOnWriteArraySet<>();

		//构造函数 本质上来说,他是个CopyOnWriteArrayList
		public CopyOnWriteArraySet() {
   
        al = new CopyOnWriteArrayList<E>()
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值