Java编程多个linux线程编程如何访问同一个共享资源

并发不一定要依赖多linux线程编程(洳PHP中很常见的多进程并发)

各个linux线程编程既可以共享进程资源(内存地址、文件I/O等),又可以独立调度(linux线程编程是CPU调度的基本单位)

每个已经执行start()且还未结束的.lang.Thread类的实例就代表了一个linux线程编程。Thread的所有关键方法都是声明为Native的在Java API中,一个Native方法往往意味着这个方法没有使用或无法使用平台无关的手段来实现(当然也可能是为了执行效率而使用Native方法不过,通常最高效的手段也就是平台相关的手段)

实現linux线程编程主要有3种方式:

3)使用用户linux线程编程加轻量级进程混合实现

内核linux线程编程就是直接由操作系统内核(Kernel)支持的linux线程编程,这种linux線程编程由内核来完成linux线程编程切换内核通过操纵调度器(Scheduler)对linux线程编程进行调度,并负责将linux线程编程的任务映射到各个处理器上每個内核linux线程编程可以视为内核的一个分身,这样操作系统就有能力同时处理多件事情支持多linux线程编程的内核就叫做多linux线程编程内核(Multi-Threads Kernel)。

程序一般不会直接去使用内核linux线程编程而是去使用内核linux线程编程的一种高级接口--轻量级进程(Light Weight Process,LWP)轻量级进程就是我们通常意义上所讲的linux线程编程,由于每个轻量级进程都由一个内核linux线程编程支持因此只有先支持内核linux线程编程,才能有轻量级进程这种轻量级进程與内核linux线程编程之间1:1的关系称为一对一的linux线程编程模型。

轻量级进程与内核linux线程编程之间1:1的关系:

由于内核linux线程编程的支持每个轻量级進程都成为一个独立的调度单元,即使有一个轻量级进程在系统调用中阻塞了也不会影响整个进程继续工作,但是轻量级进程具有它的局限性:首先由于是基于内核linux线程编程实现的,所以各种linux线程编程操作如创建、析构及同步,都需要进行系统调用而系统调用的代價相对较高,需要在用户态(User Mode)和内核态(Kernel Mode)中来回切换其次,每个轻量级进程都需要有一个内核linux线程编程的支持因此轻量级进程要消耗一定的内核资源(如内核linux线程编程的栈空间),因此一个系统支持轻量级进程的数量是有限的

从广义上来讲,一个linux线程编程只要不昰内核linux线程编程就可以认为是用户linux线程编程(User Thread,UT)轻量级进程也属于用户linux线程编程,但轻量级进程的实现始终是建立在内核之上的許多操作都要进行系统调用,效率会受到限制

而狭义上的用户linux线程编程指的是完全建立在用户空间的linux线程编程库上,系统内核不能感知linux線程编程存在的实现用户linux线程编程的建立、同步、销毁和调度完全在用户态中完成,不需要内核的帮助如果程序实现得当,这种linux线程編程不需要切换到内核态因此操作可以是非常快速且低消耗的,也可以支持规模更大的linux线程编程数量部分高性能数据库中的多linux线程编程就是由用户linux线程编程实现的。这种进程与用户linux线程编程之间1:N的关系称为一对多的linux线程编程模型:

使用用户linux线程编程的优势在于不需要系統内核支援劣势也在于没有系统内核的支援,所有的linux线程编程操作都需要用户程序自己处理linux线程编程的创建、切换和调度都是需要考慮的问题,而且由于操作系统只把处理器资源分配到进程那诸如“阻塞如何处理”,“多处理器系统中如何将linux线程编程映射到其它处理器上”这类问题解决起来将会异常困难甚至不可能完成。除了以前在不支持多linux线程编程的操作系统中(如DOS)的多linux线程编程程序与少数有特殊需求的程序外现在使用用户linux线程编程的程序越来越少了,Java、Ruby等语言都曾经使用过用户linux线程编程最终又都放弃使用它。

使用用户linux线程编程加轻量级进程混合实现

在这种混合实现下即存在用户linux线程编程,也存在轻量级进程用户linux线程编程还是完全建立在用户空间中,洇此用户linux线程编程的创建、切换、析构等操作依然廉价并且可以支持大规模的用户linux线程编程并发。而操作系统提供支持的轻量级进程则莋为用户linux线程编程和内核linux线程编程之间的桥梁这样可以使用内核提供的linux线程编程调度功能及处理器映射,并且用户linux线程编程的系统调用偠通过轻量级进程来完成大大降低了整个进程被完全阻塞的风险。在这种混合模式中用户linux线程编程与轻量级进程的数量比是不定的,即为N:M的关系:

许多UNIX系列的操作系统如Salaris、HP-UX等都提供了N:M的linux线程编程模型实现。

Javalinux线程编程在JDK1.2之前是基于称为“绿色linux线程编程”(Green Threads)的用户linux线程编程实现的,而在JDK1.2中linux线程编程模型替换为基于操作系统原生linux线程编程模型来实现。因此在目前的JDK版本中,操作系统支持怎样的linux线程編程模型在很大程度上决定了Java虚拟机的linux线程编程是怎样映射的,这点在不同的平台上没有办法达成一致虚拟机规范中也并未限定Javalinux线程編程需要使用哪种linux线程编程模型来实现。linux线程编程模型只对linux线程编程的并发规模和操作成本产生影响对Java程序的编码和运行过程来说,这些差异都是透明的

对于Sun JDK来说,它的Windows版与Linux版都是使用一对一的linux线程编程模型实现的一条Javalinux线程编程就映射到一条轻量级进程之中,因为Windows和Linux系统提供的linux线程编程模型就是一对一的

linux线程编程调度是指系统为linux线程编程分配处理器使用权的过程,主要调度方式有两种:

如果使用协哃式调度的多linux线程编程系统linux线程编程的执行时间由linux线程编程本身来控制,linux线程编程把自己的工作执行完了之后要主动通知系统切换到叧外一个linux线程编程上。协调式linux线程编程调度:

2)没有linux线程编程同步问题由于linux线程编程要把自己的事情干完后才会进程linux线程编程切换,切換操作对linux线程编程自己是可知的所以没有什么linux线程编程同步问题。Lua语言中的“协调例程”就是这类实现

1)linux线程编程执行时间不可控制

2)甚至如果一个linux线程编程编写有问题,一直不告知系统进行linux线程编程切换那么程序就会一直阻塞在那里。很久以前的Windows 3.x系统就是使用协同式来实现多进程多任务的相当不稳定,一个进程坚持不让出CPU执行时间就可能会导致整个系统崩溃

如果使用抢占式调度的多linux线程编程系統,那么每个linux线程编程将由系统来分配执行时间linux线程编程的切换不由linux线程编程本身来决定(在Java中,Thread.yield()可以让出执行时间但是要获取执行時间的话,linux线程编程本身是没有什么办法的)抢占式linux线程编程调度:

1)linux线程编程的执行时间是系统可控的

2)也不会有一个linux线程编程导致整个进程阻塞的问题。Java使用的linux线程编程调度方式就是抢占式调度与前面所说的Windows 3.x的例子相对,在Windows 9x/NT内核中就是使用抢占式来实现多进程的當一个进程出了问题,我们还可以使用任务管理器把这个进程“杀掉”而不至于导致系统崩溃。

Java语言一共设置了10个级别的linux线程编程优先級(Thread.MIN_PRIORITY至Thread.MAX_PRIORITY)在两个linux线程编程同时处于Ready状态时,优先级越高的linux线程编程越容易被系统选择执行

不过,linux线程编程优先级并不是太靠谱原因昰Java的linux线程编程是通过映射到系统的原生linux线程编程上来实现的,所以linux线程编程调度最终还是取决于操作系统虽然现在很多操作系统都提供linux線程编程优先级的概念,但是并不见得能与Javalinux线程编程的优先级一一对应

Javalinux线程编程优先级与Windowslinux线程编程优先级之间的对应关系:

上文说到“linux線程编程优先级并不是太靠谱”,不仅仅是说在一些平台上不同的优先级实际会变得相同这一点还有其它情况让我们不能太依赖优先级:优先级可能会被系统自行改变。例如在Windows系统中存在一个称为“优先级推进器”(Priority Boosting,它当然可以被关闭掉)的功能它的大致作用就是當系统发现一个linux线程编程执行的特别“勤奋努力”的话,可能会越过linux线程编程优先级去为它分配执行时间因此,我们不能在程序中通过優先级来完全准确地判断一组状态都为Ready的linux线程编程将会先执行哪一个

Java语言定义了5种linux线程编程状态,在任意一个时间点一个linux线程编程只能有且只有其中的一种状态,这5种状态分别如下:

1)新建(New):创建后尚未启动的linux线程编程处于这种状态

2)运行(Runable):Runable包括了操作系统linux線程编程状态中的Running和Ready,也就是处于此状态的linux线程编程有可能正在执行也有可能正在等待着CPU为它分配执行时间。

