网易Java研发面试官眼中的Java并发——安全性、活跃性、性能

一. 安全性问题

  1. 线程安全的本质是正确性,而正确性的含义是程序按照预期执行
  2. 理论上线程安全的程序,应该要避免出现可见性问题(CPU缓存)、原子性问题(线程切换)和有序性问题(编译优化)
  3. 需要分析是否存在线程安全问题的场景:存在共享数据且数据会发生变化,即有多个线程会同时读写同一个数据
  4. 针对该理论的解决方案:不共享数据,采用线程本地存储(Thread Local Storage,TLS);不变模式

Ⅰ. 数据竞争

数据竞争(Data Race):多个线程同时访问同一数据,并且至少有一个线程会写这个数据

1. add

private static final int MAX_COUNT = 1_000_000;
private long count = 0;
// 非线程安全
public void add() {
    int index = 0;
    while (++index < MAX_COUNT) {
        count += 1;
    }
}

2. add + synchronized

private static final int MAX_COUNT = 1_000_000;
private long count = 0;
public synchronized long getCount() {
    return count;
}
public synchronized void setCount(long count) {
    this.count = count;
}
// 非线程安全
public void add() {
    int index = 0;
    while (++index < MAX_COUNT) {
        setCount(getCount() + 1);
    }
}
  • 假设count=0,当两个线程同时执行getCount(),都会返回0
  • 两个线程执行getCount()+1,结果都是1,最终写入内存是1,不符合预期,这种情况为竟态条件

Ⅱ. 竟态条件

  1. 竟态条件(Race Condition):程序的执行结果依赖于线程执行的顺序
  2. 在并发环境里,线程的执行顺序是不确定的
    • 如果程序存在竟态条件问题,那么意味着程序的执行结果是不确定的

1. 转账

public class Account {
    private int balance;
    // 非线程安全,存在竟态条件,可能会超额转出
    public void transfer(Account target, int amt) {
        if (balance > amt) {
            balance -= amt;
            target.balance += amt;
        }
    }
}

Ⅲ. 解决方案

面对数据竞争和竟态条件问题,可以通过互斥的方案来实现线程安全,互斥的方案可以统一归为锁

二. 活跃性问题

活跃性问题:某个操作无法执行下去,包括三种情况:死锁、活锁、饥饿

Ⅰ. 死锁

  1. 发生死锁后线程会相互等待,表现为线程永久阻塞
  2. 解决死锁问题的方法是规避死锁(破坏发生死锁的条件之一)
    • 互斥:不可破坏,锁定目的就是为了互斥
    • 占有且等待:一次性申请所有需要的资源
    • 不可抢占:当线程持有资源A,并尝试持有资源B时失败,线程主动释放资源A
    • 循环等待:将资源编号排序,线程申请资源时按递增(或递减)的顺序申请

Ⅱ. 活锁

  • 活锁:线程并没有发生阻塞,但由于相互谦让,而导致执行不下去
  • 解决方案:在谦让时,尝试等待一个随机时间(分布式一致算法Raft也有采用)

Ⅲ. 饥饿

  1. 饥饿:线程因无法访问所需资源而无法执行下去

    • 线程的优先级是不相同的,在CPU繁忙的情况下,优先级低的线程得到执行的机会很少,可能发生线程饥饿
    • 持有锁的线程,如果执行的时间过长(持有的资源不释放),也有可能导致饥饿问题
  2. 解决方案
    • 保证资源充足
    • 公平地分配资源(公平锁) – 比较可行
    • 避免持有锁的线程长时间执行

三. 性能问题

  1. 锁的过度使用可能会导致串行化的范围过大,这会影响多线程优势的发挥(并发程序的目的就是为了提升性能)
  2. 尽量减少串行,假设串行百分比为5%,那么多核多线程相对于单核单线程的提升公式(Amdahl定律)
    S=1/((1-p)+p/n),n为CPU核数,p为并行百分比,(1-p)为串行百分比
  • 假如p=95%,n无穷大,加速比S的极限为20,即无论采用什么技术,最高只能提高20倍的性能

Ⅰ. 解决方案

  1. 无锁算法和数据结构

    • 线程本地存储(Thread Local Storage,TLS)
    • 写入时复制(Copy-on-write)
    • 乐观锁
    • JUC中的原子类
    • Disruptor(无锁的内存队列)
  2. 减少锁持有的时间,互斥锁的本质是将并行的程序串行化,要增加并行度,一定要减少持有锁的时间
    • 使用细粒度锁,例如JUC中的ConcurrentHashMap(分段锁)
    • 使用读写锁,即读是无锁的,只有写才会互斥的

Ⅱ. 性能指标

  1. 吞吐量:在单位时间内能处理的请求数量,吞吐量越高,说明性能越好
  2. 延迟:从发出请求到收到响应的时间,延迟越小,说明性能越好
  3. 并发量:能同时处理的请求数量,一般来说随着并发量的增加,延迟也会增加,所以延迟一般是基于并发量来说的

写在最后

原文地址:https://www.cnblogs.com/Java-no-1/p/11141000.html

时间: 2024-10-19 14:12:20

