Bootstrap

JUC高并发编程学习1

什么是JUC

Java.util.Concurrent 包

进程和线程

  1. 区别:一个进程中可以有多个线程执行
  2. 线程状态
   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:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * 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:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,(过时不候)

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;(终结)
    }
  1. wait和sleep区别
    sleep是Thread的静态方法;wait是Object的方法任何对象实例都可以调用
    /**
     * Causes the currently executing thread to sleep (temporarily cease
     * execution) for the specified number of milliseconds, subject to
     * the precision and accuracy of system timers and schedulers. The thread
     * does not lose ownership of any monitors.
     *
     * @param  millis 参数是睡眠的毫秒数
     *         the length of time to sleep in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *如果秒数是负的抛出异常
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public static native void sleep(long millis) throws InterruptedException;

    /**
     * Causes the current thread to wait until another thread invokes the
     * {@link java.lang.Object#notify()} method or the
     * {@link java.lang.Object#notifyAll()} method for this object.
     * In other words, this method behaves exactly as if it simply
     * performs the call {@code wait(0)}.
     * <p>
     * The current thread must own this object's monitor. The thread
     * releases ownership of this monitor and waits until another thread
     * notifies threads waiting on this object's monitor to wake up
     * either through a call to the {@code notify} method or the
     * {@code notifyAll} method. The thread then waits until it can
     * re-obtain ownership of the monitor and resumes execution.
     * <p>
     * As in the one argument version, interrupts and spurious wakeups are
     * possible, and this method should always be used in a loop:
     * <pre>
     *     synchronized (obj) {
     *         while (&lt;condition does not hold&gt;)
     *             obj.wait();
     *         ... // Perform action appropriate to condition
     *     }
     * </pre>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. See the {@code notify} method for a
     * description of the ways in which a thread can become the owner of
     * a monitor.
     *
     * @throws  IllegalMonitorStateException  if the current thread is not
     *               the owner of the object's monitor.
     * @throws  InterruptedException if any thread interrupted the
     *             current thread before or while the current thread
     *             was waiting for a notification.  The <i>interrupted
     *             status</i> of the current thread is cleared when
     *             this exception is thrown.
     * @see        java.lang.Object#notify()
     * @see        java.lang.Object#notifyAll()
     */
    public final void wait() throws InterruptedException {
        wait(0);
    }

sleep不会释放锁,也不需要占用锁;wait会释放锁,当被notify时
都可以被interrupted方法中断
4. 管程,就是Monitor监视器,也被称为锁。是一种同步机制,保证同一时间,只有一个线程访问被保护的数据或者代码。JVM中同步进入和退出都是基于管程对象实现的
5. 用户线程和守护线程
用户线程:自定义线程
守护线程:比如垃圾回收
示例:主线程结束了,用户线程还在运行,jvm存活

public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "----" + Thread.currentThread().isDaemon());
            while(true){

            }
        },"aa");
        thread.start();

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

在这里插入图片描述

示例: 没有用户线程了,都是守护线程,jvm结束

package com.example.juclearn;

public class Test {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "----" + Thread.currentThread().isDaemon());
            while(true){

            }
        },"aa");
        thread.setDaemon(true);
        thread.start();

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

在这里插入图片描述

========================= Lock接口 ==============================

Synchronized关键字

  1. 修饰一个代码块,被修饰的代码块称为同步语句块,作用范围是{}括起来的代码,作用对象是调用这个代码块的对象
  2. 修饰一个方法,被修饰的方法成为同步方法,作用范围是整个方法,作用对象是调用这个方法的对象。synchronized关键字不能被继承
  3. 修饰一个静态的方法,作用范围是整个静态方法,作用的对象是这个类的所有对象
  4. 修饰一个类,作用范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象

Synchronized实现卖票例子

package com.example.juclearn;

/**
 * 多线程编程实现卖票例子 3个售货员卖30张票
 */

/**
 * 第一步,创建资源类,在类中创建属性和操作方法
 */

class Ticket{
    private int number = 30;

