从零开始学JUC

1、线程和进程

  1. Java默认有几个线程?

    2个----mian和GC

  2. Java真的可以开启线程吗?

    开不了

    public synchronized void start() {
          
          
        // 本地方法,底层的C++ ,Java 无法直接操作硬件
    	private native void start0();
    }
    
  3. 线程的几个状态

    public enum State {
        // 新生
        NEW,
        // 运行
        RUNNABLE,
        // 阻塞
        BLOCKED,
        // 等待,死死地等
        WAITING,
        // 超时等待
        TIMED_WAITING,
        // 终止
        TERMINATED;
    }
    
  4. wait和sleep的区别

    • 来自不同的类

      wait–>Object

      sleep–>Thread

    • wait会释放锁;而sleep不会释放,抱着锁睡觉

    • 使用的范围不同

      wait必须在同步代码块中

      sleep可以在任何地方

在这里插入图片描述

2、Lock锁(重点)

2.1、传统的Synchronized

// 资源 OOP
class Ticket {
    
    
    // 属性+方法
    private int number = 30;
    public synchronized void sale() {
    
    
        if (number > 0) {
    
    
            System.out.println(Thread.currentThread().getName() + "买到了第" + number-- +"张票");
        }
    }
}
/*
    真正的多线程开发,公司中的开发,降低耦合性
    线程就是一个单独的资源类,没有任何附属的操作!
*/
public class TestSynchronized {
    
    
    public static void main(String[] args) {
    
    
        // 并发:多线程操作同一个资源类,把资源丢入线程
        Ticket ticket = new Ticket();
        new Thread(() -> {
    
    
            for (int i = 1; i < 20; i++) {
    
    
                ticket.sale();
            }
        }, "小明").start();
        new Thread(() -> {
    
    
            for (int i = 1; i < 20; i++) {
    
    
                ticket.sale();
            }
        }, "小红").start();
        new Thread(() -> {
    
    
            for (int i = 1; i < 20; i++) {
    
    
                ticket.sale();
            }
        }, "黄牛").start();
    }
}

2.2、Lock 接口

class Ticket_Lock {
    
    
    private int number = 30;
    Lock lock = new ReentrantLock();
    public void sale() {
    
    
        lock.lock();
        try {
    
    
            if (number > 0){
    
    
                System.out.println(Thread.currentThread().getName() + "买到了第" + number-- +"张票");
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }
}
public class TestLock {
    
    
    public static void main(String[] args) {
    
    
        Ticket_Lock ticket = new Ticket_Lock();
        new Thread(()->{
    
    for (int i = 1; i < 20 ; i++)
            ticket.sale();},"A").start();
        new Thread(()->{
    
    for (int i = 1; i < 20 ; i++)
            ticket.sale();},"B").start();
        new Thread(()->{
    
    for (int i = 1; i < 20 ; i++)
            ticket.sale();},"C").start();
    }
}

2.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 适合锁大量的同步代码!

3、生产者和消费者问题

3.1、Synchronized版

class Data{
    
    
    private int number = 0;
    public synchronized void increment() throws InterruptedException {
    
    
        while (number != 0) {
    
         //不能用if进行判断,原因见下
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName() + "===>" + number);
        this.notifyAll();
    }
    public synchronized void decrement() throws InterruptedException {
    
    
        while (number == 0) {
    
        //不能用if进行判断
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "===>" + number);
        this.notifyAll();
    }
}
public class TestPV {
    
    
    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();
    }
}
  • 用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
    在这里插入图片描述

3.2、JUC版

class Data2{
    
    
    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();   //从wait变为await
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "===>" + number);
            condition.signalAll();   //从notifyAll变为signalAll
        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }
    public 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();
        }
    }
}
public class TestCondition {
    
    
    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();
    }
}

3.3、通过Condition精准唤醒

