JUC(1)

文章基于【狂神说Java】JUC并发编程最新版通俗易懂_哔哩哔哩_bilibili 视频总结而来

目录

1. 什么是 JUC

2. 线程和进程

3.Lock锁

4.生产者和消费者问题

5.Conditional

6. 锁问题


1. 什么是 JUC

juc就是 java并发包

java.util.concurrent JUC

java.util.concurrent.atomic 原子性

java.util.concurrent.locks lock锁

java.util.function 函数式方法

Runnable:没有返回值,效率相比Callable相对较低

2. 线程和进程

进程:

  • 一个程序,本质是程序的集合,可执行。
  • 一个进程往往包含多个线程,至少一个。
  • JAVA默认有两个线程 main GC

线程: 开了一个进程,对JAVA而言:Thread,Runnable,Callable

“java真的可以开启线程吗”

我们new一个thread:

    public static void main(String[] args) {
        new Thread().start();
    }

我们进入start方法可以看到:

    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();		//调用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 */
            }
        }
    }

可以看到 start 方法是一个安全的被synchronized修饰的方法。核心是调用start0方法。

private native void start0();

可以看大start0是一个被native修饰的方法,也就是本地方法。

本地方法实际调用了底层的C++方法,java无法直接操作硬件

并发和并行

并发编程: 并发、并行

并发(多个线程操作同一个资源)

        一核心CPU模拟出来的多条线程,快速交替

并行(多个人一起走)

        多核心CPU多个线程同时执行;线程池、

