zl程序教程

您现在的位置是:首页 >  大数据

当前栏目

JUC 并发编程学习笔记(上)

2023-09-11 14:20:19 时间

1. 什么是JUC

JUC 指的就是 java.util.concurrent 工具包的简称,里面的工具类可以很方便的实现多线程的开发。

在这里插入图片描述
 
java.util 工具包、包、分类。
业务:普通的线程代码 Thread。
Runnable 没有返回值、效率相比 Callable 相对较低!

在这里插入图片描述
 

在这里插入图片描述
 

2. 进程和线程

进程是操作系统中的应用程序、是资源分配的基本单位。
线程是用来执行具体的任务和功能,是CPU调度和分派的最小单位。
一个进程往往可以包含多个线程,至少包含一个。

2.1 进程

一个进程往往可以包含多个线程,至少包含一个。
Java默认有两个线程:一个 main, 一个GC

2.2 线程

对于Java而言:ThreadRunableCallable 是进行开启线程的。

Java 真的可以开启线程吗?
答案:开不了。

Java是没有权限去开启线程的、操作硬件的,这是一个 native 的一个本地方法,它调用的底层是c++代码。

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

 

2.3 并发

多线程操作同一个资源。

  • CPU只有一核,模拟出来多条线程。可以使用CPU快速交替,来模拟多线程。
  • 并发编程的本质:充分利用CPU的资源

2.4 并行

  • CPU多核,多个线程可以同时执行。 我们可以使用线程池

获取 CPU 的核数:

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

在这里插入图片描述
 

2.5 线程的状态

线程的状态:

public enum State {
        // 线程新生
        NEW,

      	//运行状态
        RUNNABLE,

        //阻塞
        BLOCKED,

        //等待,死死地等
        WAITING,

        //超时等待
        TIMED_WAITING,

        //终止
        TERMINATED;
    }

 

2.6 wait 和 sleep 的区别

  1. 来自不同的类
    wait -> Object
    sleep -> Thread

  2. 锁的释放
    wait 会释放锁。
    sleep 处于睡眠状态,不会释放锁。

  3. 使用的范围不同
    wait 必须在同步代码块中。
    sleep 可以在任何地方处于睡眠。

  4. 是否需要捕获异常
    wait 不需要捕获异常。
    sleep 必须要捕获异常。
     

3. Lock锁(重点)

3.1 传统Synchronized

public class SaleTicketDemo1 {

    public static void main(String[] args) {
        // 并发:多线程操作同一个类,把资源丢进线程
        Ticket ticket = new Ticket();

        //@FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)-> {代码}
        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( ()-> {
            ticket.sale();
        }, "C").start();
    }
}

class Ticket {
    private int number = 50;

    // synchronized: 本质就是队列、锁
    public synchronized void sale() {
        if (number  > 0) {
            System.out.println(Thread.currentThread().getName() + "卖出了第" + (number --) + "票,剩余" + number);
        }
    }
}

 
运行结果如下:

A窗口卖出了第50票,剩余49
A窗口卖出了第49票,剩余48
A窗口卖出了第48票,剩余47
A窗口卖出了第47票,剩余46
A窗口卖出了第46票,剩余45
B窗口卖出了第45票,剩余44
B窗口卖出了第44票,剩余43
B窗口卖出了第43票,剩余42
B窗口卖出了第42票,剩余41
B窗口卖出了第41票,剩余40
B窗口卖出了第40票,剩余39
B窗口卖出了第39票,剩余38
B窗口卖出了第38票,剩余37
B窗口卖出了第37票,剩余36
B窗口卖出了第36票,剩余35
B窗口卖出了第35票,剩余34
B窗口卖出了第34票,剩余33
B窗口卖出了第33票,剩余32
B窗口卖出了第32票,剩余31
B窗口卖出了第31票,剩余30
B窗口卖出了第30票,剩余29
B窗口卖出了第29票,剩余28
B窗口卖出了第28票,剩余27
B窗口卖出了第27票,剩余26
B窗口卖出了第26票,剩余25
B窗口卖出了第25票,剩余24
B窗口卖出了第24票,剩余23
B窗口卖出了第23票,剩余22
B窗口卖出了第22票,剩余21
B窗口卖出了第21票,剩余20
B窗口卖出了第20票,剩余19
B窗口卖出了第19票,剩余18
B窗口卖出了第18票,剩余17
B窗口卖出了第17票,剩余16
B窗口卖出了第16票,剩余15
B窗口卖出了第15票,剩余14
B窗口卖出了第14票,剩余13
B窗口卖出了第13票,剩余12
B窗口卖出了第12票,剩余11
B窗口卖出了第11票,剩余10
B窗口卖出了第10票,剩余9
B窗口卖出了第9票,剩余8
B窗口卖出了第8票,剩余7
B窗口卖出了第7票,剩余6
B窗口卖出了第6票,剩余5
A窗口卖出了第5票,剩余4
A窗口卖出了第4票,剩余3
A窗口卖出了第3票,剩余2
A窗口卖出了第2票,剩余1
A窗口卖出了第1票,剩余0

 

