多线程——各种锁

一、synchronized 加锁方法与原理

synchronized是一种互斥锁

  • 一次只能允许一个线程进入被锁住的代码块

synchronized是一种内置锁/监视器锁

  • Java中每个对象都有一个内置锁(监视器,也可以理解成锁标记),而synchronized就是使用**对象的内置锁(监视器)**来将代码块(方法)锁定的

synchronized的好处

  • synchronized保证了线程的原子性。(被保护的代码块是一次被执行的,没有任何线程会同时访问)
  • synchronized还保证了可见性。(当执行完synchronized之后,修改后的变量对其他的线程是可见的)

synchronized的缺点

synchronized加锁公式
//修饰方法
public synchronized void test1(){
}
//修饰代码块
synchronized (任意对象){
}
  1. 普通同步方法,锁是当前实例对象
  2. 静态同步方法,锁是当前类的class对象
  3. 同步方法块,锁是括号里面的对象

特性

重入锁

public class Widget {    // 锁住了    
	public synchronized void doSomething() {        ...    }
	}
public class LoggingWidget extends Widget {    // 锁住了    
    public synchronized void doSomething() {        
        System.out.println(toString() + ": calling doSomething");    
        super.doSomething();    
    }
}
  1. 当线程A进入到LoggingWidget的doSomething()方法时,此时拿到了LoggingWidget实例对象的锁
  2. 随后在方法上又调用了父类Widget的doSomething()方法,它又是被synchronized修饰
  3. 那现在我们LoggingWidget实例对象的锁还没有释放,进入父类Widget的doSomething()方法还需要一把锁吗?

不需要的!

因为锁的持有者是“线程”,而不是“调用”。线程A已经是有了LoggingWidget实例对象的锁了,当再需要的时候可以继续**“开锁”**进去的!

这就是内置锁的可重入性

二、Lock接口

在这里插入图片描述
在这里插入图片描述

  • Lock方式来获取锁支持中断、超时不获取、是非阻塞的
  • 提高了语义化,哪里加锁,哪里解锁都得写出来
  • Lock显式锁可以给我们带来很好的灵活性,但同时我们必须手动释放锁
  • 支持Condition条件对象
  • 允许多个读线程同时访问共享资源
lock.lock();
    if (court > 0) {
        try {
            Thread.sleep(100);
            System.out.println(Thread.currentThread().getName() + "第" + court--);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //释放锁
            lock.unlock();
        }
    }

三、volatile关键字

内存一致性

Java中的volatile可以看做是“轻量级的synchronized”。synchronized可能会引起上下文切换和线程调度,同时保证可见性、有序性和原子性。volatile不会引起上下文切换和线程调度,但仅提供可见性和有序性保证,不提供原子性保证。

volatile关键字的作用很简单,就是一个线程在对主内存的某一份数据进行更改时,改完之后会立刻刷新到主内存。并且会强制让缓存了该变量的线程中的数据清空,必须从主内存重新读取最新数据。

防止指令重排序

为了增加执行速度,一般执行程序时是乱序执行的。

volatile为了执行准确性,不能进行乱序执行

使用方式

class A{
		int a;
		//volatile关键字直接加到字段上即可保证这个字段有上面两个功能
		volatile int b;
	}

四、JUC原子类

JUC原子类位于java.util.concurrent.atomic包下 ,主要实现的是Unsafe类

1. CAS

在这里插入图片描述

出现ABA问题,加版本号,验证版本号

2.atomic包

可以将包中的类分为五类:

  • 基本类型:AtomicBoolean、AtomicInteger、AtomicLong
  • 引用类型:AtomicReference、AtomicStampedRerence、AtomicMarkableReference
  • 数组:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
  • 对象的属性:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
  • JDK1.8新增:DoubleAccumulator、LongAccumulator、DoubleAdder、LongAdder

使用

public class AtomicTest {
    public static void main(String[] args) throws InterruptedException {
        myAtomic myAtomic = new myAtomic();
        Thread thread1 = new Thread(){
            @Override
            public void run() {
                for (int i = 0 ; i<1000;i++){
                    myAtomic.add(1);
                }
            }
        };
        Thread thread2 = new Thread(){
            @Override
            public void run() {
                for (int i = 0 ; i<1000;i++){
                    myAtomic.add(1);
                }
            }
        };
        thread1.start();
        thread2.start();
        //thread1.join();主线程执行到这必须把那俩子线程执行完才继续的
        //thread2.join();
        Thread.sleep(2000);
        System.out.println(myAtomic.get());
    }
}
class myAtomic{
    private AtomicInteger value = new AtomicInteger(0);
    public int add(int m){
        return value.addAndGet(m);
    }
    public int dec(int m){
        return value.addAndGet(-m);
    }
    public int get(){
        return value.get();
    }
}

五、锁的分类

在这里插入图片描述
前两行是MarkWord:记录锁信息、hashcode、GC
在这里插入图片描述
重量级与轻量级:是否需要操作系统处理的是重量级,反之轻量级
在这里插入图片描述

补充:

使用锁升级来实现

偏向锁—>自旋锁/轻量级锁–>重量级锁(锁升级、锁膨胀)

六、死锁

发生条件

  • 当前线程拥有其他线程需要的资源
  • 当前线程等待其他线程已拥有的资源
  • 都不放弃自己拥有的资源
public static void main(String[] args) {
        LockTest lockTest = new LockTest();
        Thread thread1 = new Thread(lockTest);
        thread1.start();
        Thread thread2 = new Thread(lockTest);
        thread2.start();
        Thread thread3 = new Thread(lockTest);
        thread3.start();
    }
    
public class LockTest implements Runnable {
    private static int court = 100;
    private Object object = new Object();
    private Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            synchronized (object) {
                if (court > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "第" + court--);
                }
            }
        }
    }
}

七、一次错误的尝试

public static void main(String[] args) {
        LockTest lockTest = new LockTest();
        Thread thread1 = new Thread(new LockTest());
        thread1.start();
        Thread thread2 = new Thread(new LockTest());
        thread2.start();
        Thread thread3 = new Thread(new LockTest());
        thread3.start();
    }
    
public class LockTest implements Runnable {
    private static int court = 100;
    private Object object = new Object();
    private Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            synchronized (object) {
                if (court > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "第" + court--);
                }
            }
        }
    }
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值