* 1.如果不加锁,则会出现重复或遗漏情况.
*执行1:单线程情况下怎么都不会有重复
* 执行2:默认多线程情况下会有重复
//执行1:如果单线程不会重复
//执行2:普通多线程情况下会有重复,会发现执行3个线程的最后3*100不等于300
* 内置锁就是当前类的实例(对象) * 内置锁是当前的Class字节码对象 * 也就是锁对象,就是所有持有该對象的线程都可以进入,等同于2.1放在普通方法上 //如果锁基本类型,需要转换为对象类型 //锁字节码,字节码全局唯一,等同有2.2 .修饰静态方法,
注意:本文中的大部分是阅读 《程序员的自我修养》 作 者:俞甲子石凡,潘爱民 的读书笔记推荐大家看看这本书。
操作系统将把程序依赖的目标文件全部加载到内存如果依赖关系满足,则系统开始进行链接链接与静态链接相似,即进行符号解析、地址重定位
例如程序program1和program2都依赖于)讨论DLL的编程問题。对于文中的错误和纰漏也热诚欢迎您指正。
创建一个静态库是相当简单的通常使用 ar 程序把一些目标文件(.o)组合在一起,
成为┅个单独的库然后运行 ranlib,以给库加入一些索引信息
ldd 用来显示执行文件需要哪些
装载管理器在哪里找到了需要的
共享库的一个非常重要嘚,也是非常难的概念是 soname——简写共享目标名(short for shared object name)这是一个为共享库(.so)文件而内嵌在控制数据中的名字。如前面提到的每一个程序嘟有一个需要使用的库的清单。这个清单的内容是一系列库的 soname如同 ldd 显示的那样,共享库装载器必须找到这个清单
soname 的关键功能是它提供叻兼容性的标准。当要升级系统中的一个库时并且新库的 soname 和老的库的 soname 一样,用旧库连接生成的程序使用新的库依然能正常运行。这个特性使得在 Linux 下升级使用
的程序和定位错误变得十分容易。
在 Linux 中应用程序通过使用 soname,来指定所希望库的版本库作者也可以通过保留或鍺改变 soname 来声明,哪些版本是相互兼容的这使得程序员摆脱了
当程序被调用的时候,Linux 共享库装载器(也被称为动态连接器)也自动被调用它的作用是保证程序所需要的所有适当版本的库都被调入内存。共享库装载器名字是 ld.so 或者是 ld-linux.so这取决于 Linux libc 的版本,它必须使用一点外部交互才能完成自己的工作。然而它接受在环境变量和配置文件中的配置信息
装载器把它作为搜索路径。为了改变这个设置必须以 root 身份運行 ldconfig 工具。这将更新 /etc/ls.so.cache 文件这个文件其实是装载器内部使用的文件之一。
可以使用许多环境变量控制共享库装载器的操作(表1-4+)
另外一個强大的库函数是 dlopen()。该函数将打开一个新库并把它装入内存。该函数主要用来加载库中的符号这些符号在编译的时候是不知道的。比洳 Apache Web 服务器利用这个函数在运行过程中加载模块这为它提供了额外的能力。一个配置文件控制了加载模块的过程这种机制使得在系统中添加或者删除一个模块时,都不需要重新编译了
可以在自己的程序中使用 dlopen()。dlopen() 在 dlfcn.h 中定义并在 dl 库中实现。它需要两个参数:一个文件名和┅个标志文件名可以是我们学习过的库中的 soname。标志指明是否立刻计算库的依赖性如果设置为 RTLD_NOW 的话,则立刻计算;如果设置的是 RTLD_LAZY则在需要的时候才计算。另外可以指定 RTLD_GLOBAL,它使得那些在以后才加载的库可以获得其中的符号
当库被装入后,可以把 dlopen() 返回的句柄作为给 dlsym() 的第┅个参数以获得符号在库中的地址。使用这个地址就可以获得库中特定函数的指针,并且调用装载库中的相应函数
重入一般可以理解为一个函数在哃时多次调用例如操作系统在进程调度过程中,或者单片机、处理器等的中断的时候会发生重入的现象
可重入的函数必须满足以下三個条件:
(1)可以在执行的过程中可以被打断;
(2)被打断之后,在该函数一次调用执行完之前可以再次被调用(或进入,reentered)
(3)再次調用执行完之后,被打断的上次调用可以继续恢复执行并正确执行。
可重入函数可以在任意时刻被中断稍后再继续运行,不会丢失数據不可重入(non-reentrant)函数不能由超过一个任务所共享,除非能确保函数的互斥(或者使用信号量或者在代码的关键部分禁用中断)。
通常以下几种情况会受到可重入性的制约:
(1)信号处理程序A内外都调用了同一个不可重入函数B;B在执行期间被信号打断,进入A (A中调用了B),完倳之后返回B被中断点继续执行这时B函数的环境可能改变,其结果就不可预料了
众所周知,在进程中断期间系统会保存和恢复进程的仩下文,然而恢复的上下文仅限于返回地址cpu寄存器等之类的少量上下文,而函数内部使用的诸如全局或静态变量和全局变量的区别buffer等並不在保护之列,所以如果这些值在函数被中断期间发生了改变那么当函数回到断点继续执行时,其结果就不可预料了打个比方,比洳malloc将如一个进程此时正在执行malloc分配堆空间,此时程序捕捉到信号发生中断执行信号处理程序中恰好也有一个malloc,这样就会对进程的环境慥成破坏因为malloc通常为它所分配的存储区维护一个链接表,插入执行信号处理函数时进程可能正在对这张表进行操作,而信号处理函数嘚调用刚好覆盖了进程的操作造成错误。
(2)多线程共享进程内部的资源如果两个线程A,B调用同一个不可重入函数FA线程进入F后,线程调度切换到B,B也执行了F那么当再次切换到线程A时,其调用F的结果也是不可预料的
常见的不可重入函数有:
可重入的定义源于单线程环境。在单线程环境下一段代码在执行中可能被硬件中断,并转而调用中断服务程序(ISR)在本次调用中断处理函数之前,有可能中斷处理函数已经在执行因此,任何中断处理函数都应该是可重入的
线程安全的概念则源自于多线程环境。可见他们的起源是不一样嘚。那么他们没有什么必然关系呢。可总结如下:
(1)一个线程安全的函数不一定是可重入的;
(2)一个可重入的函数缺也不一定是线程安全的!
(3)一个不可重入的函数一定不是线程安全的这是收到多线程并发环境的限制,但可重入本身与线程安全无必然联系
在单線程进程中,只存在一个控制流因此,这些进程所执行的代码无需重入或是线程安全的在多线程程序中,相同的功能和资源可以通过哆个控制流并发访问
要保护资源的完整性,编写的多线程程序代码必须能重入并是线程安全的不可重入对多线程环境的危害是很大的,甚至会造成系统崩溃