    /**
     * 售票方法
     */
    public synchronized void sale(){
        if (number > 0){
            System.out.println(Thread.currentThread().getName() + "卖第"+ (30-number--+1) +"张票,还剩" + number + "张");
        }
    }

}
public class TestSynchronized {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
//        创建多个线程 调用资源类的方法

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++){
                    ticket.sale();
                }

            }
        }, "AA").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++){
                    ticket.sale();
                }
            }
        }, "BB").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++){
                    ticket.sale();
                }
            }
        }, "CC").start();
    }
}

Lock接口

  1. 实现了比使用synchronized方法和语句可获得的更广泛的锁定操作
  2. 实现类有ReentrantLock(可重入锁)、ReentrantreadWriteLock.ReadLock、ReentrantreadWriteLock.WriteLock
  3. Lock 和Synchronized区别
    Lock不是Java语言内置的,是一个类;Synchronized是Java语言的关键字,是内置的
    synchronized不需要用户手动释放锁;Lock必须手动释放锁,否则会出现死锁
    Lock可以让等待锁的线程响应中断;Synchronized会让等待的线程一直等待下去
    Lock可以得知有没有获得锁;Synchronized不能
    Lock可以提高多个线程进行读操作的效率
package com.example.juclearn.lock;
/**
 * 多线程编程实现卖票例子 3个售货员卖30张票
 */

import java.util.concurrent.locks.ReentrantLock;

/**
 * 第一步,创建资源类,在类中创建属性和操作方法
 */

class Ticket{
    private int number = 30;

    private final ReentrantLock reentrantLock = new ReentrantLock();

    /**
     * 售票方法
     */
    public  void sale(){
        reentrantLock.lock();
        try {
            if (number > 0){
                System.out.println(Thread.currentThread().getName() + "卖第"+ (30-number--+1) +"张票,还剩" + number + "张");
            }
        }finally {
            reentrantLock.unlock();
        }
    }

}
public class TestLock {
    public static void main(String[] args) {
        //        创建多个线程 调用资源类的方法
        Ticket ticket = new Ticket();

        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"AA").start();

        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"BB").start();

        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"CC").start();
    }
}

注:start方法最终调用的本地方法,何时调用由操作系统决定

========================= 线程间通信 ============================
多线程编程步骤:
1 创建资源类,创建属性和方法
2 在资源类中判断、干活、通知(新增)
3 创建多个线程,调用资源类的操作方法

Synchronized实现案例

实例:有两个线程其中一个线程实现对值 +1,另一个线程实现对值 -1

package com.example.juclearn.sync;

class Share {
    private int number = 0;