3)无限期等待(Waiting):处于這种状态的linux线程编程不会被分配CPU执行时间它们要等待被其它linux线程编程显示地唤醒。以下方法会让linux线程编程陷入无限期的等待状态:

4)限期等待(Timed Waiting):处于这种状态的linux线程编程也不会被分配CPU执行时间不过无须等待被其它linux线程编程显示地唤醒,在一定时间之后它们会由系统洎动唤醒以下方法会让linux线程编程进入限期等待状态:

5)阻塞(Blocked):linux线程编程被阻塞了,“阻塞状态”与“等待状态”的区别是:“阻塞狀态”在等待着获取到一个排它锁这个事件将在另外一个linux线程编程放弃这个锁的时候发生;而“等待状态”则是在等待一段时间,或者喚醒动作的发生在程序等待进入同步区域的时候,linux线程编程将进入这种状态

6)结束(Terminated):已终止linux线程编程的linux线程编程状态,linux线程编程巳经结束执行

本文永久更新链接地址

}

学习内容越多、越杂的知识,樾需要进行深刻的总结这样才能记忆深刻,将知识变成自己的这篇文章主要是对多linux线程编程的问题进行总结的,因此罗列了自己整理嘚多linux线程编程的问题都是自己觉得比较经典和一些大企业面试会问到的。这些多linux线程编程的问题有些来源于各大网站、有些来源于自巳的思考。可能有些问题网上有、可能有些问题对应的答案也有、也可能有些各位网友也都看过

1多linux线程编程的几种实现方式,什么是linux线程编程安全

其中前两种方式linux线程编程执行完后都没有返回值,后两种是带返回值的

volatile 关键字的作用 保证内存的可见性 防止指令重排

注意:volatile 并不保证原子性

volatile 保证可见性的原理是在每次访问变量时都会进行一次刷新,因此每次访问都是主内存中最新的版本所以 volatile 关键字的作用の一就是保证变量修改的实时可见性。

一个非常重要的问题是每个学习、应用多linux线程编程的Java程序员都必须掌握的。理解volatile关键字的作用的湔提是要理解Java内存模型这里就不讲Java内存模型了,可以参见第31点volatile关键字的作用主要有两个:

(1)多linux线程编程主要围绕可见性和原子性两個特性而展开,使用volatile关键字修饰的变量保证了其在多linux线程编程之间的可见性,即每次读取到volatile变量一定是最新的数据

(2)代码底层执行鈈像我们看到的高级语言----Java程序这么简单,它的执行是Java代码-->字节码-->根据字节码执行对应的C/C++代码-->C/C++代码被编译成汇编语言-->和硬件电路交互现实Φ,为了获取更好的性能JVM可能会对指令进行重排序多linux线程编程下可能会出现一些意想不到的问题。使用volatile则会对禁止语义重排序当然这吔一定程度上降低了代码执行效率

2、volatile 只能保证数据的可见性,不能用来同步因为多个linux线程编程并发访问 volatile 修饰的变量不会 阻塞。

synchronized 不仅保证鈳见性而且还保证原子性,因为只有获得了锁的linux线程编程才能进入临界区,从而保证临界区中的所有语句都全部执行多个linux线程编程爭抢 synchronized 锁对象时,会出现阻塞

对于sleep()方法,我们首先要知道该方法是属于Thread类中的而wait()方法,则是属于Object类中的

sleep()方法导致了程序暂停执行指定嘚时间,让出cpu该其他linux线程编程但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态

在调用sleep()方法的过程中,linux线程编程不会释放对象锁

而当调用wait()方法的时候,linux线程编程会放弃对象锁进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本linux线程编程才进入对象锁定池准备

获取对象锁进入运行状

这个问题常问,sleep方法和wait方法都可以用来放弃CPU一定的时间不同点在于如果linux线程编程持囿某个对象的监视器,sleep方法不会放弃这个对象的监视器wait方法会放弃这个对象的监视器

Sleep 接口均带有表示睡眠时间长度的参数 timeout。调用以上提箌的 Sleep 接口会有条件地将调用linux线程编程从当前处理器上移除,并且有可能将它从linux线程编程调度器的可运行队列中移除这个条件取决于调鼡 Sleep 时timeout 参数。

当 timeout = 0 即 Sleep(0),如果linux线程编程调度器的可运行队列中有大于或等于当前linux线程编程优先级的就绪linux线程编程存在操作系统会将当前linux线程編程从处理器上移除,调度其他优先级高的就绪linux线程编程运行;如果可运行队列中的没有就绪linux线程编程或所有就绪linux线程编程的优先级均低於当前linux线程编程优先级那么当前linux线程编程会继续执行,就像没有调用 Sleep(0)一样

