JAVA多线程与锁机制

1 关于Synchronized和lock

  • synchronized是Java的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码

    • JDK1.5以后引入了自旋锁、锁粗化、轻量级锁,偏向锁来有优化关键字的性能。
    • 一个线程访问一个被synchronized修饰的代码块,会自动获取对应的一个锁,并在执行该代码块时,其他线程想访问这个代码块,会一直处于等待状态,自有等该线程释放锁后,其他线程进行资源竞争,竞争获取到锁的线程才能访问该代码块。
  • synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

  • Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

  • 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

  • Lock锁适合大量同步代码的同步问题,synchronized锁适合代码少量的同步问题。总体上来说,在资源竞争不激烈的情形下,性能稍微比synchronized差点。但是资源竞争非常激烈的时候,synchronized的性能会下降很多,而ReentrantLock的性能表现仍然比较稳定。

2 Java 实现线程安全的三种方式

  • 同步代码块
synchronized(obj)
{
//需要被同步的代码块
}

obj 称为同步监视器,也就是锁,原理是:当线程开始执行同步代码块前,必须先获得对同步代码块的锁定。并且任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对该同步监视器的锁定。

  • 同步方法
public synchronized void testThread()
{
//需要被同步的代码块
}

不需要再指定同步监视器,这个同步方法(非static方法)无需显式地指定同步监视器,同步方法的同步监视器就是this,也就是调用该方法的对象。

  • 同步锁,Lock锁机制, 通过创建Lock对象,采用lock()加锁,unlock()解锁,来保护指定的代码块。其中,为了确保能够在必要的时候释放锁,代码中使用finally来确保锁的释放,来防止死锁!

3 Synchronized

  • static synchronized则是该类的所有实例公用一个监视块,静态同步方法和非静态同步方法持有的是不同的锁,前者是类锁,后者是对象锁

  • 1.方法声明时使用:线程获得的是成员锁

    2.对某一代码块使用:线程获得的是成员锁

    3.synchronized后面括号里是一对象,此时,线程获得的是对象锁

    4.synchronized后面括号里是类,此时,线程获得的是对象锁

4 乐观锁、悲观锁

  • 乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。

    • 可以使用版本号机制(+递归)和CAS算法实现。如果发现数据已经被更改(通过版本号控制),则不更新数据,再次去重复 所需操作直到道没有冲突(使用递归算法)。
  • 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。

4 公平锁、非公平锁

公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁。

  • 优点:所有的线程都能得到资源,不会饿死在队列中。
  • 缺点:吞吐量会下降很多,队列里面除了第一个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的开销会很大

非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。

  • 优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会高点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。
  • 缺点:你们可能也发现了,这样可能导致队列中间的线程一直获取不到锁或者长时间获取不到锁,导致饿死

5 JAVA线程池

  • 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。线程池使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务。

  • 提高系统响应速度,降低系统资源消耗

  • 参数:

    • 一个任务被提交到线程池以后,首先会找有没有空闲存活线程(>=corePoolSize的情况)

      • 如果有则直接将任务交给这个空闲线程来执行,
      • 如果没有则会缓存到工作队列中,如果工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。
    • corePoolSize(核心线程数):

      • 核心线程会一直存活,即使没有任务需要执行
      • 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
    • maxPoolSize(最大线程数):

      • 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常
    • keepAliveTime(线程存活保持时间):

      • 当线程池中线程数大于核心线程数时,线程的空闲时间如果超过线程存活时间,那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数。
    • workQueue (工作队列):

      新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:

    • handler(线程饱和策略):

      • 当线程池和队列都满了,再加入线程会执行此策略。

6 多线程中的i++线程安全吗

不安全。i++不是原子性操作。i++分为读取i值,对i值加一,再赋值给i++,执行期中任何一步都是有可能被其他线程抢占的。