3.2 Lock接口

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
 
代码如下:

public class SaleTicketDemo2 {
    public static void main(String[] args) {
        // 并发:多线程操作同一个类,把资源丢进线程
        Ticket2 ticket = new Ticket2();

        //@FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)-> {代码}
        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( ()-> {
            ticket.sale();
        }, "C").start();
    }
}

/**
 * lock三部曲
 * 1. new ReentrantLock()
 * 2. lock.lock()  加锁
 * 3. finally --> lock.unlock()  解锁
 */
class Ticket2 {
    private int number = 50;

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

运行结果如下:

A卖出了50票,剩余49
A卖出了49票,剩余48
A卖出了48票,剩余47
C卖出了47票,剩余46
A卖出了46票,剩余45
A卖出了45票,剩余44
A卖出了44票,剩余43
A卖出了43票,剩余42
A卖出了42票,剩余41
A卖出了41票,剩余40
A卖出了40票,剩余39
A卖出了39票,剩余38
A卖出了38票,剩余37
A卖出了37票,剩余36
A卖出了36票,剩余35
A卖出了35票,剩余34
A卖出了34票,剩余33
A卖出了33票,剩余32
A卖出了32票,剩余31
A卖出了31票,剩余30
A卖出了30票,剩余29
A卖出了29票,剩余28
A卖出了28票,剩余27
A卖出了27票,剩余26
A卖出了26票,剩余25
A卖出了25票,剩余24
A卖出了24票,剩余23
A卖出了23票,剩余22
A卖出了22票,剩余21
A卖出了21票,剩余20
A卖出了20票,剩余19
A卖出了19票,剩余18
A卖出了18票,剩余17
A卖出了17票,剩余16
A卖出了16票,剩余15
A卖出了15票,剩余14
A卖出了14票,剩余13
A卖出了13票,剩余12
A卖出了12票,剩余11
A卖出了11票,剩余10
A卖出了10票,剩余9
B卖出了9票,剩余8
B卖出了8票,剩余7
B卖出了7票,剩余6
B卖出了6票,剩余5
B卖出了5票,剩余4
B卖出了4票,剩余3
B卖出了3票,剩余2
B卖出了2票,剩余1
B卖出了1票,剩余0

 

3.3 Synchronized 和 Lock的区别

  1. Synchronized 是内置的Java关键字; Lock 是一个Java类。
  2. Synchronized 无法判断获取锁的状态; Lock 可以判断是否获取了锁。
  3. Synchronized 会自动释放锁; Lock 必须手动释放锁,如果不释放锁,会出现死锁
  4. Synchronized 线程1(获取锁,阻塞)、线程2(一直等待);Lock 锁不一定会等待下去。
  5. Synchronized 可重入锁,不可以中断的、非公平;Lock 可重入锁,可以判断锁,非公平(可以自己设置)。
  6. Synchronized 适合锁少量的代码同步问题;Lock 适合锁大量的同步代码。
     

4. 生产者和消费者问题

4.1 Synchronized版的生产者和消费者问题

/**
 * 线程之间的通信问题:生产者和消费者问题  等待唤醒,通知唤醒
 * 线程交替执行 A B 操作同一个变量 num = 0
 * A num  + 1
 * B num  - 1
 */
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();
    }

}

//判断等待,业务,通知
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-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0

 
问题存在:如果是A, B, C, D 四个线程
常规的代码如下:

/**
 * 线程之间的通信问题:生产者和消费者问题  等待唤醒,通知唤醒
 * 线程交替执行 A B 操作同一个变量 num = 0
 * A num  + 1
 * B num  - 1
 */
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();
    }

}

//判断等待,业务,通知
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();
    }

}

 
运行结果会出现 number 的值为2或3的情况,这样显然是错误的。

A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
C-->1
A-->2
C-->3
B-->2
C-->3
D-->2
D-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0

 

解决方案:
在这里插入图片描述
 
将 if 改为 while 进行判断即可:

以下是解释为什么 if 改为 while 就可以解决问题了:
拿两个加法线程A、B来说,比如A先执行,执行时调用了wait方法,那它会等待,此时会释放锁,那么线程B获得锁并且也会执行wait方法,两个加线程一起等待被唤醒。此时减线程中的某一个线程执行完毕并且唤醒了这俩加线程,那么这俩加线程不会一起执行,其中A获取了锁并且加1,执行完毕之后B再执行。如果是if的话,那么A修改完num后,B不会再去判断num的值,直接会给num+1。如果是while的话,A执行完之后,B还会去判断num的值,因此就不会执行。

/**
 * 线程之间的通信问题:生产者和消费者问题  等待唤醒,通知唤醒
 * 线程交替执行 A B 操作同一个变量 num = 0
 * A num  + 1
 * B num  - 1
 */
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();
    }

}