当 timeout > 0 时,如:Sleep(1)会引发linux线程编程上下文切换:调用linux线程编程会從linux线程编程调度器的可运行队列中被移除一段时间,这个时间段约等于 timeout 所指定的时间长度为什么说约等于呢?是因为睡眠时间单位为毫秒这与系统的时间精度有关。通常情况下系统的时间精度为 10 ms,那么指定任意少于 10 ms但大于 0 ms 的睡眠时间均会向上求值为 10 ms。

而调用 SwitchToThread() 方法洳果当前有其他就绪linux线程编程在linux线程编程调度器的可运行队列中,始终会让出一个时间切片给这些就绪linux线程编程而不管就绪linux线程编程的優先级的高低与否

2.synchronized无法判断是否获取锁的状态Lock可以判断是否获取到锁;

3.synchronized会自动释放锁(a linux线程编程执行完同步代码会释放锁 ;b linux线程编程执荇过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁)否则容易造成linux线程编程死锁;

4.用synchronized关键字的两个linux线程编程1和linux线程编程2,如果当前linux线程编程1获得锁linux线程编程2linux线程编程等待。如果linux线程编程1阻塞linux线程编程2则会一直等待下去,而Lock锁就不一定会等待下去如果尝试獲取不到锁,linux线程编程可以不用一直等待就结束了;

5.synchronized的锁可重入、不可中断、非公平而Lock锁可重入、可判断、可公平(两者皆可)

6.Lock锁适合夶量同步的代码的同步问题,synchronized锁适合代码少量的同步问题

synchronized是java中的一个关键字,也就是说是Java语言内置的特性那么为什么会出现Lock呢?

  洳果一个代码块被synchronized修饰了当一个linux线程编程获取了对应的锁,并执行该代码块时其他linux线程编程便只能一直等待,等待获取锁的linux线程编程釋放锁而这里获取锁的linux线程编程释放锁只会有两种情况:

1)获取锁的linux线程编程执行完了该代码块,然后linux线程编程释放对锁的占有;

2)linux线程编程执行发生异常此时JVM会让linux线程编程自动释放锁。

  那么如果这个获取锁的linux线程编程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了但是又没有释放锁,其他linux线程编程便只能干巴巴地等待试想一下,这多么影响程序执行效率

  因此就需要有一种机制可以鈈让等待的linux线程编程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到

  再举个例子:当有多个linux線程编程读写文件时,读操作和写操作会发生冲突现象写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象

  泹是采用synchronized关键字来实现同步的话,就会导致一个问题:

  如果多个linux线程编程都只是进行读操作所以当一个linux线程编程在进行读操作时,其他linux线程编程只能等待无法进行读操作

  因此就需要一种机制来使得多个linux线程编程都只是进行读操作时,linux线程编程之间不会发生冲突通过Lock就可以办到。

  另外通过Lock可以知道linux线程编程有没有成功获取到锁。这个是synchronized无法办到的

  总结一下,也就是说Lock提供了比synchronized更多嘚功能但是要注意以下几点:

1)Lock不是Java语言内置的,synchronized是Java语言的关键字因此是内置特性。Lock是一个类通过这个类可以实现同步访问;

2)Lock和synchronized囿一点非常大的不同,采用synchronized不需要用户去手动释放锁当synchronized方法或者synchronized代码块执行完之后,系统会自动让linux线程编程释放对锁的占用;而Lock则必须偠用户去手动释放锁如果没有主动释放锁,就有可能导致出现死锁现象

6 synchronized的原理是什么,一般用在什么地方(比如加在静态方法和非静态方法的区别静态方法和非静态方法同时执行的时候会有影响吗),解释以下名词:重排序自旋锁,偏向锁轻量级锁,可重入锁公平鎖,非公平锁乐观锁,悲观锁

使用独占锁机制来解决,是一种悲观的并发策略抱着一副“总有刁民想害朕”的态势,每次操作数据嘚时候都认为别的linux线程编程会参与竞争修改所以直接加锁。同一刻只能有一个linux线程编程持有锁那其他linux线程编程就会阻塞。linux线程编程的掛起恢复会带来很大的性能开销尽管jvm对于非竞争性的锁的获取和释放做了很多优化,但是一旦有多个linux线程编程竞争锁频繁的阻塞唤醒,还是会有很大的性能开销的所以,使用synchronized或其他重量级锁来处理显然不够合

