JUC系列(一二):线程基础、生产与消费模型、8锁问题

news/2024/5/20 7:53:21 标签: 开发语言, java, juc, 高并发, 多线程

发现 学习狂神JUC系列少了开篇 特此补发

线程与进程

线程、进程、如何来解释

进程 : 一个程序 如 QQ.exe Music.exe 程序的集合

一个进程可以包含多个线程,至少包含一个线程

Java 默认是开启两个线程 main GC

线程: 开了一个进程 比如: typora 写字,自动保存(线程负责的)

对于Java 而言创建线程我们学习到的方法有三种 : Thread , Runnable , Callable

PS :Java本身是无法开启线程的!!!

java">    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 */
            }
        }
    }

	// 本地方法 调用底层的 C++ Java本身不能直接操作硬件
    private native void start0();

并发,并行

我们要学习并发编程,首先要了解 : 并发和并行是什么

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

  • CPU一核,模拟出来多条线程,天下武功,唯快不破,快速交替。

并行(多个程序一起走)

  • CPU 多核,多个线程同时执行;如果需要提高性能 : 线程池

查看一下自己的cpu核数

image-20220301201453940

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

所有的公司都很看重,就是效率,比如裁员:一个厉害的可以大于两个不再那么厉害的甚至更多

比如: 做事情的速度 高手:1s,一般人: 10s 肯定是 1s 的更加的之前也更加的有效率;

做到人员(减少),但是技术力却提高了

线程有几个状态

通过Thread 源码里的枚举类:State 中的属性可以看出来

线程有六个状态

java">    public e
num State {
        //新的线程
        NEW,
        //运行中
        RUNNABLE,
        // 阻塞
        BLOCKED,
        //等待,一直等
        WAITING,
        //等待一段时间就不等了
        TIMED_WAITING,
        // 终止线程
        TERMINATED;
    }

wait/sleep的区别

1、他们来自不同的类

wait => object

sleep => Thread

2、关于锁的释放

wait会释放锁,sleep不会释放锁

抽象理解 : sleep 睡着了没办法释放, wait 是等待,有人需要的释放

wait必须在同步代码块中使用,得有需要等的人

sleep可以在任何地方使用

3、是否需要捕获异常

wait 不需要捕获异常

sleep 需要捕获异常

Lock锁

传统 Synchronized

传统火车票案例

java">/**
 * @projectName: JUC
 * @package: Thread
 * @className: SaleTicketDemo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/3/1 20:28
 * @version: 1.0
 */
public class SaleTicketDemo {
    /**
     * 真正的多线程开发 公司中的需要降低耦合性
     * 线程就是一个单独的资源类,没有任何附属的操作
     * 1、 属性。方法
     * */
    public static void main(String[] args) {
        //并发: 多线程操作同一个资源类,把资源丢入线程
        Ticket ticket = new Ticket();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        }, "C").start();
    }
}

//资源类 OOP
class Ticket {
    //属性 方法
    private int number = 50;

    //    买票的方式
    public synchronized void sale() {
        if (number > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余" + number);
        }
    }
}

Lock接口

image-20220301204107904

常用的一些实现类

image-20220301204151242

image-20220301204430613

公平锁 : 十分公平,可以先来后到 比如 前一个线程要执行30s 后面的 需要3s 后者必须等待前者执行完

非公平锁 : 十分不公平,可以根据条件来插队

如何用Lock来编写火车票Demo

  1. new ReentrantLock();
  2. Lock.lock(); 解锁
  3. finally => lock.unlock(); 解锁

Synchronized和Lock区别

  1. Synchronized 内置的java关键字,Lock是一个java
  2. Synchronized 无法获取锁的状态 Lock 可以判断是否获取到锁
  3. Synchronized 会自动释放锁, Lock 必须手动解锁,如果不释放锁 就会死锁
  4. Synchronized 线程1(获得锁,阻塞),线程2(等待,死等),Lock锁不一定会等待
  5. Synchronized 可重入锁,不可以中断,非公平,Lock 可重入锁,可以判断锁,手动调整
  6. Synchronized 适合锁少量代码同步问题,Lock 适合锁大量的同步代码

锁是什么,如何判断锁的是谁

生产者和消费者问题

生产者和消费者问题:Synchronized 版

面试高频 : 单例模式,排序算法,生产者和消费者 死锁

java">package ProduceAndconsum;

/**
 * @projectName: JUC
 * @package: ProduceAndconsum
 * @className: A
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/3/1 21:08
 * @version: 1.0
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
    }
}

//数字 资源类
// 1、 判断等待,通知
class Data {
    private int number = 0;

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

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

但是,写出来这个简单的模型 面试官还是会挑的出毛病来,问题出在那?

问题,现在我们是两个线程 ,我们加到四个线程 A,B,C,D 现在还是安全的吗?答案是肯定的不是

虚假唤醒问题:我们增加到四个线程输出的时候就会发现一些问题来,输出不再是0101了

image-20220301212425229

image-20220301212149214

这里如何解决呢? 将 if 换成 while循环

修改之后根据官方文档的解释之后,将if改编成while

image-20220301212605922

输出就又回到了正常

java">package ProduceAndconsum;

/**
 * @projectName: JUC
 * @package: ProduceAndconsum
 * @className: A
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/3/1 21:08
 * @version: 1.0
 */
public class A {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

//数字 资源类
// 1、 判断等待,通知
class Data {
    private int number = 0;

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

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

生产者和消费者问题: JUC版

在新的学习中 synchronized 有 Lock 替换

那么 wait 和 notify谁来替换呢?

image-20220301212829262

通过 Lock来找到 Condition

image-20220301213020385

image-20220301213059259

package ProduceAndconsum;

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

/**
 * @projectName: JUC
 * @package: ProduceAndconsum
 * @className: B
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/3/1 21:31
 * @version: 1.0
 */


public class B {
    public static void main(String[] args) {
        Data2 data = new Data2();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

//数字 资源类
// 1、 判断等待,通知
class Data2 {
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    //等待
    //    condition.await();
    //唤醒全部
    //    condition.signalAll();

    //    +1
    public void increment() throws InterruptedException {
        lock.lock();
        try {
            while (number != 0) {
                //    等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //    通知其他线程 +1 完毕了
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //    -1
    public void decrement() throws InterruptedException {
        lock.lock();
        try {
            while (number == 0) {
                //    等待
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //    通知其他线程 -1 完毕了
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
java">package ProduceAndconsum;

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

/**
 * @projectName: JUC
 * @package: ProduceAndconsum
 * @className: B
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/3/1 21:31
 * @version: 1.0
 */


public class B {
    public static void main(String[] args) {
        Data2 data = new Data2();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

//数字 资源类
// 1、 判断等待,通知
class Data2 {
    private int number = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    //等待
    //    condition.await();
    //唤醒全部
    //    condition.signalAll();

    //    +1
    public void increment() throws InterruptedException {
        lock.lock();
        try {
            while (number != 0) {
                //    等待
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "=>" + number);
            //    通知其他线程 +1 完毕了
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

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

任何的一个新技术,都不可能是单纯的去替代老的技术,一定有优化和补充

Condition

image-20220301214146773

我们更换完代码之后,可以正常输出但是还是混乱的,我们想要达到 A->B->C->D 这样输出,这个时候就引出了新的知识点

Conddition精准唤醒

java">package ProduceAndconsum;

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

/**
 * @projectName: JUC
 * @package: ProduceAndconsum
 * @className: B
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/3/1 21:31
 * @version: 1.0
 */


public class C {
    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();
    }
}

//数字 资源类
// 1、 判断等待,通知
class Data3 {
    private int number = 1;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();

    public void printA() {
        lock.lock();
        try {
            //    业务,判断 执行 通知
            while (number != 1) {
                //等待
                condition.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>AAA");
            number = 2;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB() {
        lock.lock();
        try {
            //    业务,判断 执行 通知
            while (number != 2) {
                //等待
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>BBB");
            number = 3;
            condition2.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC() {
        lock.lock();
        try {
            //    业务,判断 执行 通知
            while (number != 3) {
                //等待
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName() + "=>BBB");
            number = 1;
            condition.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

输出的结果就达到我们的预期了

image-20220301215538935

8锁现象

锁 ----> 刚new 出来的对象、class

8锁就是关于的锁的八个问题,下面也有四个demo类来阐述各种场景下锁的不同状态

demo1

两个问题:

正常模式下 在两个同步线程方法调用的时候 中途延时1s 会不会改变输出结果

答:不会影响输出顺序

正常模式下 同步方法内延时4s 会不会印象输出

答:不会影响输出顺序

  • 标准情况下 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
  • 发短信延时四秒 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话

demo代码

java">package lock8;

import java.util.concurrent.TimeUnit;

/**
 * @projectName: JUC
 * @package: Lock8
 * @className: lock8Demo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 *  Lock8 就是关于锁的八个问题
 * 1. 标准情况下 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
 * 2. 发短信延时四秒 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
 * @date: 2022/3/2 1:13
 * @version: 1.0
 */
public class lock8Demo {
    public static void main(String[] args) {
        phone phone = new phone();
        new Thread(() -> {
            phone.sendSms();
        }, "A").start();
        //    捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone.call();
        }, "B").start();
    }
}

class phone {
    //synchronized 锁的对象是方法调用者,
    //两个方法用的都是 phone 对象的锁,两个方法谁先拿到锁 谁执行
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }
}

demo2

同步方法执行和普通方法执行顺序

答 :普通方法没有锁,所以普通方法先

两个对象执行会不会影响顺序

答:会 两个不同的对象锁也是不同的 ,对象1 还在等待,对象2调用的call方法不用等待所以先输出

  • 现在新增一个普通方法 问 先发短信还是先发hello 先输出hello
  • 新增两个对象 是先打电话还是先发短信
java">package lock8;

import java.util.concurrent.TimeUnit;

/**
 * @projectName: JUC
 * @package: Lock8
 * @className: lock8Demo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 *  Lock8 就是关于锁的八个问题
 * 3、 现在新增一个普通方法  问 先发短信还是先发hello 先输出hello
 * 4、 新增两个对象 是先打电话还是先发短信
 * @date: 2022/3/2 1:13
 * @version: 1.0
 */
public class lock8Demo2 {
    public static void main(String[] args) {
        //两个对象,现在是两个调用者所以是两个锁
        phone2 phone = new phone2();
        phone2 phone1 = new phone2();
        new Thread(() -> {
            phone.sendSms();
        }, "A").start();
        //    捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone1.call();
        }, "B").start();
    }
}

class phone2 {
    //synchronized 锁的对象是方法调用者,
    //两个方法用的都是 phone 对象的锁,两个方法谁先拿到锁 谁执行
    public synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call() {
        System.out.println("打电话");
    }


    //因为这里没有锁,不受锁的影响
    public void hello() {
        System.out.println("hello");
    }
}

demo3

一个对象调用静态同步方法会不会改动执行顺序

答: 不会 ,static是锁的类模版全局唯一,不会改变锁的执行交换顺序

两个对象调用静态同步方法会不会改变执行顺序

答:不会 static修饰的是类模版,锁的也是类模板而不是类对象,只要是这个类生成的对象,不管多少个都不会改变顺序

  • 一个对象 添加两个静态的同步方法,只有一个对象 先打印 发短信 还是打电话?
  • 添加两个对象,分别调用静态同步方法 先打印 发短信 还是打电话?
java">package lock8;

import java.util.concurrent.TimeUnit;

/**
 * @projectName: JUC
 * @package: Lock8
 * @className: lock8Demo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 *  Lock8 就是关于锁的八个问题
 *  5、添加两个静态的同步方法,只有一个对象 先打印 发短信 还是打电话?
 *  6、 添加两个对象,增加两个同步方法 先打印  发短信 还是打电话?
 * @date: 2022/3/2 1:13
 * @version: 1.0
 */
public class lock8Demo3 {
    public static void main(String[] args) {

        //不管多少对象, 使用的都是底层的唯一class所以不管怎么怎么改变结果都不会变,
        // 因为synchroized锁的是class对象,static修饰的方法 类一开始就加载了,
        new Thread(() -> {
            Phone3.sendSms();
        }, "A").start();
        //    捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            Phone3.call();
        }, "B").start();
    }
}

//phone3是唯一的一个class
class Phone3 {
    //synchronized 锁的对象是方法调用者,
    //两个方法用的都是 phone 对象的锁,两个方法谁先拿到锁 谁执行
    // 这里 static 是静态方法 ,类一加载就有了,这个用的锁不再是 phone锁 而是class锁
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call() {
        System.out.println("打电话");
    }

}

demo4

  • 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,因为锁的不是一个东西,sync锁的是类,static 是锁class
  • 两个对象 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,

答: 不是同一个锁,谁执行快就输出谁,

java">package lock8;

import java.util.concurrent.TimeUnit;

/**
 * @projectName: JUC
 * @package: Lock8
 * @className: lock8Demo
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 *  Lock8 就是关于锁的八个问题
 *  7、一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,因为锁的不是一个东西,sync锁的是类,static 是锁class
 *  8、两个对象 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,
 * @date: 2022/3/2 1:13
 * @version: 1.0
 */
public class lock8Demo4 {
    public static void main(String[] args) {
        Phone4 phone = new Phone4();
        //不管多少对象, 使用的都是底层的唯一class所以不管怎么怎么改变结果都不会变,
        // 因为synchroized锁的是class对象,static修饰的方法 类一开始就加载了,
        new Thread(() -> {
            Phone4.sendSms();
        }, "A").start();
        //    捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            phone.call();
        }, "B").start();
    }
}

//phone3是唯一的一个class
class Phone4 {
    //静态同步方法是锁的 class
    public static synchronized void sendSms() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    //这里普通同步方法锁的是 phone
    public synchronized void call() {
        System.out.println("打电话");
    }

}

小结

我们通过八种不同的情况来理解锁在不同情况下的执行情况:

  • 标准情况下 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
  • 发短信延时四秒 两个线程打印 发短信还是打电话 1/ 发短信 2/打电话
  • 现在新增一个普通方法 问 先发短信还是先发hello 先输出hello
  • 新增两个对象 是先打电话还是先发短信
  • 一个对象 添加两个静态的同步方法,只有一个对象 先打印 发短信 还是打电话?
  • 添加两个对象,分别调用静态同步方法 先打印 发短信 还是打电话?
  • 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,因为锁的不是一个东西,sync锁的是类,static 是锁class
  • 两个对象 一个静态方法和一个普通加锁方法, 先打印 发短信 还是打电话? 先出打电话,

大家可以自己写一下代码看一些这些问题的结果,实践出效果,实践出理解

锁的东西,无非就是对象和 类模版

  1. new 出来的对象 就是锁具体的对象,比如普通同步方法
  2. 带有static 修饰的静态同步方法 锁的是类模版是全局唯一的对象class如 : class

http://www.niftyadmin.cn/n/5055612.html

相关文章

Ubuntu 20.04部署Promethues

sudo lsb_release -r可以看到操作系统版本是20.04&#xff0c;sudo uname -r可以看到内核版本是5.5.19。 sudo wget -c https://github.com/prometheus/prometheus/releases/download/v2.37.1/prometheus-2.37.1.linux-amd64.tar.gz下载必要的组件。 tar -zxf prometheus-2.…

【Leetcode】150.逆波兰表达式求值

一、题目 1、题目描述 给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。 请你计算该表达式。返回一个表示表达式值的整数。 注意: 有效的算符为 +、-、* 和 / 。每个操作数(运算对象)都可以是一个整数或者另一个表达式。两个整数之间的除法总…

Socks5代理、IP代理与其在爬虫开发中的应用

在当今数字化时代&#xff0c;网络安全和数据获取变得愈发重要。代理服务器作为一种关键的技术手段&#xff0c;为网络工程师和爬虫开发人员提供了有力的工具。本文将深入探讨Socks5代理、IP代理以及它们在网络安全和爬虫应用中的角色与意义。 1. 代理服务器简介 代理服务器是…

【数据结构】单链表的基本操作(节点建立、插入删除)

单链表的基本操作 链表的定义链表的创建&#xff08;初始化&#xff09; 不带头结点的链表带头结点的链表 链表的插入和删除 按位序插入 带头结点不带头结点 指定节点的后插操作指定元素的前插操作按位序删除 带头结点不带头结点 指定元素的删除操作 1. 单链表的基本操作 链…

Linux运维常用命令与示例

1、文件和目录操作 ls或ll&#xff1a;列出当前目录的文件和子目录 示例&#xff1a;ls 或 ll cd&#xff1a;切换到指定目录 示例&#xff1a;cd /home/user pwd&#xff1a;显示当前所在目录 示例&#xff1a;pwd mkdir&#xff1a;创建新目录 示例&#xff1a;mkdir ne…

前端开发中或使用Bootstrap时,主轴、交叉轴(侧轴)的概念是什么?

主轴和交叉轴(侧轴)&#xff1a; 主轴&#xff08;Main Axis&#xff09;&#xff1a;主轴是Flex容器的主要方向&#xff0c;通常是元素的排列方向。在Flex布局中&#xff0c;主轴可以是水平方向或垂直方向。Flex容器中的子元素按照主轴排列。交叉轴(侧轴)&#xff08;Cross Ax…

基于Java的教务管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

TrustRadius 评论:为什么 Splashtop 优于 LogMeIn

在当今日益数字化的格局中&#xff0c;远程访问和远程支持工具不仅方便而且至关重要。无论对于居家办公人员&#xff0c;还是对于提供远程支持的 IT 专家&#xff0c;能够安全高效地访问远程系统已成为以技术为导向的日常生活的主要内容。 Splashtop 和 LogMeIn 是远程领域的两…