JUC并发编程(上)

JUC并发编程

一、什么是JUC

JUC:java.util.concurrent,就是java的并发编程包

1614169361523.png

二、进程 线程

进程:一个程序的集合,一个进程至少包含一个线程,如打开的google浏览器

线程:具体执行命令的一系列指令,如打开一个文件,加载弹幕

1614170350552.png

Q:一个java程序包含几个线程?

A:main线程和gc线程

Q:java能否自己开启多线程?

A:不能,java也是一个程序,只能调用计算机底层方法才能实现多线程。

查看线程启动方法start()可知,该方法调用了private native void start0();

public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();

三、并行 并发

并行:CPU多核,多线程同时执行,可以同时执行多个任务

并发:CPU只有1核,模拟多线程,实质是快速交替执行

Q;为什么要做高并发编程

A:充分利用CPU资源,提高效率,一个人能做的事不要用三个人

Q:怎样查看电脑CPU数

A:Runtime.getRuntime().availableProcessors()方法

public class JUCTest01 {

    public static void main(String[] args) {
//        new Thread(()->{
//            System.out.println("hello");
//        }).start();

        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

Q:线程有几种状态

A:6种

 /**
     * A thread state.  A thread can be in one of the following states:
     * 
    *
  • {@link #NEW}
    * A thread that has not yet started is in this state. *
  • *
  • {@link #RUNNABLE}
    * A thread executing in the Java virtual machine is in this state. *
  • *
  • {@link #BLOCKED}
    * A thread that is blocked waiting for a monitor lock * is in this state. *
  • *
  • {@link #WAITING}
    * A thread that is waiting indefinitely for another thread to * perform a particular action is in this state. *
  • *
  • {@link #TIMED_WAITING}
    * A thread that is waiting for another thread to perform an action * for up to a specified waiting time is in this state. *
  • *
  • {@link #TERMINATED}
    * A thread that has exited is in this state. *
  • *
* *

* A thread can be in only one state at a given point in time. * These states are virtual machine states which do not reflect * any operating system thread states. * * @since 1.5 * @see #getState */ public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: *

    *
  • {@link Object#wait() Object.wait} with no timeout
  • *
  • {@link #join() Thread.join} with no timeout
  • *
  • {@link LockSupport#park() LockSupport.park}
  • *
* *

A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called Object.wait() * on an object is waiting for another thread to call * Object.notify() or Object.notifyAll() on * that object. A thread that has called Thread.join() * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: *

    *
  • {@link #sleep Thread.sleep}
  • *
  • {@link Object#wait(long) Object.wait} with timeout
  • *
  • {@link #join(long) Thread.join} with timeout
  • *
  • {@link LockSupport#parkNanos LockSupport.parkNanos}
  • *
  • {@link LockSupport#parkUntil LockSupport.parkUntil}
  • *
*/ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }

Q:线程中的wait(),sleep()有什么区别?

A:区别如下

  1. 来自不同的类,wait来自Object,sleep来之Thread。
  2. 是否会释放锁,wait后会释放锁,sleep不会释放锁。
  3. 是否要捕获异常,wait不需要捕获异常,sleep需要
  4. 使用的位置不同,wait需要用在同步代码块中与notify/notifyall一起使用,sleep任意地方

四、Lock接口

1614176032327.png
1614176073524.png
/**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync(); //默认非公平锁
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

小插曲

锁的地方不对导致的问题

1614179599310.png
1614411324561.png
1614409751629.png

1、A,B,C线程进来时ticketNum都是大于0的,A,B,C都可以进来。

2、当使用while循环时线程会一直执行,与for循环无关。if是判断语,for一次执行一次

lock锁的位置错误

1614412532296.png

错误原因是lock不是唯一的,每一个进来的线程都会new一个锁,相当于没锁。

正常位置

1614412660881.png

锁的地方是对的,但每次都是A线程执行,没其他线程的事

1614179928687.png

有锁时只有一个线程能够在进入,线程A进入while循环后执行后,一直卡着循环,没释放锁。没锁时都可以进入,就会有问题。加个等待时间就会看到交替执行

1614182789128.png
1614182972201.png

Q:synchronize和lock有什么区别

A:区别如下

synchronize Lock
内置关键字 java的一个类
无法判断锁的状态 可以判断是否获取到了锁
会自动释放锁 需手动释放锁,否则死锁
线程A获取到锁线程A阻塞,B也会阻塞 B不一定会一直阻塞,tryLock()方法
可重入锁,不可中断,非公平 可重入锁,可中断,非公平与否可自己判断
使用与少量同步代码问题 适用于大量代码同步问题

五、生产者,消费者问题

package chapter04;

/**
 * @Author Noperx
 * @Create 2021-02-27 17:08
 */
public class JUCTest02 {

    public static void main(String[] args) {
        Data data = new Data();
        new Thread(()->{
            try {
                for (int i = 0; i < 30; i++) {
                    data.product();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"A").start();

        new Thread(()->{
            try {
                for (int i = 0; i < 10; i++) {
                    data.consume();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"B").start();

        new Thread(()->{
            try {
                for (int i = 0; i < 10; i++) {
                    data.consume();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"C").start();

        new Thread(()->{
            try {
                for (int i = 0; i < 10; i++) {
                    data.consume();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"D").start();
    }
}

//资源类,属性+方法
class Data{
    int num = 0;

    public synchronized void product() throws InterruptedException {
        while (num != 0) {
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName()+"->"+num);
        notifyAll();
    }

    public synchronized void consume() throws InterruptedException {
        while (num == 0) {
            this.wait();
        }
        num--;
        System.out.println(Thread.currentThread().getName()+"->"+num);
        notifyAll();
    }
}

虚假唤醒问题

1614417897922.png
1614418142798.png

JUC的方法

1614420076507.png
package chapter04;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author Noperx
 * @Create 2021-02-27 17:40
 */
public class JUCTest03 {public static void main(String[] args) {
    Data02 data = new Data02();
    new Thread(()->{ for (int i = 0; i < 30; i++) data.product(); },"A").start();
    new Thread(()->{ for (int i = 0; i < 30; i++) data.consume(); },"B").start();
    new Thread(()->{ for (int i = 0; i < 30; i++) data.product(); },"C").start();
    new Thread(()->{ for (int i = 0; i < 30; i++) data.consume(); },"D").start();
}
}

//资源类,属性+方法
class Data02{
    int num = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    public void product() {
        lock.lock();
        try {
            while (num != 0) {
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName()+"->"+num);
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void consume() {
        lock.lock();
        try {
            while (num == 0) {
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+"->"+num);
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}

疑问:文档中为何new了两个Condition?

这涉及另外一种用处,顺序执行A->B->C->D

package chapter04;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author Noperx
 * @Create 2021-02-27 18:13
 */
public class JUCTest04 {
    public static void main(String[] args) {
        Data03 data = new Data03();
        new Thread(()->{ for (int i = 0; i < 30; i++) data.product(); },"A").start();
        new Thread(()->{ for (int i = 0; i < 30; i++) data.transport(); },"B").start();
        new Thread(()->{ for (int i = 0; i < 30; i++) data.consume(); },"C").start();
    }

}

//资源类,属性+方法
class Data03{
    int num = 0;
    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();

    public void product() {
        lock.lock();
        try {
            while (num != 0) {
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"->"+num);
            num = 1;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }


    public void transport() {
        lock.lock();
        try {
            while (num != 1) {
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"->"+num);
            num = 2;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void consume() {
        lock.lock();
        try {
            while (num != 2) {
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+"->"+num);
            num = 0;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}



六、八种锁

一个对象,两个同步方法
一个对象,两个同步方法,一个方法中有延迟
两个对象,两个同步方法,一个方法中有延迟
一个对象,一个同步方法,一个普通方法
一个对象,两个静态同步方法,一个方法中有延迟
两个对象,两个静态同步方法,一个方法中有延迟
一个对象,两个同步方法,一个方法中有延迟
一个对象,一个静态同步方法,一个普通方法

这里主要理解synchronize锁的是什么,它锁的是方法的调用者——对象本身,类本身或其他对象如"hello"字符串等

示例代码

package chapter04;

import java.util.concurrent.TimeUnit;

/**
 * @Author Noperx
 * @Create 2021-02-27 21:38
 */
public class JUCTest05 {
    public static void main(String[] args) throws InterruptedException {
        Person person = new Person();
        Person person2 = new Person();
        new Thread(()->{
            try {
                person.buyCar();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{person.buyHouse();}).start();
    }

}


class Person{
    public static synchronized void buyCar() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("买车");
    }

    public static synchronized void buyHouse(){
        System.out.println("买房");
    }

    public void waitToDie(){
        System.out.println("等死");
    }
}

七、并发下的集合

集合类不安全

1614440294000.png

List

package chapter04;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @Author Noperx
 * @Create 2021-02-27 22:07
 */
public class JUCTest06 {
    public static void main(String[] args) {
        //ConcurrentModificationException 并发修改异常,多线程下ArrayList不安全
        /*
        解决办法:
        1、使用安全的集合类 Vector
        2、使用集合工具类 Collections.synchronizedList()
        3、使用JUC下的类 CopyOnWriteArrayList<>()
         */
        List strs = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                strs.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(strs);
            },String.valueOf(i)).start();
        }

    }
}


Exception in thread "1" [af9dc, 99db6, cf297, 8595f, dbc82]
Exception in thread "3" [af9dc, 99db6, cf297, 8595f, dbc82, 22d6a]
Exception in thread "7" [af9dc, 99db6, cf297, 8595f, dbc82, 22d6a, 0ed99, f7126, 8657d]Exception in thread "0" Exception in thread "4" 
[af9dc, 99db6, cf297, 8595f, dbc82, 22d6a, 0ed99, f7126]
[af9dc, 99db6, cf297, 8595f, dbc82]
java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at java.util.AbstractCollection.toString(AbstractCollection.java:461)
    at java.lang.String.valueOf(String.java:2994)
    at java.io.PrintStream.println(PrintStream.java:821)
    at chapter04.JUCTest06.lambda$main$0(JUCTest06.java:23)
    at java.lang.Thread.run(Thread.java:745)
java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at java.util.AbstractCollection.toString(AbstractCollection.java:461)
    at java.lang.String.valueOf(String.java:2994)
    at java.io.PrintStream.println(PrintStream.java:821)
    at chapter04.JUCTest06.lambda$main$0(JUCTest06.java:23)
    at java.lang.Thread.run(Thread.java:745)
java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at java.util.AbstractCollection.toString(AbstractCollection.java:461)
    at java.lang.String.valueOf(String.java:2994)
    at java.io.PrintStream.println(PrintStream.java:821)
    at chapter04.JUCTest06.lambda$main$0(JUCTest06.java:23)
    at java.lang.Thread.run(Thread.java:745)
java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at java.util.AbstractCollection.toString(AbstractCollection.java:461)
    at java.lang.String.valueOf(String.java:2994)
    at java.io.PrintStream.println(PrintStream.java:821)
    at chapter04.JUCTest06.lambda$main$0(JUCTest06.java:23)
    at java.lang.Thread.run(Thread.java:745)
java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
    at java.util.ArrayList$Itr.next(ArrayList.java:851)
    at java.util.AbstractCollection.toString(AbstractCollection.java:461)
    at java.lang.String.valueOf(String.java:2994)
    at java.io.PrintStream.println(PrintStream.java:821)
    at chapter04.JUCTest06.lambda$main$0(JUCTest06.java:23)
    at java.lang.Thread.run(Thread.java:745)

package chapter04;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @Author Noperx
 * @Create 2021-02-27 22:07
 */
public class JUCTest06 {
    public static void main(String[] args) {
        //ConcurrentModificationException 并发修改异常,多线程下ArrayList不安全
        /*
        解决办法:
        1、使用安全的集合类 Vector
        2、使用集合工具类 Collections.synchronizedList()
        3、使用JUC下的类 CopyOnWriteArrayList<>()
         */
        List strs = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                strs.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(strs);
            },String.valueOf(i)).start();
        }

    }
}


[2c222, 9e81a, 12a99, 41fee]
[2c222, 9e81a, 12a99, 41fee, 17bd8, 576ee, 254e7, d145e]
[2c222, 9e81a, 12a99, 41fee, 17bd8, 576ee, 254e7]
[2c222, 9e81a, 12a99, 41fee, 17bd8, 576ee]
[2c222, 9e81a, 12a99, 41fee, 17bd8, 576ee, 254e7, d145e]
[2c222, 9e81a, 12a99, 41fee, 17bd8, 576ee, 254e7, d145e, 8072a]
[2c222, 9e81a, 12a99, 41fee, 17bd8]
[2c222, 9e81a, 12a99, 41fee]
[2c222, 9e81a, 12a99, 41fee]
[2c222, 9e81a, 12a99, 41fee, 17bd8, 576ee, 254e7, d145e, 8072a, 174bb]

CopyOnWriteArrayList和Vector的区别,从源码分析

add方法,一个用的是synchronize关键字,一个用的Locks锁

//Vector 

 /**
     * Appends the specified element to the end of this Vector.
     *
     * @param e element to be appended to this Vector
     * @return {@code true} (as specified by {@link Collection#add})
     * @since 1.2
     */
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }
//CopyOnWriteArrayList

public void add(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            if (index > len || index < 0)
                throw new IndexOutOfBoundsException("Index: "+index+
                                                    ", Size: "+len);
            Object[] newElements;
            int numMoved = len - index;
            if (numMoved == 0)
                newElements = Arrays.copyOf(elements, len + 1);
            else {
                newElements = new Object[len + 1];
                System.arraycopy(elements, 0, newElements, 0, index);
                System.arraycopy(elements, index, newElements, index + 1,
                                 numMoved);
            }
            newElements[index] = element;
            setArray(newElements);
        } finally {
            lock.unlock();
        }
    }

Set

package chapter04;

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @Author Noperx
 * @Create 2021-02-27 22:07
 */
public class JUCTest07 {
    public static void main(String[] args) {
        //ConcurrentModificationException 并发修改异常,多线程下HashSet不安全
        /*
        解决办法:
        1、使用集合工具类 Collections.synchronizedSet()
        2、使用JUC下的类 CopyOnWriteArraySet<>()
         */
        Set strs =new CopyOnWriteArraySet<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                strs.add(UUID.randomUUID().toString().substring(0, 5));
                System.out.println(strs);
            },String.valueOf(i)).start();
        }

    }
}

HashSet本质

/**
     * Constructs a new, empty set; the backing HashMap instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }

/**
     * Adds the specified element to this set if it is not already present.
     * More formally, adds the specified element e to this set if
     * this set contains no element e2 such that
     * (e==null ? e2==null : e.equals(e2)).
     * If this set already contains the element, the call leaves the set
     * unchanged and returns false.
     *
     * @param e element to be added to this set
     * @return true if this set did not already contain the specified
     * element
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

Map

package chapter04;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @Author Noperx
 * @Create 2021-02-27 22:07
 */
public class JUCTest07 {
    public static void main(String[] args) {
        //ConcurrentModificationException 并发修改异常,多线程下ArrayList不安全
        /*
        解决办法:
        使用JUC下的类 ConcurrentHashMap<>()
         */
        Map strs =new ConcurrentHashMap<>();
        for (int i = 0; i < 50; i++) {
            new Thread(()->{
                strs.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
                System.out.println(strs);
            },String.valueOf(i)).start();
        }

    }
}

//实际中使用的HashMap

/**
     * Constructs an empty HashMap with the default initial capacity
     * (16) and the default load factor (0.75).
     */
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

/**
     * Constructs an empty HashMap with the specified initial
     * capacity and load factor.
     *
     * @param  initialCapacity the initial capacity
     * @param  loadFactor      the load factor
     * @throws IllegalArgumentException if the initial capacity is negative
     *         or the load factor is nonpositive
     */
    public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }


/*
new HashMap<>();等价于new HashMap<>(0.75f, 16)

你可能感兴趣的:(JUC并发编程(上))