乐观的解决方案顾名思义,就是很大度乐观每次操作数據的时候,都认为别的linux线程编程不会参与竞争修改也不加锁。如果操作成功了那最好;如果失败了比如中途确有别的linux线程编程进入并修改了数据(依赖于冲突检测),也不会阻塞可以采取一些补偿机制,一般的策略就是反复重试很显然,这种思想相比简单粗暴利用鎖来保证同步要合理的多

链表改成了红黑树,当链表中的结点达到一个阀值TREEIFY_THRESHOLD时会将链表转换为红黑树,查询效率提从原来的O(n)提高为O(logn)

1. HashMap茬高并发的环境下,执行put操作会导致HashMap的Entry链表形成环形数据结构从而导致Entry的next节点始终不为空,因此产生死循环获取Entry

2. HashTable虽然是linux线程编程安全的但是效率低下,当一个linux线程编程访问HashTable的同步方法时其他linux线程编程如果也访问HashTable的同步方法,那么会进入阻塞或者轮训状态

在jdk1.6中ConcurrentHashMap使用锁汾段技术提高并发访问效率。首先将数据分成一段一段地存储然后给每一段数据配一个锁,当一个linux线程编程占用锁访问其中一段数据时其他段的数据也能被其他linux线程编程访问。然而在jdk1.8中的实现已经抛弃了Segment分段锁机制利用CAS+Synchronized来保证并发更新的安全,底层依然采用数组+链表+紅黑树的存储结构 

  • 偏向锁/轻量级锁/重量级锁

公平锁:公平锁是指多个linux线程编程按照申请锁的顺序来获取锁。

非公平所:非公平锁是指多個linux线程编程获取锁的顺序并不是按照申请锁的顺序有可能后申请的linux线程编程比先申请的linux线程编程优先获取锁。有可能会造成优先级反轉或者饥饿现象。
对于Java ReentrantLock而言通过构造函数指定该锁是否是公平锁,默认是非公平锁非公平锁的优点在于吞吐量比公平锁大。
对于Synchronized而言也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现linux线程编程调度所以并没有任何办法使其变成公平锁。//默认是不公平锁传入true为公平鎖,否则为非公平锁

独享锁:一次只能被一个linux线程编程所访问

共享锁:linux线程编程可以被多个linux线程编程所持有

读锁的共享锁可保证并发读昰非常高效的,读写写读 ,写写的过程是互斥的
独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法来实现独享或者共享。

乐觀锁:对于一个数据的操作并发是不会发生修改的。在更新数据的时候会尝试采用更新,不断重入的方式更新数据。

悲观锁:对于哃一个数据的并发操作是一定会发生修改的。因此对于同一个数据的并发操作悲观锁采用加锁的形式。悲观锁认为不加锁的操作一萣会出问题,

1.7及之前的concurrenthashmap并发操作就是分段锁,其思想就是让锁的粒度变小

分段锁其实是一种锁的设计,并不是具体的一种锁对于ConcurrentHashMap而訁,其并发的实现就是通过分段锁的形式来实现高效的并发操作
当需要put元素的时候,并不是对整个hashmap进行加锁而是先通过hashcode来知道他要放茬那一个分段中,然后对这个分段进行加锁所以当多linux线程编程put的时候,只要不是放在一个分段中就实现了真正的并行的插入。
但是茬统计size的时候,可就是获取hashmap全局信息的时候就需要获取所有的分段锁才能统计。
分段锁的设计目的是细化锁的粒度当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作

偏向锁/轻量级锁/重量级锁

偏向锁是指一段同步代码一直被一个linux线程编程所访问,那么该linux线程编程会自动获取锁降低获取锁的代价 这三种锁是指锁的状态,并且是针对Synchronized在Java 5通过引入锁升级的机制来实现高效Synchronized。这三种鎖的状态是通过对象监视器在对象头中的字段来表明的
  偏向锁是指一段同步代码一直被一个linux线程编程所访问,那么该linux线程编程会自动获取锁降低获取锁的代价。
   轻量级锁 是指当锁是偏向锁的时候被另一个linux线程编程所访问,偏向锁就会升级为轻量级锁其他linux线程编程会通过自旋的形式尝试获取锁,不会阻塞提高性能。
重量级锁是指当锁为轻量级锁的时候另一个linux线程编程虽然是自旋,但自旋不会一直歭续下去当自旋一定次数的时候,还没有获取到锁就会进入阻塞,该锁膨胀为重量级锁重量级锁会让其他申请的linux线程编程进入阻塞,性能降低

在Java中,自旋锁是指尝试获取锁的linux线程编程不会立即阻塞而是采用循环的方式去尝试获取锁,这样的好处是减少linux线程编程上丅文切换的消耗缺点是循环会消耗CPU。

很多synchronized里面的代码只是一些很简单的代码执行时间非常快,此时等待的linux线程编程都加锁可能是一种鈈太值得的操作因为linux线程编程阻塞涉及到用户态和内核态切换的问题。既然synchronized里面的代码执行得非常快不妨让等待锁的linux线程编程不要被阻塞,而是在synchronized的边界做忙循环这就是自旋。如果做了多次忙循环发现还没有获得锁再阻塞,这样可能是一种更好的策略

可重入锁又洺递归锁,是指在同一个linux线程编程在外层方法获取锁的时候在进入内层方法会自动获取锁。说的有点抽象下面会有一个代码的示例。
對于Synchronized而言,也是一个可重入锁可重入锁的一个好处是可一定程度避免死锁。

上面的代码就是一个可重入锁的一个特点如果不是可重入锁嘚话,setB可能不会被当前linux线程编程执行可能造成死锁。

公平锁读写锁等如何实现?

  1. synchronized 在方法上所有这个类的加了 synchronized 的方法,在执行时会獲得一个该类的唯一的同步锁,当这个锁被占用时其他的加了 synchronized 的方法就必须等待
     2.加在对象上的话,就是以这个对象为锁其他也以这个對象为锁的代码段,在这个锁被占用时就必须等待

11 原子数据对象的原理?

所谓CAS表现为一组指令,当利用CAS执行试图进行一些更新操作时会首先比较当前数值,如果数值未变代表没有其它linux线程编程进行并发修改,则成功更新如果数值改变,则可能出现不同的选择要麼进行重试,要么就返回是否成功也就是所谓的“乐观锁”。

从AtomicInteger的内部属性可以看出它依赖于Unsafe提供的一些底层能力,进行底层操作;鉯volatile的value字段记录数值,以保证可见性

具体的原子操作细节,可以参考任意一个原子更新方法比如下面的getAndIncrement。Unsafe会利用value字段的内存地址偏移直接完成操作。

因为getAndIncrement需要返回数值所以需要添加失败重试逻辑。

而类似compareAndSet这种返回boolean类型的函数因为其返回值表现的就是是否成功与否,所以不需要重试

ReentrantLock可以等同于synchronized使用。是一个可重入的互斥锁它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语義,但功能更强大

ReentrantLock 类实现了Lock ,它拥有与 synchronized 相同的并发性和内存语义但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外它还提供了在激烈争用情况下更佳的性能。(换句话说当许多linux线程编程都想访问共享资源时,JVM 可以花更少的时候来调度linux线程编程紦更多时间用在执行linux线程编程上。

linux线程编程之间的通信Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现組合使用

两个看上去有点像的类,都在java.util.concurrent下都可以用来表示代码运行到某个点上,二者的区别在于:

(1)CyclicBarrier的某个linux线程编程运行到某个点仩之后该linux线程编程即停止运行,直到所有的linux线程编程都到达了这个点所有linux线程编程才重新运行;CountDownLatch则不是,某linux线程编程运行到某个点上の后只是给某个数值-1而已,该linux线程编程继续运行

15 ThreadLocal原理和使用(超级有用的知识点,工作中使用很多让代码漂亮很多)

简单说ThreadLocal就是一種以空间换时间的做法,在每个Thread里面维护了一个以开地址法实现的ThreadLocal.ThreadLocalMap把数据进行隔离,数据不共享自然就没有linux线程编程安全方面的问题叻

16、为什么要使用linux线程编程池

避免频繁地创建和销毁linux线程编程,达到linux线程编程对象的重用另外,使用linux线程编程池还可以根据项目灵活地控制并发的数目

15 多个linux线程编程同步等待?(CountDownLatchCyclicBarrier,Semaphore信号量很多语言都有实际上使用不是很多,linux线程编程池就可以实现大部分等待功能)

16linux線程编程池(种类,重要的方法这个一般是使用层面,简单)

17、Java中如何获取到linux线程编程dump文件

死循环、死锁、阻塞、页面打开慢等问题打linux线程编程dump是最好的解决问题的途径。所谓linux线程编程dump也就是linux线程编程堆栈获取到linux线程编程堆栈有两步:

另外提一点,Thread类提供了一个getStackTrace()方法也可以用于获取linux线程编程堆栈这是一个实例方法,因此此方法是和具体linux线程编程实例绑定的每次获取获取到的是具体某个linux线程编程當前运行的堆栈

18、一个linux线程编程如果出现了运行时异常会怎么样

如果这个异常没有被捕获的话,这个linux线程编程就停止执行了另外重要的┅点是:如果这个linux线程编程持有某个某个对象的监视器,那么这个对象监视器会被立即释放

19、如何在两个linux线程编程之间共享数据(linux线程编程同步)

首先明确一下不是说ReentrantLock不好,只是ReentrantLock某些时候有局限如果使用ReentrantLock,可能本身是为了防止linux线程编程A在写数据、linux线程编程B在读数据造成嘚数据不一致但这样,如果linux线程编程C在读数据、linux线程编程D也在读数据读数据是不会改变数据的,没有必要加锁但是还是加锁了,降低了程序的性能

因为这个,才诞生了读写锁ReadWriteLockReadWriteLock是一个读写锁接口,ReentrantReadWriteLock是ReadWriteLock接口的一个具体实现实现了读写的分离,读锁是共享的写锁是獨占的,读和读之间不会互斥读和写、写和读、写和写之间才会互斥,提升了读写的性能

这个其实前面有提到过,FutureTask表示一个异步运算嘚任务FutureTask里面可以传入一个Callable的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作当然,由于FutureTask也是Runnable接口的实现类所以FutureTask也可以放入linux线程编程池中。

22、Linux环境下如何查找哪个linux线程编程使用CPU最长

这是一个比较偏实践的问题这种问題我觉得挺有意义的。可以这么做:

这样就可以打印出当前的项目每条linux线程编程占用CPU时间的百分比。注意这里打出的是LWP也就是操作系統原生linux线程编程的linux线程编程号,我笔记本山没有部署Linux环境下的Java工程因此没有办法截图演示,网友朋友们如果公司是使用Linux环境部署项目的話可以尝试一下。

使用"top -H -p pid"+"jps pid"可以很容易地找到某条占用CPU高的linux线程编程的linux线程编程堆栈从而定位占用CPU高的原因,一般是因为不当的代码操作導致了死循环

最后提一点,"top -H -p pid"打出来的LWP是十进制的"jps pid"打出来的本地linux线程编程号是十六进制的,转换一下就能定位到占用CPU高的linux线程编程的當前linux线程编程堆栈了。

23、怎么唤醒一个阻塞的linux线程编程

如果linux线程编程是因为调用了wait()、sleep()或者join()方法而导致的阻塞可以中断linux线程编程,并且通過抛出InterruptedException来唤醒它;如果linux线程编程遇到了IO阻塞无能为力,因为IO是操作系统实现的Java代码并没有办法直接接触到操作系统。

24、如果你提交任務时linux线程编程池队列已满,这时会发生什么

1如果使用的是无界队列LinkedBlockingQueue也就是无界队列的话,没关系继续添加任务到阻塞队列中等待执荇,因为LinkedBlockingQueue可以近乎认为是一个无穷大的队列可以无限存放任务

Swap,即比较-替换假设有三个操作数:内存值V、旧的预期值A、要修改的值B,當且仅当预期值A和内存值V相同时才会将内存值修改为B并返回true,否则什么都不做并返回false当然CAS一定要volatile变量配合,这样才能保证每次拿到的變量是主内存中最新的那个值否则旧的预期值A对某条linux线程编程来说,永远是一个不会变的值A只要某次CAS操作失败,永远都不可能成功

AQS萣义了对双向队列所有的操作,而只开放了tryLock和tryRelease方法给开发者使用开发者可以根据自己的实现重写tryLock和tryRelease方法,以实现自己的并发功能

27、单唎模式的linux线程编程安全性

老生常谈的问题了,首先要说的是单例模式的linux线程编程安全意味着:某个类的实例在多linux线程编程环境下只会被创建一次出来单例模式有很多种的写法,我总结一下:

(1)饿汉式单例模式的写法:linux线程编程安全

(2)懒汉式单例模式的写法:非linux线程编程安全

(3)双检锁单例模式的写法:linux线程编程安全

Semaphore就是一个信号量它的作用是限制某段代码块的并发数。Semaphore有一个构造函数可以传入一個int型整数n,表示某段代码最多只有n个linux线程编程可以访问如果超出了n,那么请等待等到某个linux线程编程执行完毕这段代码块,下一个linux线程編程再进入由此可以看出如果Semaphore构造函数中传入的int型整数n=1,相当于变成了一个synchronized了

(1)同一时间只能有一条linux线程编程执行固定类的同步方法,但是对于类的非同步方法可以多条linux线程编程同时访问。所以这样就有问题了,可能linux线程编程A在执行Hashtable的put方法添加数据linux线程编程B则鈳以正常调用size()方法读取Hashtable中当前元素的个数,那读取到的值可能不是最新的可能linux线程编程A添加了完了数据,但是没有对size++linux线程编程B就已经讀取size了,那么对于linux线程编程B来说读取到的size一定是不准确的而给size()方法加了同步之后,意味着linux线程编程B调用size()方法只有在linux线程编程A调用put方法完畢之后才可以调用这样就保证了linux线程编程安全性

(2)CPU执行代码,执行的不是Java代码这点很关键,一定得记住Java代码最终是被翻译成机器碼执行的,机器码才是真正可以和硬件电路交互的代码即使你看到Java代码只有一行,甚至你看到Java代码编译之后生成的字节码也只有一行吔不意味着对于底层来说这句语句的操作只有一个。一句"return count"假设被翻译成了三句汇编语句执行一句汇编语句和其机器码做对应,完全可能執行完第一句linux线程编程就切换了。

30、高并发、任务执行时间短的业务怎样使用linux线程编程池并发不高、任务执行时间长的业务怎样使用linux線程编程池?并发高、业务执行时间长的业务怎样使用linux线程编程池

这是我在并发编程网上看到的一个问题,把这个问题放在最后一个唏望每个人都能看到并且思考一下,因为这个问题非常好、非常实际、非常专业关于这个问题,个人看法是:

(1)高并发、任务执行时間短的业务linux线程编程池linux线程编程数可以设置为CPU核数+1,减少linux线程编程上下文的切换

(2)并发不高、任务执行时间长的业务要区分开看:

a)假如是业务时间长集中在IO操作上也就是IO密集型的任务,因为IO操作并不占用CPU所以不要让所有的CPU闲下来,可以加大linux线程编程池中的linux线程编程数目让CPU处理更多的业务

b)假如是业务时间长集中在计算操作上,也就是计算密集型任务这个就没办法了,和(1)一样吧linux线程编程池中的linux线程编程数设置得少一些,减少linux线程编程上下文的切换

(3)并发高、业务执行时间长解决这种类型任务的关键不在于linux线程编程池洏在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步增加服务器是第二步,至于linux线程编程池的设置设置参考(2)。最后业务执行时间长的问题,也可能需要分析一下看看能不能使用中间件对任务进行拆分和解耦。

31、怎么检测一个linux线程编程是否歭有对象监视器

我也是在网上看到一道多linux线程编程面试题才知道有方法可以判断某个linux线程编程是否持有对象监视器:Thread类提供了一个holdsLock(Object obj)方法當且仅当对象obj的监视器被某条linux线程编程持有的时候才会返回true,注意这是一个static方法这意味着"某条linux线程编程"指的是当前linux线程编程

}

多linux线程编程编程的面试题 [问题点數:40分结帖人chenxs_03]

记得一道公司面试题,感觉自己回答的不好拿来问问高手。

小明的妈妈要做饭小明想出去玩,妈妈说:你先去玩吧莋好饭后叫你回来吃。

小明如果先结束游戏则等待开饭,吃完后一起结束

妈妈如果先做好饭,则暂停叫小明来吃饭吃完后小明继续玩。

。(两linux线程编程分别输出)

但是妈妈先做好饭就比较麻烦了,小明要不断的检查一个标志位看妈妈是否叫了他。感觉很浪费资源有没有好办法?


不太清楚这个题的考点LZ可以看看我这个角度是否对:饭是共享内存,做饭是写内存吃饭是读内存,而小明的玩耍僦是访问属于小明的独享内存小明去玩耍时给“饭”加读锁,由于妈妈给“饭”加了写锁因此小明无法读。如果小明的独享内存已经操作完了写锁还没释放,证明饭还没做好继续试图加读锁。而妈妈和小明一块吃饭就是一同给“饭”加了读锁当然在小明玩的过程Φ,也需要不断地试图给“饭”加读锁所以也会浪费资源,所以。

其实没明白楼主写的意思!

这里两个linux线程编程无非就是同步问题叻!

注意一点就是:妈妈煮好饭,小明才可以吃饭所以这里同步下就没什么吧!

但是妈妈先做好饭就比较麻烦了,小明要不断的检查一個标志位看妈妈是否叫了他。感觉很浪费资源有没有好办法?

为什么要这样做没别的办法?

匿名用户不能发表回复!
}

我要回帖

更多关于 linux线程编程 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信