线程

原子性、可见性和有序性

会有一股神秘感。 提交于 2020-03-06 13:36:24
1. 原子性 操作是原子的,则它不可被分割。从另一个线程的视角来看,它不应该看到这个操作的中间状态,只能看到两种状态:①还没开始执行 ② 已经执行结束。 在多个线程访问临界资源时,如果临界区的代码不是原子的,则一个线程执行到某个中间状态,然后被时钟中断,另一个线程接着执行,就会覆盖数据或访问到不正确的数据。 2. 可见性 由于在存储器层次结构中,更高层的数据是更低层数据的一个子集,该数据副本可能存在存储系统的不同层中。为了提高性能,高层数据写回底层并不是实时的,因此可能造成在某时刻高层数据和底层数据的不一致。 在多核cpu下,由于每个核内部都内置了cache,各个核的cache可能缓存了同一个内存地址的数据,cache之间如果没有某种机制协调,会导致数据不一致(各个线程看到相同数据的值不一样)。 当一个线程修改了共享数据后,我们希望其他线程也能看到这个改变后的结果。如果其他线程看不到这个修改后的数据,继续访问缓存的旧数据,就可能出现问题。 3. 有序性 ① 哪些地方可能产生重排序? 源代码-->字节码/机器码-->存储系统-->CPU 会造成重排序的有:① 编译器 ② 存储系统 ③ CPU的执行单元。 ② 为什么要重排序呢? 为了提高性能。比如更利于cpu的流水、乱序执行;cpu的异步写入store buffer;store buffer的合并写操作等。 ③ 重排序会造成什么影响呢

LockSupport浅析

我是研究僧i 提交于 2020-03-06 12:55:14
最初想有没有必要写这类文章,网上相关的文章很多,有些更为透彻,自己再写一篇不免有重复造轮子的感觉。 但想想写文除了分享知识外也可以帮助自己总结归纳,也稍稍可以提高点自我满足感。 基本的线程阻塞原语,被用于创建锁和其他同步类上。 这个类的作用有点类似于 Semaphore ,通过许可证( permit )来联系使用它的线程。如果许可证可用,调用 park 方法会立即返回并在这个过程中消费这个许可,不然线程会阻塞。调用 unpark 会使许可证可用。(和 Semaphores 有些许区别,许可证不会累加,最多只有一张) 因为有了许可证,所以调用 park 和 unpark 的先后关系就不重要了,这里可以对比一下Object的 wait 和 notify ,如果先调用同一个对象的 notify 再 wait ,那么调用 wait 的线程依旧会被阻塞,依赖方法的调用顺序。在一些场景下, LockSupport 的方法都可以取代 notify 和 wait 。举个简单的例子: final Object lock = new Object(); Thread t1 = new Thread() { public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace()

字节跳动面试,问了我乐观锁和悲观锁的AQS、sync和Lock,这个回答让我拿了offer

爷,独闯天下 提交于 2020-03-06 11:35:00
前言 关于线程安全一提到可能就是加锁,在面试中也是面试官百问不厌的考察点,往往能看出面试者的基本功和是否对线程安全有自己的思考。 那锁本身是怎么去实现的呢?又有哪些加锁的方式呢? 我今天就简单聊一下乐观锁和悲观锁,他们对应的实现 CAS ,Synchronized,ReentrantLock 正文 一个120斤一身黑的小伙子走了进来,看到他微微发福的面容,看来是最近疫情伙食好运动少的结果,他难道就是今天的面试官渣渣丙? 等等难道是他?前几天刷B站看到的不会是他吧!!! 是的我已经开始把面试系列做成视频了,以后会有各种级别的面试,从大学生到阿里P7+的面试,还有阿里,拼多多,美团,字节风格的面试我也都约好人了,就差时间了,大家可以去B站搜: 三太子敖丙 观看 我也不多跟你BB了,我们直接开始好不好,你能跟我聊一下CAS么? CAS(Compare And Swap 比较并且替换)是乐观锁的一种实现方式,是一种轻量级锁,JUC 中很多工具类的实现就是基于 CAS 的。 CAS 是怎么实现线程安全的? 线程在读取数据时不进行加锁,在准备写回数据时,先去查询原值,操作的时候比较原值是否修改,若未被其他线程修改则写回,若已被修改,则重新执行读取流程。 举个栗子:现在一个线程要修改数据库的name,修改前我会先去数据库查name的值,发现name=“ 帅丙 ”,拿到值了,我们准备修改成name

JDK并发包

北城以北 提交于 2020-03-06 10:30:21
1.重入锁(ReentrantLock) 重入锁使用java.util.concurrent.locks.ReentrantLock类来实现,具有与synchronized关键字相似的功能。 1 package com.company; 2 3 import java.util.concurrent.locks.ReentrantLock; 4 5 public class User implements Runnable { 6 private ReentrantLock lock = new ReentrantLock(); 7 static int i = 0; 8 @Override 9 public void run() { 10 lock.lock(); 11 for (int j = 0; j < 10000000; j++) { 12 i++; 13 } 14 System.out.println(i); 15 lock.unlock(); 16 } 17 18 public static void main(String[] args) throws InterruptedException { 19 //注意要使用同一个对象创建线程 20 User u = new User(); 21 Thread t1 = new Thread(u); 22 Thread

ThreadLocal

不羁的心 提交于 2020-03-06 09:05:00
所属包 : java.lang; 描述 : ThreadLocal类提了供线程本地变量。它与普通变量的区别在于,每个使用该变量的线程都会初始化一个完全独立的副本。ThreadLocal 变量通常被private static修饰,用于关联线程上下文。 错误的理解 : ThreadLocal为解决多线程程序的并发问题提供了一种新的思路 ;ThreadLocal的目的是为了解决多线程访问资源时的共享问题; 解决的问题 : ThreadLocal提供了线程本地变量,每个线程都有一个该变量的副本,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度 应用场景 : 当一个变量需要在线程间隔离而在方法或类间共享时,可以使用ThreadLocal ; 案例 :利用ThreadLocal设计一个 懒汉式的单例模式 线程上下文工具类,应用场景:线程上下文传参 public class ThreadContextUtil { private static ThreadLocal < Map < String , Object > > threadContext = new ThreadLocal < Map < String , Object > > ( ) ; private static volatile ThreadContextUtil

linux--mysql(主从复制position)

試著忘記壹切 提交于 2020-03-06 09:03:47
异步复制(主从复制)master节点不会关心slave节点的状态,只需要写自己的数据即可 能不能完成复制看slave节点的io线程和sql线程是否开启 (1)主库开启binlog日志(设置log - bin参数) (2)主从server - id不同 (3)从库服务器能连同主库 mysql的主从配置又叫replication,AB复制,基于binlog二进制日志,主数据库必须开启binlog二进制日志才能进行复制 (1) master将改变记录到二进制日志 ( binary log ) 中(这些记录叫做二进制日志事件,binary log events); (2)从库生成两个线程,一个i / o线程,一个SQL线程,i / o线程去请求主库的binlog,sql线程进行日志回放来复制 (3) slave将master的binary log events拷贝到它的中继日志 ( relay log ) ; (4)slave重做中继日志中的事件,将更改应用到自己的数据上。 mysql的主从复制(异步复制)(基于position)把一个事件拆开来复制,并不是以一个完整的事件为单位来进行复制 一开始两个mysql必须一模一样,否则会报错 master自己做自己的,写在自己的日志里 slave能否同步成功取决于IO线程,和SQL线程回放日志 IO通过联系master拿到master的二进制日志

多线程-读写锁

懵懂的女人 提交于 2020-03-06 09:00:35
1. 读写锁 ReadWriteLock管理一组锁,一个是只读的锁,一个是写锁。 Java并发库中ReetrantReadWriteLock实现了ReadWriteLock接口并添加了可重入的特性 假设你的程序中涉及到对一些共享资源的读和写操作,且写操作没有读操作那么频繁。在没有写操作的时候,两个线程同时读一个资源没有任何问题,所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源,就不应该再有其它线程对该资源进行读或写(译者注:也就是说:读-读能共存,读-写不能共存,写-写不能共存)。这就需要一个读/写锁来解决这个问题。 对于lock的读写锁,可以通过new ReentrantReadWriteLock()获取到一个读写锁。所谓读写锁,便是多线程之间读不互斥,读写互斥。读写锁是一种自旋锁,如果当前没有读者,也没有写者,那么写者可以立刻获得锁,否则它必须自旋在那里,直到没有任何写者或读者。如果当前没有写者,那么读者可以立即获得该读写锁,否则读者必须自旋在那里,直到写者释放该锁 简单来说就是 独占锁(写锁):一次只能被一个线程占有 共享锁(读锁):该锁可以被多个线程占有! 先看一下没有锁的情况 package demo.ReadWriteLock; ​ import java.util.HashMap; import java.util.Map; ​

如何检查线程是否死锁了?

北战南征 提交于 2020-03-06 08:44:47
产生死锁的四个必要条件 (1) 互斥条件:一个资源每次只能被一个进程(线程)使用。 (2) 请求与保持条件:一个进程(线程)因请求资源而阻塞时,对已获得的资源保持不放。 (3) 不剥夺条件 : 此进程(线程)已获得的资源,在末使用完之前,不能强行剥夺。 (4) 循环等待条件 : 多个进程(线程)之间形成一种头尾相接的循环等待资源关系。 可以使用 jstack或者pstack 和 gdb 工具对死锁程序进行分析。 pstack : 功能是打印输出此进程的堆栈信息。可以输出所有线程的调用关系栈 jstack: jstack 是java虚拟机自带的一种堆栈跟踪工具,所以仅适用于java程序,功能跟pstack一样,但是更强大,可以提示哪个地方可能死锁了。 pstack和jstack判断死锁,都需要多执行几次命令,观察每次的输出结果,才能推测是否死锁了。 gdb : 1 运行程序,设置能影响程序运行的参数和环境 ; 2 控制程序在指定的条件下停止运行; 3 当程序停止时,可以检查程序的状态; 4 当程序 crash 时,可以检查 core 文件; 5 可以修改程序的错误,并重新运行程序; 6 可以动态监视程序中变量的值; 7 可以单步执行代码,观察程序的运行状态。 线程死锁分析: 1. 连续多次执行 $pstack <PID> 其中PID是进程号 查看每个线程的函数调用关系的堆栈

python学习第一天

社会主义新天地 提交于 2020-03-06 08:23:55
一、编译型vs解释型 编译型 优点:编译器一般会有预编译的过程对代码进行优化。因为编译只做一次,运行时不需要编译,所以编译型语言的程序执行效率高。可以脱离语言环境独立运行。 缺点:编译之后如果需要修改就需要整个模块重新编译。编译的时候根据对应的运行环境生成机器码,不同的操作系统之间移植就会有问题,需要根据运行的操作系统环境编译不同的可执行文件。 解释型 优点:有良好的平台兼容性,在任何环境中都可以运行,前提是安装了解释器(虚拟机)。灵活,修改代码的时候直接修改就可以,可以快速部署,不用停机维护。 缺点:每次运行的时候都要解释一遍,性能上不如编译型语言。 二、Python的优缺点 先看优点 Python的定位是“优雅”、“明确”、“简单”,所以Python程序看上去总是简单易懂,初学者学Python,不但入门容易,而且将来深入下去,可以编写那些非常非常复杂的程序。 开发效率非常高,Python有非常强大的第三方库,基本上你想通过计算机实现任何功能,Python官方库里都有相应的模块进行支持,直接下载调用后,在基础库的基础上再进行开发,大大降低开发周期,避免重复造轮子。 高级语言————当你用Python语言编写程序的时候,你无需考虑诸如如何管理你的程序使用的内存一类的底层细节 可移植性————由于它的开源本质,Python已经被移植在许多平台上(经过改动使它能够工 作在不同平台上)

Linux内核多线程(一)

孤者浪人 提交于 2020-03-06 08:14:05
Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求)。内核需要多个执行流并行,为了防止可能的阻塞,支持多线程是必要的。内核线程就是内核的分身,一个分身可以处理一件特定事情。内核线程的调度由内核负责,一个内核线程处于阻塞状态时不影响其他的内核线程,因为其是调度的基本单位。这与用户线程是不一样的。因为内核线程只运行在内核态,因此,它只能使用大于PAGE_OFFSET(3G)的地址空间。内核线程和普通的进程间的区别在于内核线程没有独立的地址空间,mm指针被设置为NULL;它只在 内核空间运行,从来不切换到用户空间去;并且和普通进程一样,可以被调度,也可以被抢占。 内核线程(thread)或叫守护进程(daemon),在操作系统中占据相当大的比例,当Linux操作系统启动以后,你可以用”ps -ef”命令查看系统中的进程,这时会发现很多以”d”结尾的进程名,确切说名称显示里面加 "[]"的,这些进程就是内核线程。 创建内核线程最基本的两个接口函数是: kthread_run (threadfn, data, namefmt, ...) 和 kernel_thread (int(* fn)(void *),void * arg,unsigned long flags) 这里我们主要介绍kthread_run,后面会专门分析这两个函数的异同。