多线程基本原理和锁的基本原理

多线程基本原理

线程的合理使用能够提升程序的处理性能,主要有两个方面,第一个是能够利用多核 cpu 以及超线程技术来实现线程的并行执行;第二个是线程的异步化执行相比于同步执行来说,异步执行能够很好的优化程序的处理性能提升并发吞吐量

线程安全

如果多个线程访问同一个共享对象,在不需额外的同步以及调用端代码不用做其他协调的情况下,这个对象的结果与我们预期规定的结果保持一致,那说明这个对象是线程安全的。

多线程对于共享变量访问带来的安全问题

多个线程同时对一个变量修改,就会存在数据安全问题。

多线程基本原理和锁的基本原理_第1张图片

线程不安全实例

package com.example.demo.synchronizedX;

import java.io.IOException;
import java.util.concurrent.locks.Lock;

/**
 * @author Andy Cui
 * @version 1.0
 * @date 2021/1/26 14:19
 */
public class SynchronizedDemo2  {
    static Object lock = new Object();
    static Integer count=0;
    public static void incr(){
        //synchronized (count) {
        //synchronized (lock) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count++;
        //}
    }
    public static void main(String[] args) throws IOException, InterruptedException {
        for(int i=0;i<1000;i++){
            new Thread(()->SynchronizedDemo2.incr()).start();
        }
        Thread.sleep(2000);
        System.out.println("result:"+count);
    }
}
result:691

对于线程安全性,本质上是管理对于数据状态的访问,而且这个状态通常是共享的、可变的。

共享,是指这个数据变量可以被多个线程访问;

可变,指这个变量的值在它的生命周期内是可以改变的。

解决线程并行导致的数据安全性问题

我们可以思考一下,问题的本质在于共享数据存在并发访
问。如果我们能够有一种方法使得线程的并行变成串行,
那是不是就不存在这个问题呢?

一、锁

1.synchronized 的基本认识

Java SE 1.6 对synchronized 进行了各种优化之后,有些情况下它就并不那么重,Java SE 1.6 中为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁轻量级锁

2.基本语法

synchronized 有三种方式来加锁,不同的修饰类型,代表锁的控制粒度。

  1. 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
  2. 静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
  3. 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

3.使用

修改上面的代码,放开注释synchronized (lock),可以达到数据安全的效果。运行结果如下

result:1000

注意:synchronized (count)不安全,应为count是在不断变化的不同的Integer。

4.Mark Word
多线程基本原理和锁的基本原理_第2张图片
多线程基本原理和锁的基本原理_第3张图片
多线程基本原理和锁的基本原理_第4张图片

图片来源https://blog.csdn.net/scdn_cp/article/details/86491792

5.锁是如何保证多线程的互斥特性

synchronized(lock)是基于lock 这个对象的生命周期来控制锁粒度的。对象在Hotspot 虚拟机内存中布局,可以分为三个区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding),线程在获取锁的时候,实际上就是获得一个监视器对象(monitor) ,monitor 可以认为是一个同步对象,所有的Java 对象是天生携带 monitor。在 hotspot 源码的markOop.hpp 文件中

多线程基本原理和锁的基本原理_第5张图片

多个线程访问同步代码块时,相当于去争抢对象监视器修改对象中的锁标识,上面的代码中ObjectMonitor这个对象和线程争抢锁的逻辑有密切的关系。

6.synchronized 锁的升级

在分析 mark word 时,提到了偏向锁、轻量级锁、重量级锁。在分析这几种锁的区别时,我们先来思考一个问题使用锁能够实现数据的安全性,但是会带来性能的下降。不使用锁能够基于线程并行提升程序性能,但是却不能保证线程安全性。这两者之间似乎是没有办法达到既能满足性能也能满足安全性的要求。hotspot 虚拟机的作者经过调查发现,大部分情况下,加锁
的代码不仅仅不存在多线程竞争,而且总是由同一个线程多次获得。所以基于这样一个概率,是的 synchronized 在JDK1.6 之后做了一些优化,为了减少获得锁和释放锁带来的性能开销,引入了偏向锁、轻量级锁的概念。因此大家会发现在 synchronized 中,锁存在四种状态分别是:无锁、偏向锁、轻量级锁、重量级锁; 锁的状态根据竞争激烈的程度从低到高不断升级

LockSupport
讲的挺好的链接。
参考: https://baijiahao.baidu.com/s?id=1666548481761194849&wfr=spider&for=pc

偏向锁、轻量级锁、重量级锁的理解和心得,下一篇再整理,

你可能感兴趣的:(java并发编程)