网易Java研发面试官眼中的Java并发——安全性、活跃性、性能的相关文章

【JAVA秒会技术之秒杀面试官】秒杀Java面试官——集合篇(一)

[JAVA秒会技术之秒杀面试官]秒杀Java面试官--集合篇(一) [JAVA秒会技术之秒杀面试官]JavaEE常见面试题(三) http://blog.csdn.net/qq296398300/article/category/6876287

如何写出面试官欣赏的Java单例

单例模式是一种常用的软件设计模式.在它的核心结构中只包含一个被称为单例的特殊类.通过单例模式可以保证系统中一个类只有一个实例. 今天我们不谈单例模式的用途,只说一说如果在面试的时候面试官让你敲一段代码实现单例模式的情况下怎样写出让面试官眼前一亮的单例代码.因为笔者学的是Java,所以接下来的实例将用Java语言编写. 说到单例模式,第一个想到的是该类中有一个初始化为null的自身引用,且被private修饰符修饰,其它类不得直接访问.除此之外,单例模式的类还需要有private的构造方法,这一点

阿里Java面试官分享初级Java程序员通过面试的技巧

本来想分享毕业生和初级程序员如何进大公司的经验,但后来一想,人各有志,有程序员或许想进成长型或创业型公司或其它类型的公司,所以就干脆来分享些提升技能和通过面试的技巧,技巧我讲,公司你选,两厢便利. 毕业生和初级程序员(一般是工作经验3年以下)大多处于事业的青黄不接的阶段,在找工作时往往会遇到缺乏实际项目经验的瓶颈,作为技术面试官,我也经常在面试过程中感受到这些候选人缺乏实际经验的缺陷.不过本人之前做过java兼职培训老师,也总结了些这批人群提升实际技能和面试技能的技巧,最近也老有人来问我这个,所

【架构师技巧分享】程序员面试美团:面试官突然问Java “锁”你应该怎么回答?

Java提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高的效率.本文旨在对锁相关源码(本文中的源码来自JDK 8).使用场景进行举例,为读者介绍主流锁的知识点,以及不同的锁的适用场景. Java中往往是按照是否含有某一特性来定义锁,我们通过特性将锁进行分组归类,再使用对比的方式进行介绍,帮助大家更快捷的理解相关知识.下面给出本文内容的总体分类目录: 1.乐观锁 VS 悲观锁 乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度.在Java和数据库中都有此概念对应

哪些问题是面试官经常问Java工程师的问题 ? --- 转自quora

Which are the frequently asked interview questions for Java Engineers ? Vivek Vermani, www.buggybread.com | Programme... (more) 265 upvotes by Ridox Liu, Shivani Sahni Vermani, Viet Thang, (more) Java的基础知识   For a Core Java Developer , Questions arou

JAVA开发之大型互联网企业高并发架构Tomcat服务器性能优化视频教程

课程目标熟练掌握高并发架构Tomcat服务器性能优化. 适用人群对计算机,java开发人员,Java架构师,运维感兴趣的朋友! 课程简介Tomcat是Apache软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,由Apache.Sun和其他一些公司及个人共同开发而成.Tomcat服务器是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP程序的首选. Tomc

字节跳动Java研发面试99题(含答案):JVM+Spring+MySQL+线程池+锁

JVM的内存结构 根据 JVM 规范,JVM 内存共分为虚拟机栈.堆.方法区.程序计数器.本地方法栈五个部分. 1. Java虚拟机栈:线程私有:每个方法在执行的时候会创建一个栈帧,存储了局部变量表,操作数栈,动态连接,方法返回地址等:每个方法从调用到执行完毕,对应一个栈帧在虚拟机栈中的入栈和出栈. 2. 堆:线程共享:被所有线程共享的一块内存区域,在虚拟机启动时创建,用于存放对象实例. 3. 方法区:线程共享:被所有线程共享的一块内存区域:用于存储已被虚拟机加载的类信息,常量,静态变量等. 4

面试官问我“Java中的锁有哪些?以及区别”,我跪了

在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类.介绍的内容如下: 公平锁/非公平锁 可重入锁 独享锁/共享锁 互斥锁/读写锁 乐观锁/悲观锁 分段锁 偏向锁/轻量级锁/重量级锁 自旋锁 上面是很多锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计,下面总结的内容是对每个锁的名词进行一定的解释. 公平锁/非公平锁 公平锁是指多个线程按照申请锁的顺序来获取锁.非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线

面试官,不要再问我“Java GC垃圾回收机制”了

Java GC垃圾回收几乎是面试必问的JVM问题之一,本篇文章带领大家了解Java GC的底层原理,图文并茂,突破学习及面试瓶颈. 楔子-JVM内存结构补充 在上篇<JVM之内存结构详解>中有些内容我们没有讲,本篇结合垃圾回收机制来一起学习.还记得JVM中堆的结构图吗? 图中展示了堆中三个区域:Eden.From Survivor.To Survivor.从图中可以也可以看到它们的大小比例,准确来说是:8:1:1.为什么要这样设计呢,本篇文章后续会给出解答,还是根据垃圾回收的具体情况来设计的.