    public synchronized void incr() throws InterruptedException {
//        判断
        if (number != 0){
            this.wait();
        }
//        干活
        number++;
        System.out.println(Thread.currentThread().getName() + "::"  + number);
//        通知
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
//        判断
        if (number != 1){
            this.wait();
        }
//        干活
        number--;
        System.out.println(Thread.currentThread().getName() + "::"  + number);
//        通知
        this.notifyAll();
    }

}
public class ThreadComDemo1 {
    public static void main(String[] args) {
        Share share = new Share();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();

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

上述代码只有两个线程,当有四个线程时会存在问题,代码如下

package com.example.juclearn.sync;

class Share {
    private int number = 0;

    public synchronized void incr() throws InterruptedException {
//        判断
        if (number != 0){
            this.wait();
        }
//        干活
        number++;
        System.out.println(Thread.currentThread().getName() + "::"  + number);
//        通知
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
//        判断
        if (number != 1){
            this.wait();
        }
//        干活
        number--;
        System.out.println(Thread.currentThread().getName() + "::"  + number);
//        通知
        this.notifyAll();
    }

}
public class ThreadComDemo1 {
    public static void main(String[] args) {
        Share share = new Share();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();

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

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

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


}

在这里插入图片描述

原因:wait和notify 实现中断和虚假唤醒是有可能的,而且此方法应始终在循环中使用

改进:wait在哪里睡在哪里醒,必须要放在循环里才会再次判断

package com.example.juclearn.sync;

class Share {
    private int number = 0;

    public synchronized void incr() throws InterruptedException {
//        判断
        while (number != 0){
            this.wait();
        }
//        干活
        number++;
        System.out.println(Thread.currentThread().getName() + "::"  + number);
//        通知
        this.notifyAll();
    }

    public synchronized void decr() throws InterruptedException {
//        判断
        while (number != 1){
            this.wait();
        }
//        干活
        number--;
        System.out.println(Thread.currentThread().getName() + "::"  + number);
//        通知
        this.notifyAll();
    }

}
public class ThreadComDemo1 {
    public static void main(String[] args) {
        Share share = new Share();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();

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

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

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

Lock实现案例

package com.example.juclearn.lock;

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

class Share{
    private int number = 0;
//    创建可重人锁
    private Lock lock = new ReentrantLock();
//    得到Condition对象
    private Condition condition = lock.newCondition();

    public void incr() throws InterruptedException {
        lock.lock();
        try {
            while (number != 0){
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "::"  + number);
            condition.signalAll();
        }finally {
            lock.unlock();
        }
    }

    public void decr() throws InterruptedException {
        lock.lock();
        try {
            while (number != 1){
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "::"  + number);
            condition.signalAll();
        }finally {
            lock.unlock();
        }
    }
}
public class ThreadComDemo2 {
    public static void main(String[] args) {
        Share share = new Share();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();

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

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

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

===================== 线程间定制化通信 ===========================
实现功能:启动三个线程,按照如下要求
AA打印2次,BB打印3次,CC打印4次
AA打印2次,BB打印3次,CC打印4次
AA打印2次,BB打印3次,CC打印4次

进行10轮

package com.example.juclearn.lock;

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

class ShareResource{
    private int flag = 1;
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    public void print2(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag != 1){
                condition1.await();
            }
            for (int i = 0; i < 2; i++) {
                System.out.println(Thread.currentThread().getName()+":"+"第"+loop+"次");
            }
//            修改标志位
            flag = 2;
//            通知BB线程
            condition2.signal();
        }finally {
            lock.unlock();
        }
    }

    public void print3(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag != 2){
                condition2.await();
            }
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName()+":"+"第"+loop+"次");
            }
            flag = 3;
//            通知CC线程
            condition3.signal();
        }finally {
            lock.unlock();
        }
    }

    public void print4(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag != 3){
                condition3.await();
            }
            for (int i = 0; i < 4; i++) {
                System.out.println(Thread.currentThread().getName()+":"+"第"+loop+"次");
            }
            flag = 1;
//            通知AA线程
            condition1.signal();
        }finally {
            lock.unlock();
        }
    }
}
public class ThreadCusComDemo {
    public static void main(String[] args) {
        ShareResource shareResource = new ShareResource();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print2(i+1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"AA").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print3(i+1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"BB").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print4(i+1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"CC").start();
    }
}

======================= 集合的线程安全 =========================

ArrayList的线程不安全及解决方式

ArrayList的 add 方法源码,没有加Synchronized 表明方法是线程不安全的


    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

package com.example.juclearn.collectionsafe;

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

public class ArrayListSafe {
    public static void main(String[] args) {
//        List<String> list = new ArrayList<>();
//        解决方法1 Vector
//        List<String> list = new Vector<>();
//        解决方法2 Collections
//        List<String> list = Collections.synchronizedList(new ArrayList());
//        解决方式3 CopyOnWriteArrayList
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                String key = UUID.randomUUID().toString().substring(0,8);
                list.add(key);
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }
}

在这里插入图片描述

HashSet线程不安全及解决方案

package com.example.juclearn.collectionsafe;


import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

