Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2019-07-31:谈一谈java线程常见的几种锁? #110

Open
MoJieBlog opened this issue Jul 31, 2019 · 3 comments
Open

2019-07-31:谈一谈java线程常见的几种锁? #110

MoJieBlog opened this issue Jul 31, 2019 · 3 comments
Labels

Comments

@MoJieBlog
Copy link
Collaborator

比如:

  • synchronized
  • ReentrantLock
  • ReentrantReadWriteLock
  • AtomicInteger

当然也可以是其他锁

@gabyallen
Copy link

4种Java线程锁(线程同步)

  1. synchronized
    在Java中synchronized关键字被常用于维护数据一致性。
    synchronized机制是给共享资源上锁,只有拿到锁的线程才可以访问共享资源,这样就可以强制使得对共享资源的访问都是顺序的。
    synchronized实现的机理依赖于软件层面上的JVM,因此其性能会随着Java版本的不断升级而提高。
    2.ReentrantLock(ReentrantLock引入两个概念:公平锁与非公平锁。)
    可重入锁,顾名思义,这个锁可以被线程多次重复进入进行获取操作。
    ReentantLock继承接口Lock并实现了接口中定义的方法,除了能完成synchronized所能完成的所有工作外,还提供了诸如可响应中断锁、可轮询锁请求、定时锁等避免多线程死锁的方法。
    Semaphore基本能完成ReentrantLock的所有工作,使用方法也与之类似,通过acquire()与release()方法来获得和释放临界资源。
    此处AtomicInteger是一系列相同类的代表之一,常见的还有AtomicLong、AtomicLong等,他们的实现原理相同,区别在与运算对象类型的不同。

@Moosphan Moosphan added the 线程 label Aug 1, 2019
@Alex-Cin
Copy link

Alex-Cin commented Aug 2, 2019

比如:

  • synchronized
  • ReentrantLock
  • ReentrantReadWriteLock
  • AtomicInteger

当然也可以是其他锁

还有一个 unsafe 类;
cas 是乐观锁的一种实现形式, 在多核 CPU 机器上会有比较好的性能;

@MicroKibaco
Copy link

MicroKibaco commented Aug 3, 2019

首先,Java为什么会出现锁的概念,锁是用来干什么的?这里我给大家解释一下, 是保证 线程同步与安全的.

synchronized

synchronized 有什么用?大家看一下这段代码

public class Synchronized2Demo implements TestDemo {

    private int x = 0;

    private void count() {
        x++;
    }

    @Override
    public void runTest() {
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 1_000_000; i++) {
                    count();
                }
                System.out.println("final x from 1: " + x);
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 1_000_000; i++) {
                    count();
                }
                System.out.println("final x from 2: " + x);
            }
        }.start();
    }
}

synchronized的互斥访问

  当添加 synchronized 的时候,多个线程不能同时访问同一个方法,synchronized 的意义是保证方法或代码块的内部资源的互斥访问,同一时间用用一个monitor监视代码最多一个代码访问,怎么解释这句话呢?

运行上段代码,我们不难发现:

final x from 1: 1000000
final x from 2: 1314570

Fuck! 竟然结果不是200 0000,而且每次结果都不一样,这是为什么呢?

  因为:x++ 不是原子操作, 不是一行代码执行的,他是可以分解的代码,可以分离成这样

int temp = i;
i + 1 = temp;

你看,这是两段代码吧,加 synchronized 修饰 就可以完美解决这个问题.

synchronized 的本质:

  1. 保证方法的内部或内部代码块内部资源(数据)的互斥访问,即同一时间,由同一个 Monitor 监视代码,最多只能一个线程在访问

  2. 保证线程之间对监视资源的数据同步.即,任何线程获取 Monitor 后第一时间,会将共享数据的缓存复制到自己的缓存中;任何线程在释放 Monitor 的第一时间,会将缓存的数据复制到共享内存中

Lock / ReentrantReadWriteLock

  同样是加锁机制 ,使用方式更加灵活,同时也更加麻烦一些

 
Lock lock = new ReentrantLock();
{
   // -------伪代码-------
}
lock.lock(); try {
x++;
} finally {
    lock.unlock();
}

  finally 作用是保证在方法提前结束或出现Exception的时候,依然能正常释放锁.

  一般不会直接使用 Lock,而是直接使用更加复杂的锁,如: ReadWriteLock:

public class ReadWriteLockDemo implements TestDemo {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
    private ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();
    private int x = 0;

    @Override
    public void runTest() {
        print(15);
        count();
    }


    public void count() {
        writeLock.lock();

        try {
            x++;
        } finally {
            writeLock.unlock();
        }


    }

    public void print(int time) {
        readLock.lock();
        try {
            for (int i = 0; i < time; i++) {
                System.out.println(x + "");
            }

            System.out.println("over");
        } finally {
            readLock.unlock();
        }

    }
}
volatile
  1. 保证加了 volatile 关键字的字段的操作具有同步性,以及对 long 和 double 操作的原子性.因此 volatile 可以看做简化版的 synchronized
  2. volatile 只对基本数据类型(byte ,short ,long ,char,int ,float ,double,boolean)的赋值操作和对象引用赋值有效,你修改 User.name 是不能保证同步的
  3. volatile 依然解决 不了 ++ 的原子性问题
java.util.concurrent.atomic
  1. AtomicInteger , AtomicInteger等类,作用和 volatile 基本一致,可以看做通用版本的 volatile
  
AtomicInteger atomicInteger = new AtomicInteger(0);

{
// -------伪代码--------
}

 atomicInteger.getAndIncrement();

 解决了 ++ 原子性 问题,非常好用.

悲观锁

不管数据怎么样我都会加锁,这个就是悲观锁

乐观锁

我认为,别人不会读数据我倾向于别人不会动数据,所以不对代码块加锁,这就是乐观锁

线程安全问题的本质:

  在多线程访问共同资源时,在某一个线程对资源进行写操作中途(写入已经开始了,但还没结束),其他线程对这个写了一半资源进行读操作,或基于这个写了一半的资源进行了写操作,导致出现数据错误

锁机制的本质:

  通过共享资源进行访问限制,让同一时间只有一个线程可以访问资源,保证数据的准确性

总结:

  无论是线程安全问题,还是针对线程衍生的锁机制,它的核心在于共享资源,而不是某个方法或几行代码


当然还有死锁问题,这里我就不一一和大家阐述了.

最后大家觉的我的内容对大家有帮助,欢迎通过以下渠道阅读我的文章或github star +1

github CSDN 掘金
MicroKibaco Kibaco 小木箱

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants