并发编程001 --- 初识并发

什么是并发编程

简单的说,所谓的并发编程指的是同一台处理器“同时”处理多个任务。

并发的三种场景

1、分工

合理的拆解不同的任务,并能分配到线程,使多个任务更高效的执行。

2、同步

线程的执行依赖其他线程的执行结果。

3、互斥

多个线程需要抢占共享资源。

并发问题的源头

多线程的出现虽然可以提高应用程序的执行效率,但是不可避免的,也会引入一些问题,这些问题的源头如下:

1、缓存带来的可见性问题

由于CPU的读写速度远远大于内存的读写速度,故CPU利用缓存来缓和CPU和内存读写速度差异带来的问题;

对于多核处理器,每个核都有独立的缓存,这样CPU在计算完数值后,将数值存入缓存,但是写到内存的时机是不确定的,因此会发生缓存可见性问题

示例:如下程序,预期结果为20000,但实际执行结果为10000~20000之间

public class Add {
    private static long count = 0;

    public static long testAdd() throws InterruptedException {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    count ++;
                }
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    count ++;
                }
            }
        });

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

        return count;
    }
}

2、线程切换带来的原子性问题

操作系统支持的最大线程数要远远大于操作系统核数,这是为了缓解CPU的IO速度差异,采用了分时复用机制。

原子性问题的原因是:多线程操作共享变量时,一个线程还未对该变量操作完成,由于分时复用策略,另外一个线程获取了执行权,这个线程获取到的值有可能是错误的。

常见的面试题:long型的变量在32位系统的高并发应用程序中,为什么会有线程安全问题?

原因:long型变量是64位的,32位操作系统对long型变量赋值的操作步骤为:先对高32位赋值,再对低32位赋值;这样,如果中间发生了线程切换,就可能获取到错误的值

3、编译优化带来的有序性问题

有序性问题是有编译器会对我们的指令进行优化重排序,这样不会影响最终的执行结果;但是有时还是会发生一些意想不到的问题;

举例:单例模式下双重检查

public class Singleton {
    private static Singleton instance = null;

    public static Singleton getInstance() {
        if (null == instance) {
            synchronized (Singleton.class) {
                if (null == instance) {
                    instance = new Singleton();
                    return instance;
                }
            }
        }

        return instance;
    }
}

       第一层判空是为了避免加锁导致的性能问题;第二层判空是为了避免创建多个实例;这看起来并没有什么问题,但是由于编译器指令重排,可能会出现问题。

正常创建实例的指令顺序为:分配内存--->内存初始化---->变量指向内存地址

编译优化后指令顺序可能为:分配内存--->变量指向内存地址--->内存初始化

如果线程执行到第二步的时候被剥夺执行权,另一个线程判空的结果为非空,从而直接返回了instance;由于此时instance未初始化,可能会导致空指针异常

     并发带来的三个问题

1、安全性问题

安全性问题的本质就是数据的正确性,为了保证线程安全,应该避免同一时刻不同线程操作共享数据。

2、活跃性问题

饥饿:由于线程优先级低等原因,可能会导致线程一直不能被执行

死锁:线程竞争共享资源,并且互相持有对方的锁,造成多个线程一直等待,造成死锁。

活锁:和死锁相反,活锁是由于“过于谦让”导致的问题;线程访问共享资源,发现另一个线程也需要访问共享资源,于是退出,等待重试;

另外的线程也是如此,因此出现活锁问题。

3、性能问题

锁的过度使用,导致程序串行执行的范围过大,这样就违背了并发编程的优势;

在实际应用中,应尽量减少不必要锁的使用,尽量减少串行

原文地址:https://www.cnblogs.com/sniffs/p/11622728.html

时间: 2024-10-15 14:14:01

并发编程001 --- 初识并发的相关文章

长文慎入-探索Java并发编程与高并发解决方案

所有示例代码,请见/下载于https://github.com/Wasabi1234/concurrency #1 基本概念##1.1 并发同时拥有两个或者多个线程,如果程序在单核处理器上运行多个线程将交替地换入或者换出内存,这些线程是同时"存在"的,每个线程都处于执行过程中的某个状态,如果运行在多核处理器上,此时,程序中的每个线程都将分配到一个处理器核上,因此可以同时运行.##1.2 高并发( High Concurrency) 互联网分布式系统架构设计中必须考虑的因素之一,通常是指

并发编程专题(一)-并发与多线程

1.并发 1.1 并发与并行 首先介绍一下并发与并行,两者虽然只有一字之差,但实际上却有着本质的区别,其概念如下: 并行性(parallel):指在同一时刻,有多条指令在多个处理器上同时执行: 并发性(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果. 在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多个程序同时运行,这在单 CPU 系统中,每一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,只不

Java并发编程与高并发解决方案

第1章 课程准备   1-1 课程导学    1-2 并发编程初体验   1-3 并发与高并发基本概念第2章 并发基础   2-1 CPU多级缓存-缓存一致性   2-2 CPU多级缓存-乱序执行优化   2-3 JAVA内存模型    2-4 并发的优势与风险第3章 项目准备   3-1 案例环境初始化   3-2 案例准备工作    3-3 并发模拟-工具   3-4 并发模拟-代码 第4章 线程安全性   4-1 线程安全性-原子性-atomic-1    4-2 线程安全性-原子性-at

4.java并发编程艺术-java并发编程基础

java从诞生开始就明智的选择了内置对多线程的支持,这使得java语言相比同一时期的其他语言具有明显的优势.线程作为操作系统调度的最小单元,多个线程能够同时执行,这将显著提升程序的性能,在多核环境中表现的更加明显.但是,过多的创建线程和对线程的不当管理也容易造成问题.本章将着重介绍java并发编程的基础知识,从启动一个线程到线程间不同的通信方式,最后通过简单的线程池示例以及应用(简单的Web服务器)来串联本章所介绍的内容. 1.线程简介 1.1 什么是线程 现代操作系统中在运行一个程序时,会为其

【并发编程】高并发相关技术

高并发之扩容思路 垂直扩容(纵向扩展):提高系统部件能力 水平扩容(横向扩展):增加更多系统成员来实现 读操作扩展:memcache.redis.CDN等缓存 写操作扩展:Cassandra.Hbase等 高并发之缓存思路 缓存特征 命中率:命中数/(命中数+未命中数) 最大元素(空间) 清空策略:FIFO, LFU, LRU, 过期时间,随机等 缓存命中率影响因素 业务场景和业务需求 缓存的设计(粒度和策略) 缓存容量和基础设施 缓存分类和应用场景 本地缓存:编程实现(成员变量.局部变量.静态

【并发编程】Java并发编程-看懂AQS的前世今生

在我们可以深入学习AbstractQueuedSynchronizer(AQS)之前,必须具备了volatile.CAS和模板方法设计模式的知识,本文主要想从AQS的产生背景.设计和结构.源代码实现及AQS应用这4个方面来学习下AQS 如果想学习Java工程化.高性能及分布式.深入浅出.微服务.Spring,MyBatis,Netty源码分析的朋友可以加我的Java高级交流:854630135,群里有阿里大牛直播讲解技术,以及Java大型互联网技术的视频免费分享给大家. 1.AQS产生背景 通过

Java并发编程-线程的并发工具类

Fork-Join 什么是分而治之?规模为N的问题,N<阈值,直接解决,N>阈值,将N分解为K个小规模子问题,子问题互相对立,与原问题形式相同,将子问题的解合并得到原问题的解动态规范工作密取workStealing Fork/Join使用的标准范式 常用的并发工具类CountDownLatch作用:是一组线程等待其他的线程完成工作以后在执行,加强版joinawait用来等待,countDown负责计数器的减一CyclicBarrier让一组线程达到某个屏障,被阻塞,一直到组内最后一个线程达到屏

Java并发编程(四):并发容器(转)

解决并发情况下的容器线程安全问题的.给多线程环境准备一个线程安全的容器对象. 线程安全的容器对象: Vector, Hashtable.线程安全容器对象,都是使用 synchronized 方法实现的. concurrent 包中的同步容器,大多数是使用系统底层技术实现的线程安全.类似 native. Java8 中使用 CAS. 1.Map/Set 1.1 ConcurrentHashMap/ConcurrentHashSet 底层哈希实现的同步 Map(Set).效率高,线程安全.使用系统底

Hibernate逍遥游记-第15章处理并发问题-001事务并发问题及隔离机制介绍

1. 2.第一类丢失更新 3.脏读 4.虚读.幻读 5.不可重复读 6.第二类丢失更新 7.数据库的锁机制 8.数据库事务的隔离机制