juc-2-锁应用/线程通信

目录

1 线程安全(库存超卖)

2 锁用法

2.1 同步方法

2.2.同步代码块

2.3 synchronized 作用于静态方法

总结

案例 静态成员变量 (synchronized锁非静态方法)

2.4ReentrantLock类是可重入、互斥、实现了Lock接口的锁

3 死锁产生与排查

4 线程间的(等待与通知机制)

5 原子性分类(原理需要分文章讲解太长)


1 线程安全(库存超卖)

1:多个线程对同一个变量做写的操作 。

2: 集群部署,多个服务对同一个变量做写的操作 (分布式锁)。等等

简单的小案例

public class ThreadRunnable implements Runnable{
    private  int  a=100;
    @Override
    public void run() {
       while (true){
           if(a>1){
             a--;
               System.out.println(Thread.currentThread().getName()+"=="+a);
           }
       }

    }
    public static void main(String[] args) {
        //同一个对象,同一变量 a
        ThreadRunnable thread = new ThreadRunnable();
        //两个线程分别减共享变量a
        new Thread(thread).start();
        new Thread(thread).start();
    }
}

 juc-2-锁应用/线程通信_第1张图片

多线程下变量的不可见性

在多线程并发执行下,多个线程修改共享的成员变量,会出现一个线程修改共享变量后,另一个线程,不能直接看到线程修改后的变量的最新值。

2 锁用法

2.1 同步方法

   由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,

    内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态

  注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类

 public synchronized void add(){}

2.2.同步代码块

被该关键字修饰的语句块会自动被加上内置锁,从而实现同步

注:同步是一种高开销的操作,因此应该尽量减少同步的内容。

    通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可

   synchronized(this){

    }

juc-2-锁应用/线程通信_第2张图片

2.3 synchronized 作用于静态方法

总结

  1.  当synchronized作用于静态方法时,其锁就是当前类的class对象锁。由于静态成员不专属于任何一个实例对象,是类成员,因此通过class对象锁可以控制静态 成员的并发操作。
  2.  synchronized作用于非静态方法时,也可以直接锁类。也可以控制静态 成员的并发操作。
  3.  synchronized作用于非静态方法时,必须是同一个对象才能控制静态 成员的并发操作。

需要注意的是如果一个线程A调用一个实例对象的非static synchronized方法,而线程B需要调用这个实例对象所属类的静态 synchronized方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的class对象,而访问非静态 synchronized 方法占用的锁是当前实例对象锁

juc-2-锁应用/线程通信_第3张图片

案例 静态成员变量 (synchronized锁非静态方法)

   synchronized修饰非静态方法时,锁的是当前实例,不同实例就不是同一把锁了,就不是线程安全 

public class ThreadRunnable5 implements Runnable {
    private static int a = 100;
    @SneakyThrows @Override
    public void run() {
            while (true) {
                if (a > 1) {
                    increase4Obj();
                    Thread.sleep(100);
                }
            }
    }
    public synchronized   void increase4Obj(){
            System.out.println(Thread.currentThread().getName() + "==" + a);
            a--;
    }
    public static void main(String[] args) {
        ThreadRunnable5 thread = new ThreadRunnable5();
        new Thread(thread).start();
        new Thread(thread).start();
    }
}

juc-2-锁应用/线程通信_第4张图片

普通方法锁的就是 对象的实例,不同的实例对象时,就是两个锁。无法保证线程安全

juc-2-锁应用/线程通信_第5张图片

案例 静态成员变量 (synchronized锁静态方法 或 直接锁类)

juc-2-锁应用/线程通信_第6张图片

2.4ReentrantLock类是可重入、互斥、实现了Lock接口的锁

        lock() : 获得锁

        unlock() : 释放锁

  注意及时释放锁,否则会出现死锁,通常在finally代码释放锁 

3 死锁产生与排查

死锁产生

juc-2-锁应用/线程通信_第7张图片

public class ThreadRunnable3 implements Runnable {
    private int count = 1;
    private String locak = "aa";
    @SneakyThrows @Override
    public void run() {
        while (true) {
            count++;
            if (count % 2 == 0) {
                synchronized (locak) {
                    a();
                }
            } else {
                synchronized (this) {
                    b();
                }
            }
        }

    }
    public synchronized void a() {
        System.out.println("我是a方法" + Thread.currentThread().getName());
    }
    public void b() {
        synchronized (locak) {
            System.out.println("我是b方法" + Thread.currentThread().getName());
        }
    }

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

死锁排查

jdk安装目录 jdk1.8.0_341\bin\jconsole.exe

juc-2-锁应用/线程通信_第8张图片

 juc-2-锁应用/线程通信_第9张图片

 juc-2-锁应用/线程通信_第10张图片

4 线程间的(等待与通知机制)

线程等待wait()和通知notify(),主要用于多线程之间的通信协作,而且这两个方法都是属于Object类,说明任何对象都可以调用这两个方法。

        当在一个实例对象上调用了wait()方法之后,当前线程就会在这个对象上等待。直到另外的线程调用了该对象的notify()方法,处于等待状态的线程才得以继续进行。

notifyAll(); 通知所有等待在该对象的线程。

        这样,多线程之间就可以用这两个方法进行通信协作了

 wait() 让出cup释放锁 这个方法一定注意,不要用if判断处理他的逻辑,不要用if判断处理他的逻辑,不要用if判断处理他的逻辑

写的不错的文章

wait()和notify()方法的使用_Morning sunshine的博客-CSDN博客_wait和notify的用法

wait()方法的注意点_EclipseO2的博客-CSDN博客

个人觉得了解即可。并没有太多应用场景,jdk8已经有很多api满足我们日常开发,线程间协作。了解原理即可。

5 原子性分类(原理需要分文章讲解太长)

1synchronized  jdk1.6开始,锁升级过程,偏向锁--->轻量级锁--->重量级锁

2Lock锁,需要自己实现锁的升级过程,底层基于aqs+cas实现

加锁时情况本地工作内存,获取主存上的最新值。 解锁时将新值刷新到主存里。

3ThreadLocal,需要注意内存泄漏。(但是高效)

4原子类CAS 非阻塞

你可能感兴趣的:(java,开发语言)