JAVA多线程与锁机制的更多相关文章

  1. java多线程(三)——锁机制synchronized(同步语句块)

    用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法之行一个长时间的任务,那么B线程必须等待比较长的时间,在这样的情况下可以使用synchronized同步语句快来解 ...

  2. java多线程(二)——锁机制synchronized(同步方法)

    synchronized Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码.当两个并发线程访问同一个对象object中 ...

  3. JAVA中关于锁机制

    本文转自 http://blog.csdn.net/yangzhijun_cau/article/details/6432216 一段synchronized的代码被一个线程执行之前,他要先拿到执行这 ...

  4. Java多线程的同步机制(synchronized)

    一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个 ...

  5. Java 多线程:锁(三)

    Java 多线程:锁(三) 作者:Grey 原文地址: 博客园:Java 多线程:锁(三) CSDN:Java 多线程:锁(三) StampedLock StampedLock其实是对读写锁的一种改进 ...

  6. 深入浅出Java并发包—锁机制(三)

    接上文<深入浅出Java并发包—锁机制(二)>  由锁衍生的下一个对象是条件变量,这个对象的存在很大程度上是为了解决Object.wait/notify/notifyAll难以使用的问题. ...

  7. 深入浅出Java并发包—锁机制(二)

    接上文<深入浅出Java并发包—锁机制(一)  >  2.Sync.FairSync.TryAcquire(公平锁) 我们直接来看代码 protected final boolean tr ...

  8. 深入浅出Java并发包—锁机制(一)

    前面我们看到了Lock和synchronized都能正常的保证数据的一致性(上文例子中执行的结果都是20000000),也看到了Lock的优势,那究竟他们是什么原理来保障的呢?今天我们就来探讨下Jav ...

  9. SDL 开发实战(七): SDL 多线程与锁机制

    为什么要用多线程?在音视频领域主要是实现音视频同步.实现了音视频同步,我们的播放器就基本上合格了. 这里我们将讲解一下SDL的多线程与锁机制. 多线程的好处主要是能使程序更加充分利用硬件(主要是CPU ...

随机推荐

  1. Java基础(第二期)

    数据类型扩展以及面试题讲解 整数拓展:进制 int i=10; int i2=010; //八进制0 int i3=0x10; //十六进制0x 0~9 A~F 16 相关进制转换自行学习,用的不多 ...

  2. postcss 运用及原理

    postcss 入坑指南 目标: 掌握 postcss 的使用 自定义 postcss 插件 掌握 stylelint 的使用 自定义 stylelint rule 扩展 css parser 解释器 ...

  3. SSH服务连接

    SSH基本概述 SSH是一个安全协议,在进行数据传输时,会对数据包进行加密处理,加密后在进行数据传输.确保了数据传输安全. SSH服务 ssh: secure shell, protocol, 22/ ...

  4. word2vector论文笔记

    背景 很多当前的NLP系统和技术都把单词像ont-hot一样当做原子性的一个概念去对待,单纯就是一个索引,无法表示词之间的相似性.原因就是往往一个简单的.鲁棒的.可观测的模型在海量数据集上的学习效果要 ...

  5. 多线程之ThreadLocal类

    深入研究java.lang.ThreadLocal类 0.前言 ThreadLocal(线程变量副本)Synchronized实现内存共享,ThreadLocal为每个线程维护一个本地变量.采用空间换 ...

  6. Redis 主从复制(Replication)

    为了保证服务的可用性,现代数据库都提供了复制功能,同时在多个进程中维护一致的数据状态. Redis 支持一主多从的复制架构,该功能被简化成了一条 SLAVEOF 命令,下面通过条命令来解析 Redis ...

  7. codeforces 1036B - Diagonal Walking v.2【思维+构造】

    题目:戳这里 题意:起点(0,0),终点(n,m),走k步,可以走8个方向,问能不能走到,能走到的话最多能走多少个斜步. 解题思路:起点是固定的,我们主要分析终点.题目要求走最多的斜步,斜步很明显有一 ...

  8. 一个操作系统的实现sudo mount -o loop pm.img /mnt/floppy mount point /mnt/floppy does not exist losetup device is busy

    部分参考:https://blog.csdn.net/u012323667/article/details/79266623 一. sudo mount -o loop pm.img /mnt/flo ...

  9. 基于OpenCV全景拼接(Python)SIFT/SURF

    一.实验内容: 利用sift算法,实现全景拼接算法,将给定的两幅图片拼接为一幅. 二.实验环境: 主机配置: CPU :intel core i5-7300 2.50GHZ RAM :8.0GB 运行 ...

  10. 求第n行杨辉三角(n很大,取模

    1 #include <iostream> 2 #include <cstdio> 3 4 using namespace std; 5 typedef long long l ...