public class HashSetSafe {
    public static void main(String[] args) {

//        Set<String> set = new HashSet<>();
//        解决方案 使用
        Set<String> set = new CopyOnWriteArraySet<>();

        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                String key = UUID.randomUUID().toString().substring(0,8);
                set.add(key);
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

HashMap线程不安全及解决方案

package com.example.juclearn.collectionsafe;


import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class HashMapSafe {
    public static void main(String[] args) {

//        Map<String,String> map = new HashMap<>();
//        解决方案 使用ConcurrentHashMap
        Map<String,String> map = new ConcurrentHashMap<>();
        for (int i = 0; i < 30; i++) {
            String key = String.valueOf(i);
            new Thread(()->{

                String value = UUID.randomUUID().toString().substring(0,8);
                map.put(key,value);
                System.out.println(map);
            },String.valueOf(i)).start();
        }
    }
}

==================== Synchronized 锁的八种情况======================

Synchronized的具体表现形式

  1. 普通同步方法,锁的是当前对象的实例【相当于是房间门】
  2. 静态同步方法,锁的是当前类的Class对象(字节码文件)【相当于是单元门】
  3. 同步方法块,锁的是Synchronized括号里配置的对象
package com.example.juclearn.sync;

import java.util.concurrent.TimeUnit;

class Phone{
    public static synchronized void senSMS() throws InterruptedException {
        TimeUnit.SECONDS.sleep(4);
        System.out.println("--sendSMS---");
    }

    public  synchronized void sendEmail(){
        System.out.println("---sendEmail---");
    }

    public void sayHello(){
        System.out.println("sayHello---");
    }
}
public class EightTypeForSync {
    public static void main(String[] args) {
        Phone phone = new Phone();
        Phone phone2 = new Phone();
        new Thread(()->{
            try {
                phone.senSMS();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"AA").start();
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(()->{
            phone2.sendEmail();
        },"BB").start();
    }

}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

公平锁和非公平锁

非公平锁:会出现线程饿死情况,效率高
公平锁:效率低

源码


    /**
     * 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
     * 如果参数是true 表明创建的是公平锁
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

公平锁源码


    /**
     * Sync object for fair locks
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

非公平锁源码


    /**
     * Sync object for non-fair locks
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

可重入锁

  1. Synchronized证明可重入锁。可重入锁理解起来相当于我们打开了房间大门,剩下房间内部的所有门都可以随意进入。
package com.example.juclearn.sync;

public class SyncForReentrantLock {
    public static void main(String[] args) {
        Object o = new Object();
        new Thread(()->{
            synchronized (o) {
                System.out.println(Thread.currentThread().getName() + "外层");
                synchronized (o) {
                    System.out.println(Thread.currentThread().getName() + "中层");
                    synchronized (o) {
                        System.out.println(Thread.currentThread().getName() + "内层");
                    }
                }
            }
        },"AA").start();
    }
}

synchronized修饰的方法可以递归调用,导致StackOverFlow

package com.example.juclearn.sync;

public class SyncForReentrantLock1 {
    public synchronized void add() {
        add();
    }
    public static void main(String[] args) {
        SyncForReentrantLock1 syncForReentrantLock1 = new SyncForReentrantLock1();
        syncForReentrantLock1.add();
    }
}

在这里插入图片描述

  1. Lock证明可重入锁
package com.example.juclearn.lock;

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

public class LockForReentrantLock {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();

        try{
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "外层");
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + "内层");
            }finally {
                lock.unlock();
            }
        }finally {
            lock.unlock();
        }
    }
}

死锁

  1. 定义:两个或两个以上线程在执行过程中,因为争夺资源而造成的一种互相等待的现象,如果没有外力干涉,无法再执行下去。
  2. 产生原因:
    系统资源不足
    进程运行推进顺序不合适
    资源分配不当
package com.example.juclearn.deadlock;

import java.util.concurrent.TimeUnit;

public class TestDeadLock {
    public static void main(String[] args) {
        Object a = new Object();
        Object b = new Object();

        new Thread(()->{
            synchronized (a) {
                System.out.println(Thread.currentThread().getName()+"已经获取a,尝试获取b锁");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b){
                    System.out.println(Thread.currentThread().getName()+"已经获取b锁");
                }
            }
        },"t1").start();

        new Thread(()->{
            synchronized (b) {
                System.out.println(Thread.currentThread().getName()+"已经获取b,尝试获取a锁");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (a){
                    System.out.println(Thread.currentThread().getName()+"已经获取a锁");
                }
            }
        },"t2").start();
    }
}

在这里插入图片描述

  1. 验证是否是死锁:jps + jstack
E:\juc>jstack 9408
2022-04-17 12:33:17
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.181-b13 mixed mode):

"DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x000000000212a800 nid=0x24dc waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"t2" #12 prio=5 os_prio=0 tid=0x000000000a9f9800 nid=0x1904 waiting for monitor entry [0x000000000b4ff000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.juclearn.deadlock.TestDeadLock.lambda$main$1(TestDeadLock.java:33)
        - waiting to lock <0x00000000d5ed4fe8> (a java.lang.Object)
        - locked <0x00000000d5ed4ff8> (a java.lang.Object)
        at com.example.juclearn.deadlock.TestDeadLock$$Lambda$2/883049899.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"t1" #11 prio=5 os_prio=0 tid=0x000000000a9f7800 nid=0x21a4 waiting for monitor entry [0x000000000b3ae000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.juclearn.deadlock.TestDeadLock.lambda$main$0(TestDeadLock.java:19)
        - waiting to lock <0x00000000d5ed4ff8> (a java.lang.Object)
        - locked <0x00000000d5ed4fe8> (a java.lang.Object)
        at com.example.juclearn.deadlock.TestDeadLock$$Lambda$1/495053715.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

"Service Thread" #10 daemon prio=9 os_prio=0 tid=0x000000000999b800 nid=0x1174 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000000990e000 nid=0x24e8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000000990d800 nid=0x7fc waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000000990c800 nid=0x2668 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x00000000098e8000 nid=0x226c runnable [0x000000000a15f000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
        at java.net.SocketInputStream.read(SocketInputStream.java:171)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        - locked <0x00000000d5f365a8> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.BufferedReader.fill(BufferedReader.java:161)
        at java.io.BufferedReader.readLine(BufferedReader.java:324)
        - locked <0x00000000d5f365a8> (a java.io.InputStreamReader)
        at java.io.BufferedReader.readLine(BufferedReader.java:389)
        at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:48)

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x00000000097c2800 nid=0x2014 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000000008377000 nid=0x1a6c runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000000008360000 nid=0x24a8 in Object.wait() [0x00000000097ae000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000d5d08ed0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x00000000d5d08ed0> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000008319000 nid=0x253c in Object.wait() [0x000000000959f000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000d5d06bf8> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x00000000d5d06bf8> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x000000000830d800 nid=0x998 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x000000000213f000 nid=0x2628 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002141000 nid=0x7c4 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002142800 nid=0x1948 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002144000 nid=0x25f0 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x000000000999e000 nid=0x24a0 waiting on condition

JNI global references: 316


Found one Java-level deadlock:
=============================
"t2":
  waiting to lock monitor 0x000000000835cd98 (object 0x00000000d5ed4fe8, a java.lang.Object),
  which is held by "t1"
"t1":
  waiting to lock monitor 0x000000000835f418 (object 0x00000000d5ed4ff8, a java.lang.Object),
  which is held by "t2"

Java stack information for the threads listed above:
===================================================
"t2":
        at com.example.juclearn.deadlock.TestDeadLock.lambda$main$1(TestDeadLock.java:33)
        - waiting to lock <0x00000000d5ed4fe8> (a java.lang.Object)
        - locked <0x00000000d5ed4ff8> (a java.lang.Object)
        at com.example.juclearn.deadlock.TestDeadLock$$Lambda$2/883049899.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)
"t1":
        at com.example.juclearn.deadlock.TestDeadLock.lambda$main$0(TestDeadLock.java:19)
        - waiting to lock <0x00000000d5ed4ff8> (a java.lang.Object)
        - locked <0x00000000d5ed4fe8> (a java.lang.Object)
        at com.example.juclearn.deadlock.TestDeadLock$$Lambda$1/495053715.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

;