class Data3{
    
    
    private int number = 1;
    Lock lock = new ReentrantLock();
    Condition conditionA = lock.newCondition();
    Condition conditionB = lock.newCondition();
    Condition conditionC = lock.newCondition();
    public void printA() {
    
    
        lock.lock();
        try {
    
    
            while (number != 1) {
    
    
                conditionA.await();
            }
            System.out.println(Thread.currentThread().getName() + "AAAAAA");
            number = 2;
            conditionB.signal();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }
    public void printB() {
    
    
        lock.lock();
        try {
    
    
            while (number != 2) {
    
    
                conditionB.await();
            }
            System.out.println(Thread.currentThread().getName() + "BBBBBB");
            number = 3;
            conditionC.signal();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }
    public void printC() {
    
    
        lock.lock();
        try {
    
    
            while (number != 3) {
    
    
                conditionC.await();
            }
            System.out.println(Thread.currentThread().getName() + "CCCCCC");
            number = 1;
            conditionA.signal();
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            lock.unlock();
        }
    }
}
public class TestCondition2 {
    
    
    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();
    }
}

在这里插入图片描述

4、八锁问题—助你全面理解锁机制

4.1、多个线程使用同一把锁—顺序执行

class Phone1 {
    
    
    public synchronized void sendEmail() {
    
    
        System.out.println("发短信");
    }
    public synchronized void callPhone() {
    
    
        System.out.println("打电话");
    }
}
public class LockDemo1 {
    
    
    public static void main(String[] args) {
    
    
        Phone1 phone1 = new Phone1();
        new Thread(() -> {
    
    phone1.sendEmail();}, "A").start();
        new Thread(() -> {
    
    phone1.callPhone();}, "B").start();
    }   // 发短信-->打电话
}
  • 解释:被 synchronized 修饰的方法,锁的对象是方法的调用者,两个方法调用的对象是同一个,先获得锁的先执行,也就是顺序执行。

4.2、多个线程使用同一把锁,其中某个线程里面还有阻塞—顺序执行

class Phone2 {
    
    
    public synchronized void sendEmail() {
    
    
        System.out.println("发短信");
    }
    public synchronized void callPhone() {
    
    
        System.out.println("打电话");
    }
}
public class LockDemo2 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Phone2 phone2 = new Phone2();
        new Thread(() -> {
    
    phone2.sendEmail();}, "A").start();
        TimeUnit.SECONDS.sleep(2);
        new Thread(() -> {
    
    phone2.callPhone();}, "B").start();
    }   // 发短信-->打电话
}
  • 解释:锁的对象还是方法的调用者,两个方法调用的对象是同一个,先调用的先执行!

4.3、多个线程有锁与没锁—随机执行

class Phone3 {
    
    
    public synchronized void sendEmail() {
    
    
        System.out.println("发短信");
    }
    public void callPhone() {
    
    
        System.out.println("打电话");
    }
}
public class LockDemo3 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Phone3 phone3 = new Phone3();
        new Thread(() -> {
    
    phone3.sendEmail();}, "A").start();
        new Thread(() -> {
    
    phone3.callPhone();}, "B").start();
    }   //随机执行
}
  • 解释:同步方法有锁机制,而普通方法没有 synchronized 修饰,不受锁的影响!

4.4、多个线程使用多把锁—随机执行

class Phone4 {
    
    
    public synchronized void sendEmail() {
    
    
        System.out.println("发短信");
    }
    public synchronized void callPhone() {
    
    
        System.out.println("打电话");
    }
}
public class LockDemo4 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Phone4 phoneA = new Phone4();
        Phone4 phoneB = new Phone4();
        new Thread(() -> {
    
    phoneA.sendEmail();}, "A").start();
        new Thread(() -> {
    
    phoneB.callPhone();}, "B").start();
    }   //随机执行
}
  • 解释:被 synchronized 修饰的方法,锁的对象是调用者,这里的锁是两个不同的对象,也就是有两个不同的资源,所以互不影响。