public class Test1 {
    public static void main(String[] args) {
        // 获取处理器核心数
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

并发编程的本质: 充分运用CPU的资源

线程有几个状态

    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. 新生 NEW
1. 运行 RUNNABLE
1. 阻塞 BLOCKED
1. 等待,死等 WAITING
1. 超时等待 TIMED_WAITING
1. 中止 TERMINATED

wait 和 sleep的区别:

1. 类不同:

                wait 是 object类

                sleep 是 Thread类

2. 关于锁的释放

        wait会释放锁,sleep不会释放

3. 适用范围不同

        wait必须在同步代码块中

        sleep可以在任何地方

4. 需要捕获异常

        wait不需要捕获异常

        sleep可能会发生超时等待的情况,需要捕获异常

3.Lock锁

传统synchronize锁

package com.demo;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName SaleTicket.java
 * @Description 买票的例子
 *              线程就是一个单独的资源类,没有任何附属
 *              1. 属性
 *              2. 方法
 * @createTime 2022年04月27日 10:11:00
 */
public class SaleTicket {
    public static void main(String[] args) {
        //并发:多线程操作同一个资源类
        Ticket ticket = new Ticket();


        //@FunctionInterface 函数式接口: 在jdk1.8后使用lambda表达式
        //lambda表达式: (参数)->{ 代码 },名字

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

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

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

    /**
     * 资源类OOP
     */
    static class Ticket{
        private int number = 50;
        public void sale(){
            if (number>0){
                System.out.println(Thread.currentThread().getName()+"卖出了"+number--+"张票。"+" 余票:"+number);
            }
        }
    }
}

上面的代码会出现线程并发问题,也就是争抢线程的时候,顺序完全乱掉

 这时候,最简单的方式就是加synchronized同步锁,来达到顺序执行的目的

static class Ticket{
    private int number = 30;
    // synchronized 原理就是排队,
    public synchronized void sale(){
       if (number>0){
          System.out.println(Thread.currentThread().getName()+"卖出了"+number--+"张票。"+" 余票:"+number);
       }
    }
}

 

Lock

 Lock lock = new ReentrantLock();

我们看到ReentrantLock的源码:

    /**
     * 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();
    }

        可以看到,无参构造的时候,进来的是一个非公平锁,如果传参,可以根据Boolean值来确定创建的锁是公平锁还是非公平锁

公平锁:

        十分公平,先来后到

非公平锁:

        不公平,可以插队

修改代码使用Lock加锁,一样可以实现加锁的功能 :

    static class Ticket2{
        private int number = 30;
        Lock lock = new ReentrantLock();
        public void sale(){
            lock.lock(); //加锁
            try {
                //业务代码
                if (number>0){
                    System.out.println(Thread.currentThread().getName()+"卖出了"+number--+"张票。"+" 余票:"+number);
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.unlock();  //解锁
            }
        }
    }

synchronize锁和lock的区别

  1. synchronize 是java内置的关键字,lock 是一个java类

  2. synchronize 无法判断锁的状态,lock 就可以判断是否获取到了锁

  3. synchronize 可以自动释放锁,lock 需要手动释放,不释放会出现死锁

  4. synchronize 线程1(获得锁,阻塞)、线程2(等待),Lock 锁不一定会等待。可以通过

     lock.tryLock()

    方法尝试获取锁

  5. synchronize 可重入锁,非公平;lock, 可重入锁,可判断锁,非公平锁(可以自己设置种类);

  6. synchronize 适合锁少量的代码同步问题,lock 适合锁大量的代码

4.生产者和消费者问题

生产者和消费者的synchronize问题

package com.demo.pc;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName A.java
 * @Description 线程通信问题:
 *              A,B操作同一个线程: 通知唤醒,等待唤醒
 * @createTime 2022年04月27日 13:24:00
 */
public class A {

    public static void main(String[] args) {
        Data data = new Data();

        new Thread(()->{
            for (int i = 0; i <30 ; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i = 0; i <30 ; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"B").start();
    }
}

class Data{
    private int number = 0;

    public synchronized void increment() throws InterruptedException {
        if(number != 0){
            //等待操作
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知线程,操作完毕
        this.notifyAll();
    }

    public synchronized  void  decrement() throws InterruptedException {
        if (number==0){
            //等待操作
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知线程,操作完毕
        this.notifyAll();
    }
}

如果有4个线程呢,就会出现虚假唤醒问题

解决办法: 等待应该总是出现在循环中,if 改为 while判断

package com.demo.pc;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName A.java
 * @Description 线程通信问题:
 *              A,B操作同一个线程: 通知唤醒,等待唤醒
 * @createTime 2022年04月27日 13:24:00
 */
public class A {

    public static void main(String[] args) {
        Data data = new Data();

        new Thread(()->{
            for (int i = 0; i <30 ; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i = 0; i <30 ; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"B").start();

        new Thread(()->{
            for (int i = 0; i <30 ; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"C").start();

        new Thread(()->{
            for (int i = 0; i <30 ; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"D").start();
    }

}

class Data{
    private int number = 0;

    public synchronized void increment() throws InterruptedException {
        while (number != 0){
            //等待操作
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知线程,操作完毕
        this.notifyAll();
    }

    public synchronized  void  decrement() throws InterruptedException {
        while (number==0){
            //等待操作
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知线程,操作完毕
        this.notifyAll();
    }
}

5.Conditional

package com.demo.pc;

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

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName A.java
 * @Description 线程通信问题:
 *              A,B操作同一个线程: 通知唤醒,等待唤醒
 * @createTime 2022年04月27日 13:24:00
 */
public class A {

    @SuppressWarnings("AlibabaAvoidManuallyCreateThread")
    public static void main(String[] args) {
        Data data = new Data();

        new Thread(()->{
            for (int i = 0; i <30 ; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i = 0; i <30 ; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"B").start();

        new Thread(()->{
            for (int i = 0; i <30 ; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"C").start();

        new Thread(()->{
            for (int i = 0; i <30 ; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"D").start();
    }

}

class Data{
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    public void increment() throws InterruptedException {
        lock.lock();
        try {
            while (number != 0){
                //等待操作
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            //通知线程,操作完毕
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public synchronized  void  decrement() throws InterruptedException {
        lock.lock();
        try {
            while (number==0){
                //等待操作
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"=>"+number);
            //通知线程,操作完毕
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

可以得到和synchronize一样的结果 。

conditional 的优势,可以精准通知唤醒线程

package com.demo.pc;

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

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName B.java
 * @Description A执行完调用B,B执行完调用C,C执行完调用A
 * @createTime 2022年04月27日 17:20:00
 */
public class B {
    public static void main(String[] args) {
        Data3 data = new Data3();

        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                data.printA();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                data.printB();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i <10 ; i++) {
                data.printC();
            }
        },"C").start();
    }

   static class Data3{
        private final Lock lock = new ReentrantLock();
        private final Condition condition1 = lock.newCondition();
        private final Condition condition2 = lock.newCondition();
        private final Condition condition3 = lock.newCondition();
        private int number = 1;
        public void printA(){
            lock.lock();
            try {
                //判断 -》执行 -》通知
                while (number!=1){
                    condition1.await();
                }
                System.out.println(Thread.currentThread().getName()+"A");
                number++;
                condition2.signal();

            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
        public void printB(){
            lock.lock();
            try {
                while (number!=2){
                    condition2.await();
                }
                System.out.println(Thread.currentThread().getName()+"B");
                number++;
                condition3.signal();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
        public void printC(){
            lock.lock();
            try {
                while (number!=3){
                    condition3.await();
                }
                System.out.println(Thread.currentThread().getName()+"C");
                number=1;
                condition1.signal();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        }
    }
}

通过监视器指定唤醒

6. 锁问题

Q1:以下代码是会先发短信还是先打电话

package com.demo.pc;

import java.util.concurrent.TimeUnit;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName Lock8.java
 * @Description 8锁,就是锁的8个问题
 * @createTime 2022年04月28日 10:58:00
 */
public class Lock8 {

    public static void main(String[] args) {
        Phone phone = new Phone();

        new Thread(()->{
            phone.sendMessage();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        new Thread(()->{
            phone.phoneCall();
        },"B").start();
    }
}
class Phone{
    public synchronized void sendMessage(){
        System.out.println("Send message");
    }

    public synchronized void phoneCall(){
        System.out.println("call");
    }
}

Q2:如果我们让sendMessage延迟4秒,先发短信还是打电话

package com.demo.pc;

import java.util.concurrent.TimeUnit;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName Lock02.java
 * @Description TODO
 * @createTime 2022年04月28日 14:28:00
 */
public class Lock02 {
    public static void main(String[] args) {
        Phone2 phone = new Phone2();

        new Thread(()->{
            phone.sendMessage();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        new Thread(()->{
            phone.phoneCall();
        },"B").start();
    }
}
class Phone2 {
    public synchronized void sendMessage() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("Send message");
    }

    public synchronized void phoneCall() {
        System.out.println("call");
    }
}

        Q1和Q2都是锁的问题,我们使用synchronize关键字, 锁的对象是方法的调用者(对象锁:phone),也就是我们new的phone对象,不论是phoneCall还是sendMessage,都拿到的是同一把锁,所以谁先拿到,谁先执行。

Q3: 我们增加一个普通方法hello方法,然后让线程B去调用hello,是先发短信还是hello

package com.demo.pc;

import java.util.concurrent.TimeUnit;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName Lock8.java
 * @Description 8锁,就是锁的8个问题
 * @createTime 2022年04月28日 10:58:00
 */
public class Lock8 {

    public static void main(String[] args) {
        Phone phone = new Phone();

        new Thread(()->{
            phone.sendMessage();
        },"A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        new Thread(()->{
            phone.hello();
        },"B").start();
    }
}
class Phone{
    public synchronized void sendMessage(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("Send message");
    }

    public synchronized void phoneCall(){
        System.out.println("call");
    }
	
    //这里没有锁
    public void hello(){
        System.out.println("hello");
    }
}

因为hello没有锁,不受锁的影响,所以先打印出hello,再打印的SendMessage

Q4:我们新建一个对象phone2,用第一个对象去调用发短信方法,第二个对象去调用打电话方法,哪一个先执行?

package com.demo.pc;

import java.util.concurrent.TimeUnit;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName Lock03.java
 * @Description TODO
 * @createTime 2022年04月28日 14:44:00
 */
public class Lock03 {
    public static void main(String[] args) {
        Phone3 phone = new Phone3();
        Phone3 phone2 = new Phone3();
        
        new Thread(() -> {
            phone.sendMessage();
        }, "A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        new Thread(() -> {
            phone2.phoneCall();
        }, "B").start();
    }
}
    class Phone3 {
        public synchronized void sendMessage() {
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("Send message");
        }

        public synchronized void phoneCall() {
            System.out.println("call");
        }
    }

        这时候两个对象,所以有两把锁,phong和phong2拿到的是不一样的锁,所以就按实现之星,先打电话,再发短信

Q5: 如果我们把打电话和发短信的方法换成静态方法,会先执行哪一个?

package com.demo.pc;

import java.util.concurrent.TimeUnit;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName Lock.java
 * @Description TODO
 * @createTime 2022年04月28日 14:52:00
 */
public class Lock04 {
    public static void main(String[] args) {
        Phone4 phone = new Phone4();

        new Thread(() -> {
            phone.sendMessage();
        }, "A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        new Thread(() -> {
            phone.phoneCall();
        }, "B").start();
    }
}
class Phone4 {
    public static synchronized void sendMessage() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("Send message");
    }

    public static synchronized void phoneCall() {
        System.out.println("call");
    }
}

        我们使用static修饰,类一加载的时候就有了,所以synchronize锁的是这个class模板。在我们用static修饰方法的时候,我们的 Class Phone4就只有一个class对象,所以两个调用拿到的是同一把锁。

Q6: 两个对象,phone和phone2,phone调用发短信方法,phone2调用打电话方法,会先发短信还是先打电话?

package com.demo.pc;

import java.util.concurrent.TimeUnit;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName Lock.java
 * @Description TODO
 * @createTime 2022年04月28日 14:52:00
 */
public class Lock04 {
    public static void main(String[] args) {
        Phone4 phone = new Phone4();
        Phone4 phone2 = new Phone4();
        new Thread(() -> {
            phone.sendMessage();
        }, "A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        new Thread(() -> {
            phone2.phoneCall();
        }, "B").start();
    }
}
class Phone4 {
    public static synchronized void sendMessage() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("Send message");
    }

    public static synchronized void phoneCall() {
        System.out.println("call");
    }
}

        我们使用static修饰,synchronize是锁住了这个Class模板,所以及时是两个对象,但是拿到的还是同一把锁,所以是下发短信再打电话

Q7: 一个静态同步方法,一个普通同步方法hello,一个对象调用,先发短信还是先hello

package com.demo.pc;

import java.util.concurrent.TimeUnit;

/**
 * @author Zeyu Wan
 * @version 1.0.0
 * @ClassName Lock5.java
 * @Description TODO
 * @createTime 2022年04月28日 15:03:00
 */
public class Lock5 {
    public static void main(String[] args) {
        Phone5 phone = new Phone5();

        new Thread(() -> {
            phone.sendMessage();
        }, "A").start();

        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        new Thread(() -> {
            phone.hello();
        }, "B").start();
    }
}
class Phone5 {
    public static synchronized void sendMessage() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("Send message");
    }

    public static synchronized void phoneCall() {
        System.out.println("call");
    }

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

        会先执行hello,再执行发短信,因为static所得是class模板,普通同步方法所得是调用对象,不是一个锁所以先执行了hello

总结:

锁去锁住的东西一般分为两种,一种是new出来的对象,一种是static的,也就是类一开始就加载出来的class模板对象。锁是否执行,需要注意是不是同一个对象,同一把锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PigeonEssence

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值