//判断等待,业务,通知
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();
    }

}

 
运行结果如下:

A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
C-->1
B-->0
A-->1
B-->0
C-->1
B-->0
A-->1
D-->0
C-->1
B-->0
A-->1
D-->0
C-->1
B-->0
A-->1
D-->0
C-->1
B-->0
A-->1
D-->0
C-->1
D-->0
A-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0

 

4.2 JUC版的生产者和消费者问题

在这里插入图片描述
 
在这里插入图片描述
 
代码实现:

/**
 * 线程之间的通信问题:生产者和消费者问题  等待唤醒,通知唤醒
 * 线程交替执行 A B 操作同一个变量 num = 0
 * A num  + 1
 * B num  - 1
 */
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();
    }

}

//判断等待,业务,通知
class Data2 { //  数字 资源类
    private int number = 0;

    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    // + 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 (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

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

    }
}

 
运行结果如下:

A-->1
B-->0
C-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
B-->0
A-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0
C-->1
D-->0

出现的问题:运行结果是随机的状态出现,我们想要的是有序的执行 A->B->C->D。
 

4.3 Condition 精确的通知和唤醒线程

代码如下:

/**
 * A执行完调用B,B执行完调用C,C执行完调用A
 */
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();
    }
}

class Data3 {
    private Lock lock = new ReentrantLock();

    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private 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");
            // 唤醒指定的人,B
            condition2.signal();
            number = 2;
        } 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");
            // 唤醒指定的人,B
            condition3.signal();
            number = 3;
        } 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");
            // 唤醒指定的人,B
            condition1.signal();
            number = 1;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

 
运行结果如下:

A->执行了A
B->执行了B
C->执行了C
A->执行了A
B->执行了B
C->执行了C
A->执行了A
B->执行了B
C->执行了C
A->执行了A
B->执行了B
C->执行了C
A->执行了A
B->执行了B
C->执行了C
A->执行了A
B->执行了B
C->执行了C
A->执行了A
B->执行了B
C->执行了C
A->执行了A
B->执行了B
C->执行了C
A->执行了A
B->执行了B
C->执行了C
A->执行了A
B->执行了B
C->执行了C

 

5. 八锁现象

测试1代码如下:

/**
 * 八锁,就是关于锁的八个问题
 * 1. 标准情况下,两个线程先打印发短信还是打电话? 发短信 -> 打电话
 * 2. send 延迟4秒,两个线程先打印发短信还是打电话? 发短信 -> 打电话
 */
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();

        // 锁的问题
        new Thread(()->{
            phone.send();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.call();
        }, "B").start();
    }
}

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

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

测试1 运行结果如下:

发短信
打电话

 
测试2代码如下:

/**
 * 3. 增加一个普通方法 先执行发短息还是hello? 普通方法
 * 4. 两个对象都是同步方法,先发短信还是打电话? 打电话
 */
public class Test2 {
    public static void main(String[] args) {

        // 两个对象,两个调用者,两把锁
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();

        // 锁的问题
        new Thread(()->{
            phone1.send();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.call();
        }, "B").start();
    }
}

class Phone2 {
    // synchronized 锁的对象是方法的调用者
    public synchronized void send() {
        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");
    }
}

 
测试2运行结果如下:

打电话
发短信

 

测试3代码如下:

/**
 * 5.增加两个静态的同步方法,只有一个对象 先打印 发短信 还是 打电话? 发短信
 * 6.两个对象! 增加两个静态的同步方法,先打印 发短信 还是 打电话? 发短信
 * */
public class Test3 {
    public static void main(String[] args) {
        //两个对象的class类模板只有一个 static 锁的是class
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();

        //锁的问题
        new Thread(() -> {
            phone1.send();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone2.call();
        }, "A").start();
    }
}

class Phone3 {
    //synchronized 锁的对象是方法的调用者
    //static静态方法
    //类一加载就有了!class模板
    public static synchronized void send() {
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    //这里没有锁!不是同步方法,不受锁的影响
    public void hello() {
        System.out.println("hello");
    }

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

 
测试3运行结果如下:

发短信
打电话

 
测试4代码如下:

/**
 * 7. 1个静态的同步方法,1个普通的同步方法,一个对象,先打印 发短信 还是 打电话? 打电话
 * 8. 1个静态的同步方法,1个普通的同步方法,二个对象,先打印 发短信 还是 打电话? 打电话
 */
public class Test4 {
    public static void main(String[] args) {
        //两个对象的class类模板只有一个 static 锁的是class
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();

        //锁的问题
        new Thread(() -> {
            phone1.send();
        }, "A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone2.call();
        }, "A").start();
    }
}

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

    //普通的同步方法 锁的调用者
    public synchronized void call() {
        System.out.println("打电话");
    }
}

测试4运行结果如下:

打电话
发短信

 
 
创作不易,如果有帮助到你,请给题解点个赞和收藏,让更多的人看到!!!
关注博主不迷路,内容持续更新中。