4.5、Class锁:多个线程使用一个对象—顺序执行

  • 被 synchronized 修饰的方法,锁的对象是调用者;

  • 被 synchronized 和 static 同时修饰的方法,锁的对象是类的 class 对象,是唯一的一把锁。

锁Class和锁对象的区别:

  1. Class 锁 ,类模版,只有一个;
  2. 对象锁 , 通过类模板可以new 多个对象。
class Phone5 {
    
    
    public static synchronized void sendEmail() {
    
    
        System.out.println("发短信");
    }
    public static synchronized void callPhone() {
    
    
        System.out.println("打电话");
    }
}
public class LockDemo5 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Phone5 phone5 = new Phone5();
        new Thread(() -> {
    
    phone5.sendEmail();}, "A").start();
        new Thread(() -> {
    
    phone5.callPhone();}, "B").start();
    }   // 发短信-->打电话
}
  • 解释:两个方法都同时被 synchronized 和 static 修饰,锁的对象就是 Class 模板对象,全局唯一!两个线程争抢同一个锁,并不是因为 synchronized 导致的,程序会从上到下依次执行。

4.6、Class锁:多个线程使用多个对象—顺序执行

class Phone6 {
    
    
    public static synchronized void sendEmail() {
    
    
        System.out.println("发短信");
    }
    public static synchronized void callPhone() {
    
    
        System.out.println("打电话");
    }
}
public class LockDemo6 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Phone6 phoneA = new Phone6();
        Phone6 phoneB = new Phone6();
        new Thread(() -> {
    
    phoneA.sendEmail();}, "A").start();
        new Thread(() -> {
    
    phoneB.callPhone();}, "B").start();
    }   // 发短信-->打电话
}
  • 解释:被 synchronized 修饰 和 static 修饰的方法,锁的对象是类的 class 对象,是唯一的一把锁。
    Class锁是唯一的,所以多个对象使用的也是同一个Class锁。

4.7、Class锁与对象锁:多个线程使用一个对象—随机执行

class Phone7 {
    
    
    public static synchronized void sendEmail() {
    
    
        System.out.println("发短信");
    }
    public synchronized void callPhone() {
    
    
        System.out.println("打电话");
    }
}
public class LockDemo7 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Phone7 phone7 = new Phone7();
        new Thread(() -> {
    
    phone7.sendEmail();}, "A").start();
        new Thread(() -> {
    
    phone7.callPhone();}, "B").start();
    }   //随机执行
}
  • 解释:被 synchronized和static修饰的方法,锁的对象是类的class对象!唯一的同一把锁;只被synchronized修饰的方法,是普通锁(如对象锁),不是Class锁,所以进程之间执行顺序互不干扰。

4.8、Class锁与对象锁:多个线程使用多个对象—随机执行

class Phone8 {
    
    
    public static synchronized void sendEmail() {
    
    
        System.out.println("发短信");
    }
    public synchronized void callPhone() {
    
    
        System.out.println("打电话");
    }
}
public class LockDemo8 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Phone8 phoneA = new Phone8();
        Phone8 phoneB = new Phone8();
        new Thread(() -> {
    
    phoneA.sendEmail();}, "A").start();
        new Thread(() -> {
    
    phoneB.callPhone();}, "B").start();
    }   //随机执行
}
  • 解释:被 synchronized和static修饰的方法,锁的对象是类的class对象!唯一的同一把锁;只被synchronized修饰的方法,是普通锁(如对象锁),不是Class锁,所以进程之间执行顺序互不干扰。

小结

  1. new this 本身的这个对象,调用者
  2. static class 类模板,保证唯一!
一个对象中含有多个synchronized方法,当一个线程去访问synchronized修饰的方法时就会加锁,其他线程就会被阻塞;就像一个独立的公共厕所,只允许一个人进去。
静态同步方法锁的是Class对象,所有的静态同步方法的锁唯一,就算有多个线程访问多个资源也会被阻塞,因为这些资源都来自同一个Class对象,而Class对象唯一!

猜你喜欢

转载自blog.csdn.net/qq_42372017/article/details/109544292