高并发与多线程基础

多线程

1.   java中有几种方法可以实现一个线程?   
        
答:在Java中实现一个线程有两种方法,第一是实现Runnable接口实现它的run()方法,第二种是继承Thread类,覆盖它的run()方法。这两种方法的区别是,如果你的类已经继承了其它的类,那么你只能选择实现Runnable接口了,因为Java只允许单继承的。

2.   如何停止一个正在运行的线程?
        
答:当不阻塞时候设置一个标志位,让代码块正常运行结束并停止线程。如果发生了阻塞,用interupt()方法,Thread.interrupt()方法不会中断一个正在运行的线程。这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。

3.   notify()notifyAll()有什么区别?
        
答:1notify()notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。
               2
void notify(): 唤醒一个正在等待该对象的线程。
               3
void notifyAll(): 唤醒所有正在等待该对象的线程。
       
两者的最大区别在于:
               notifyAll
使所有原来在该对象上等待被notify的线程统统退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争。
         notify
他只是选择一个wait状态线程进行通知,并使它获得该对象上的锁,但不惊动其他同样在等待被该对象notify的线程们,当第一个线程运            行完毕以后释放对象上的锁,此时如果该对象没有再次使用notify语句,即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的          通知,继续处在wait状态,直到这个对象发出一个notifynotifyAll,它们等待的是被notifynotifyAll,而不是锁。

4.   sleep() wait()有什么区别?
        
答:sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。
wait
Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

5.   什么是Daemon线程?它有什么意义?
       
答:所谓后台(daemon)线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这个线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程介绍时,程序也就终止了,同时会杀死进程中的所有后台线程。反过来说,只要有任何非后台线程还在运行,程序就不会终止。必须在线程启动之前调用setDaemon()方法,才能把它设置为后台线程。注意:后台进程在不执行finally子句的情况下就会终止其run()方法。

6.   java如何实现多线程之间的通讯和协作?
     答: Java提供了3个非常重要的方法来巧妙地解决线程间的通信问题。这3个方法分别是:wait()notify()notifyAll()。它们都是Object类的最终方法,因此每一个类都默认拥有它们。虽然所有的类都默认拥有这3个方法,但是只有在synchronized关键字作用的范围内,并且是同一个同步问题中搭配使用这3个方法时才有实际的意义。这些方法在Object类中声明的语法格式如下所示:
    

[html] view plaincopy

1.  final void wait() throws InterruptedException  

2.  final void notify()  

3.  final void notifyAll()  

1.   什么是可重入锁(ReentrantLock)? 

      答:  java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现。这就为 Lock 的多种实现留下了空间,各种实现可能有不同的调度算法、性能特性或者锁定语义。 ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。(换句话说,当许多线程都想访问共享资源时,JVM 可以花更少的时候来调度线程,把更多时间用在执行线程上。)
          reentrant
锁意味着什么呢?简单来说,它有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放。这模仿了 synchronized 的语义;如果线程进入由线程已经拥有的监控器保护的 synchronized 块,就允许线程继续进行,当线程退出第二个(或者后续)synchronized 块的时候,不释放锁,只有线程退出它进入的监控器保护的第一个 synchronized 块时,才释放锁。

2.   当一个线程进入某个对象的一个synchronized的实例方法后,其它线程是否可进入此对象的其它方法?
         
答:A、一个线程在访问一个对象的同步方法时,另一个线程可以同时访问这个对象的非同步方法
                B
 一个线程在访问一个对象的同步方法时,另一个线程不能同时访问这个同步方法。

3.   synchronizedjava.util.concurrent.locks.Lock的异同?
         
答:Lock synchronized 有一点明显的区别 —— lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能永远得不到释放!这一点区别看起来可能没什么,但是实际上,它极为重要。忘记在 finally 块中释放锁,可能会在程序中留下一个定时炸弹,当有一天炸弹爆炸时,您要花费很大力气才有找到源头在哪。而使用同步,JVM 将确保锁会获得自动释放。

4.   乐观锁和悲观锁的理解及如何实现,有哪些实现方式?

并发框架

1.   SynchronizedMapConcurrentHashMap有什么区别?
       
答:java5中新增了ConcurrentMap接口和它的一个实现类ConcurrentHashMapConcurrentHashMap提供了和Hashtable以及SynchronizedMap中所不同的锁机制。比起synchronizedMap来,它提供了好得多的并发性。多个读操作几乎总可以并发地执行,同时进行的读和写操作通常也能并发地执行,而同时进行的写操作仍然可以不时地并发进行(相关的类也提供了类似的多个读线程的并发性,但是,只允许有一个活动的写线程)。Hashtable中采用的锁机制是一次锁住整个hash表,从而同一时刻只能由一个线程对其进行操作;而ConcurrentHashMap中则是一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。前面说到的16个线程指的是写线程,而读操作大部分时候都不需要用到锁。只有在size等操作时才需要锁住整个hash表。
       
在迭代方面,ConcurrentHashMap使用了一种不同的迭代方式。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据iterator完成后再将头指针替换为新的数据,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。

2.   CopyOnWriteArrayList可以用于什么应用场景?
       
答:CopyOnWriteArrayList(免锁容器)的好处之一是当多个迭代器同时遍历和修改这个列表时,不会抛出ConcurrentModificationException。在CopyOnWriteArrayList中,写入将导致创建整个底层数组的副本,而源数组将保留在原地,使得复制的数组在被修改时,读取操作可以安全地执行。

线程安全

1.   什么叫线程安全?servlet是线程安全吗?
       
答:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。 或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题。
            servlet
不是线程安全的,每个servlet都只被实例化一次,每个调用都是servlet的同一个实例,并且对类变量没有线程安全,数据量大的时候容易照成异常。

2.   同步有几种实现方法?
     
答:同步的实现方面有两种,分别是synchronized,waitnotify

3.   volatile有什么用?能否用一句话说明下volatile的应用场景?
   
答:Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。可以被看作是一种程度较轻的 synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。
         
您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
                         A.
对变量的写操作不依赖于当前值。
                         B.
该变量没有包含在具有其他变量的不变式中。

4.   请说明下java的内存模型及其工作流程。
   
答:Java把内存划分成两种:一种是栈内存,一种是堆内存。
         
栈内存:存放对象:函数中基本类型的变量和对象的引用变量、静态类方法;特点:栈有一个很重要的特殊性,就是存在栈中的数据可以共享。
         
堆内存:存放对象:用来存放由new创建的对象和数组;特点:在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。
          java
内存模型 ( java memorymodel ):根据Java Language Specification中的说明, jvm系统中存在一个主内存(Main MemoryJava HeapMemory)Java中所有对象成员变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些对象成员变量的拷贝,线程对所有对象成员变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。
                (1)
获取对象监视器的锁(lock)
                (2)
清空工作内存数据, 从主存复制对象成员变量到当前工作内存, 即同步数据 (read and load)
                (3)
执行代码,改变共享变量值 (use and assign)
                (4)
将工作内存数据刷回主存 (store and write)
                (5)
释放对象监视器的锁 (unlock)

5.   为什么代码会重排序?

并发容器和框架

1.   如何让一段程序并发的执行,并最终汇总结果?
 
答:使用CyclicBarrier CountDownLatch都可以,使用CyclicBarrier 在多个关口处将多个线程执行结果汇总,CountDownLatch 在各线程执行完毕后向总线程汇报结果。

2.   如何合理的配置java线程池?如CPU密集型的任务,基本线程池应该配置多大?IO密集型的任务,基本线程池应该配置多大?用有界队列好还是无界队列好?任务非常多的时候,使用什么阻塞队列能获取最好的吞吐量?
 
答:1)配置线程池时CPU密集型任务可以少配置线程数,大概和机器的cpu核数相当,可以使得每个线程都在执行任务
        2
IO密集型时,大部分线程都阻塞,故需要多配置线程数,2*cpu核数
        3
)有界队列和无界队列的配置需区分业务场景,一般情况下配置有界队列,在一些可能会有爆发性增长的情况下使用无界队列。
        4
)任务非常多时,使用非阻塞队列使用CAS操作替代锁可以获得好的吞吐量。

3.   如何使用阻塞队列实现一个生产者和消费者模型?

4.   多读少写的场景应该使用哪个并发容器,为什么使用它?比如你做了一个搜索引擎,搜索引擎每次搜索前需要判断搜索关键词是否在黑名单里,黑名单每天更新一次。
 
答:1CopyOnWriteArrayList这个容器适用于多读少写
        2
)读写并不是在同一个对象上。在写时会大面积复制数组,所以写的性能差,在写完成后将读的引用改为执行写的对象

Java中的锁

1.   如何实现乐观锁(CAS)?如何避免ABA问题?
 
答:1)读取内存值的方式实现了乐观锁(比如:SVN系统),方法:第一,比较内存值和期望值;第二,替换内存值为要替换值。
        2
)带参数版本来避免aba问题,在读取和替换的时候进行判定版本是否一致
                   

2.   读写锁可以用于什么应用场景?
 
答: 读写锁可以用于多读少写的场景,读写锁支持多个读操作并发执行,写操作只能由一个线程来操作
        ReadWriteLock
对向数据结构相对不频繁地写入,但是有多个任务要经常读取这个数据结构的这类情况进行了优化。ReadWriteLock使得你可以同事有多个读取者,只要它们都不试图写入即可。如果写锁已经被其他任务持有,那么任何读取者都不能访问,直至这个写锁被释放为止。
        ReadWriteLock
对程序心性能的提高受制于如下几个因素也还有其他等等的因素。
          1
)数据被读取的频率与被修改的频率相比较的结果。
          2
)读取和写入的时间
          3
)有多少线程竞争
          4
)是否在多处理机器上运行

3.   什么时候应该使用可重入锁?
 
答:重入锁指的是在某一个线程中可以多次获得同一把锁,在线程中多次操作有锁的方法。

4.   什么场景下可以使用volatile替换synchronized
 
答: 只需要保证共享资源的可见性的时候可以使用volatile替代,synchronized保证可操作的原子性一致性和可见性。volatile适用于新值不依赖于就值的情形。
      volatile
java提供的一种同步手段,只不过它是轻量级的同步,为什么这么说,因为volatile只能保证多线程的内存可见性,不能保证多线程的执行有序性。而最彻底的同步要保证有序性和可见性,例如synchronized。任何被volatile修饰的变量,都不拷贝副本到工作内存,任何修改都及时写在主存。因此对于Valatile修饰的变量的修改,所有线程马上就能看到,但是volatile不能保证对变量的修改是有序的。volatile存在的意义是,任何线程对某个变量的修改,都会马上被其他线程读取到,因为直接操作主存,没有线程对工作内存和主存的同步。所以,volatile的使用场景是有限的,在有限的一些情形下可以使用 volatile 变量替代锁(synchronized)。
       
要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
              1)变量的写操作不依赖于当前值。
              2)该变量没有包含在具有其他变量的不变式中 

高性能高并发面试

http://www.zhihu.com/search?q=%E9%AB%98%E5%B9%B6%E5%8F%91&type=question

 http://storage.it168.com/a2012/0217/1313/000001313424_5.shtml

redis,nginx/Tengine,keeplive,DRBD,heartbeat这些小工具还是可以在虚拟机上面多开几台跑起来的.至于大业务场景,除了进大公司没有别的办法,因为有些工具运行的配置要求太高,必须多台服务器配合才能完成.
如果有精力,业内很喜欢用perl,python,C来写一些针对热点业务的负载脚本.这需要有http协议等网络封包的理论基础.
一些建议处理高并发要学习的东西实在太多.要在没有实际工作经验的情况下逐一了解太难,也很难深入.对于高并发的学习,我建议除了多阅读高并发架构的文档学习基本的方法论以外,自己要去深入学习网络基础,数据结构和算法.这些都是处理高并发热点的理论基础.

-----------------------------------------------------------------------------------------------------------------------

进程和线程模型,非阻塞IOepoll/iocp这些不提,横向扩展和读写分离, 
hot standby
这些老生常谈的也不算,memcached/redis缓存也不算, 
也不扯nodejstwistedgeventtornadoerlang等等有助于高并发的工具, 
这些都不算,有哪些秘密? 感觉做过高并发的都看不起同行了,到底有啥绝活?

SEDA or Actor这类的设计的东西?

-----------------------------------------------------------------------------------------------------------------------

业务数据库  - 数据水平分割(分区分表分库)、读写分离
业务应用 - 逻辑代码优化(算法优化)、公共数据缓存
应用服务器 - 反向静态代理、配置优化、负载均衡(apache分发,多tomcat实例)
系统环境 - JVM调优
页面优化 - 减少页面连接数、页面尺寸瘦身

应用服务器配置优化,如连接数的优化,每个请求都是独立的连接线程,所以优化此配置可以提高服务器接收HTTP并发请求的能力.当然,也不是支持的连接数越多越好。因为接收过多的HTTP请求,可能会导致服务器处理不了,宕机、瘫痪,类似铁路局购票网的状况。大部分的站点会根据服务器处理能力来设置连接数上限。

提升应用服务器的处理能力:
如多服务器集群,接收1000个请求分发多几个服务器去处理。同时,CPU主频,jvm,代码逻辑都不同程度影响业务计算能力。

如果业务有对数据库进行操作的,那么磁盘的IO读写速率是影响服务器的处理能力的最大因素。
因为无论配置的连接数再多,也需要数据库服务器执行SQL时进行的磁盘IO读写能力支撑才行。关于数据库服务器将读写压力分担。常用的方法我上面已经总结了。。。

-----------------------------------------------------------------------------------------------------------------------

Java做一个大流量、高并发的网站应该怎么样进行底层构架?采用哪些框架技术比较适合?

 

通用措施:
1
、动态资源和静态资源分离;
2
CDN
3
、负载均衡;
4
、分布式缓存;
5
、数据库读写分离或数据切分(垂直或水平);
6
、服务分布式部署。

没看到对业务的描述,业务不同面对的可能是相差很大的方案,太早介入性能方面的考虑未必是好事,有可能浪费太多时间在一些弯路上,如果实在担心的话,尽量采用一些扩展性强一点点的框架,还是那句话,不要太早走极端。

 

多台tomcat做负载均衡,即使你效率再高。对于高并发,单台tomcat能管理的thread pool的线程数也是有限的    
tomcat使用apr/nio模式增加吞吐量    
对于大流量,动静分离,tomcat处理静态资源的能力比较差,交给nginx   
缓存是重要的,把实时性要求不高的,或者可以忍受一段时间内的实时,缓存起来。   

使用cdn缓存

 

1:框架用最熟悉的;
2
:优化从最上层的业务逻辑开始;
3
:硬件舍得投入。

跟框架没必然联系,另外直接使用serverlet对性能提升微乎其微。关健还在于负载均衡的多层次使用,缓存的合理使用,数据垂直和水平切分,异步方案,多系统间的同步,最后千万别忘了可用性。

框架对性能影响还是很大的,如果部署到分布式架构上的话,系统不仅性能得到了提升,容错性和可扩展性都会大大改善。前台可以用Tuscany进行逻辑划分,子系统分布到不同的服务器上,后台可以使用Hadoop进行分布式存储。但是分布式需要面临很多的技术问题:分布式缓存,分布式数据库等等,对技术人员要求较高。此外,还可以使用nginx做负载均衡分发给tomcat

框架对性能的影响微乎其微,Java的话挑熟悉的好用的就行了。更重要的是整体架构的设计、数据库的设计和优化、缓存系统等等。用Tomcat的话不要用Apache,小并发量还能用用,一旦请求多了会很麻烦,用NginxJVM特别是GC的调优也可以看看。

有几个常用的措施  
1、对常用功能建立缓存模块  
2、网页尽量静态化  
3、使用单独的图片服务器,降低服务器压力,使其不会因为图片加载造成崩溃  
4、使用镜像解决不同网络接入商和不同地域用户访问差异  
5、数据库集群图表散列  
6、加强网络层硬件配置,硬的不行来软的。  
7、终极办法:负载均衡

大流量,高并发的网站主要考虑的是可伸缩性,当用户量,流量增大的时候,可以通过增加机器来分担压力。。至于直接用不用servlet,这个看你的架构。。性能和复杂性一般都不能兼得。。

个人认为框架并不能为系统的性能得到多大的提升,框架能够帮助开发人员提高开发效率,滥用框架反而会导致系统性能的下降。  
建议使用开发团队最熟悉的框架,关注代码本身,防止出现内存溢出的情况。  
大流量,高并发的网站主要的压力应该是在数据库的io操作上,尽量避免系统频繁请求数据库,优化查询语句,合理使用索引,减少sql语句执行的时间。  
可以使用一些缓存技术,如memcache,将频繁读取,不经常变化的数据放入缓存中。

面对大流量,高并发的问题,最好的方法是增加硬件投入。另外,我不认为servlet去处理底层数据是个明智的选择。servlet玩的就是requestresponse,处理和响应客户端请求为主。推荐一本书《构建高性能WEB站点》。

底层架构是指服务器吗?   

服务器上的集群优化很重要的。  
尝试下 Apache + Tomcat 吧。多个Tomcat集群,Apache做前端,负责静态内容。再加几台服务器,分别做memcached(多台)、数据库服务器。   

ApacheTomcat之间的沟通采用AJP协议,据说效率很高。   

这么下来,这么多台服务器,足够撑很多用户了。

http://www.zhihu.com/question/19809311

-----------------------------------------------------------------------------------------------------------------------

先学测试吧。不是那种业务功能的测试,是系统的测试。因为要解决大数据量、高并发的问题,我个人的知识与经验是:

1、先用单机测试。用工具产生大并发量去轰击服务器,直至服务器缓慢,甚至接近崩溃;

2、在服务器艰难地工作的时候,用工具测试服务器,仔细分析,是什么使得服务器如此艰难,是 cpu 是网络?还是硬盘 io ?又或者是你的应用?数据库?

3、找到系统瓶颈后,优化,解决这个瓶颈,然后再循环测试。这时你又会发现新的瓶颈,再解决。循环1 - 3步,直到各方面基本平衡为止。

4、当单机无法解决问题的时候,接着开始考虑负载均衡,考虑分布式方案,然后再用 1 - 3 的步骤分析与测试。

最后,你的问题是,要学什么?答案就是:学要完成上述的步骤,解决其中产生的问题所涉及的各种知识。这不是一两本书可以讲得完的。

-----------------------------------------------------------------------------------------------------------------------

大流量: Varnish, Nginx,HAproxy 

高并发: Node.js, Nginx, Redis,No-SQL

静态化,CDN

-----------------------------------------------------------------------------------------------------------------------

你需要了解大数据高并发的瓶颈在哪里,一般都是数据库层面的,机械硬盘承载不起非常快速的读写操作,cpu承载不起大量的逻辑运算,所以最基本的解决思路就是:
1.换固态硬盘加快硬盘的读写效率。
2.建立缓存中间件降低对硬盘的读写次数,缓存不用多说了,最最最基本和重要的优化策略。
3.将硬盘的读写或者数据的计算分摊到多台机器上,也就是集群。hadoop就是基于这个层面的。
4.良好的查询算法,降低读的次数,分表,分库,索引等都是基于这层面的。

理论上来讲,在带宽充裕的情况下,只要遵循上面的4个思路进行延伸就可以解决大部分的高并发问题。

-----------------------------------------------------------------------------------------------------------------------

怎样学习才能拥有所谓“高并发”的经验?

这个问题完全可以重定向到如何处理高并发业务场景. 以下只是我工作一年多接触到的一些基础,也许有偏差,要具备高并发的经验确实需要有实际项目,因为业务逻辑其实很容易理清,但是要在高并发的情况下如何找到业务繁忙的热点并进行优化,完全只能凭经验.
假如没有靠谱的公司,接触不到高并发的业务场景怎么办? 从处理技巧上,可以通过大牛学习高并发的架构,比如张宴:张宴的博客 -Web系统架构与底层研发.至少你可以知道处理高并发的业务逻辑是:

·        前端:异步请求+资源静态化+cdn

·        后端:请求队列+轮询分发+负载均衡+共享缓存

·        数据层:redis缓存+数据分表+写队列

·        存储:raid阵列+热备

·        网络:dns轮询+DDOS攻击防护

对于高并发并没有什么通用解决方案,必须根据业务场景进行分析,不同的业务场景对于架构的取舍是不一样的.但万变不离其宗,掌握这些处理高并发的分析方法还是很有必要的.
如何学习高并发的工具? 处理高并发的开源轮子其实很多.很多高并发的架构分享都会提及使用的工具,自己多留心,再看看手册,有条件自己搭起来跑一跑.redis,nginx/Tengine,keeplive,DRBD,heartbeat这些小工具还是可以在虚拟机上面多开几台跑起来的.至于大业务场景,除了进大公司没有别的办法,因为有些工具运行的配置要求太高,必须多台服务器配合才能完成.
如何模拟高并发场景? 并不是只有实际生产环境才能测试高并发,其实模拟高并发的轮子也很多,最常用的apache benchmark,winrunner,loadrunner,这些教程很多,用来模拟基本的高并发业务绰绰有余,自己安装试用版,学学如何用,模拟些常用的业务. 如果有精力,业内很喜欢用perl,python,C来写一些针对热点业务的负载脚本.这需要有http协议等网络封包的理论基础.
一些建议 处理高并发要学习的东西实在太多.要在没有实际工作经验的情况下逐一了解太难,也很难深入.对于高并发的学习,我建议除了多阅读高并发架构的文档学习基本的方法论以外,自己要去深入学习网络基础,数据结构和算法.这些都是处理高并发热点的理论基础.

 

 

刘天斯:一例千万级pv高性能高并发网站架构

一个支撑千万级PV的网站是非常考验一个架构是否成熟、健壮(本文不涉及软件架构的层面,有兴趣也可以讨论)。现抛出一个系统层面的架构,不保证是最优的方案,但也许适合你。理由是再优秀的架构都不具备通用性,需要根据每种应用特点针对性来设计。希望起到抛砖引玉的作用。

架构说明:

1、架构中直接引入软件名称的模块,是个人推荐使用的,如HaproxyHadoop等;

2、关于全局负载均衡,看成本投入情况,可以使用商业的产品,如F5-GTM,开源方案便是自搭智能DNS

3、本地负载均衡方案,可以考虑F5-LTM或成熟的开源解决方案LVS

4、代理层为什么推荐大家使用HaproxyHaproxy是一个非常优秀的反向代理软件,十分高效、稳定。国内top 10的互联网公司都有在使用;

5、缓存层可以使用SquidVarnish,个人更倾向Varnish。配置灵活、运行稳定,提供非常便利的管理接口。为啥在缓存层前面加一层代理?优点非常多,列举如下:

·        根据应用配置URI路由规则,集中热点来提高后端缓存的命中率;

·        轻松划分网站频道、版块,更好对应用进步组织、规划;

·        URI进行一般性安全过滤,抵御注入攻击;

·        弹性调配硬件资源,应对突发事件产生大流量;

·        可回收宝贵的公网IP资源;

6、应用层开源技术方案非常多且成熟,在此不详细描述;

7、数据库层主流开源解决方案Mysql是首选,主从复制(一主对多从)是目前比较靠谱的模式;

8、关于Nosql,应用场景不多说,可参考给部门做的Mongodb技术交流PPT”文章,redismemcached等作为热点数据存储、数据库缓存都非常理想;

9、内网DNS扮演的角色非常重要,一定要消灭code中出现的内网IP地址,很大程度减少因IP变更、服务器故障而修改源码的情况,同时也便于维护;

10、内网LB适用在内部WEB接口、多台数据库Slave、多台Nosql Slave、公共服务等应用的负载均衡,可以使用LVSHaproxy来实现,可用性要求不高的应用可行直接使用Localhost DNS轮询;

11hadoop适合海量数据的存储与处理,如做网站日志分析、用户数据挖掘等;

12、管理集群,平台的核心,运维的阵地;

java面试中的高并发的问题

原创 2016年07月06日 15:51:07

·        2029

1)尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。

2)我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他们都有独立的、甚至很多台的图片服务器。采用图片服务器这样的架构

3)数据库分离: 根据不同应用模块,创建不同的数据库。

4)使用缓存技术。  像redis等nosql的关系型数据库, 搭建redis的集群。

5)负载均衡均衡地分发请求。

JAVA多线程和并发基础面试题

原创 2014年05月23日 11:28:36

·        标签:

·        线程池 /

·        java /

·        jvm /

·        技术

·        41929

多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题。(校对注:非常赞同这个观点

Java多线程面试问题

1. 进程和线程之间有什么不同?

一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。Java运行环境是一个包含了不同的类和程序的单一进程。线程可以被称为轻量级进程。线程需要较少的资源来创建和驻留在进程中,并且可以共享进程中的资源。

2. 多线程编程的好处是什么?

在多线程程序中,多个线程被并发的执行以提高程序的效率,CPU不会因为某个线程需要等待资源而进入空闲状态。多个线程共享堆内存(heap memory),因此创建多个线程去执行一些任务会比创建多个进程更好。举个例子,ServletsCGI更好,是因为Servlets支持多线程而CGI不支持。

3. 用户线程和守护线程有什么区别?

当我们在Java程序中创建一个线程,它就被称为用户线程。一个守护线程是在后台执行并且不会阻止JVM终止的线程。当没有用户线程在运行的时候,JVM关闭程序并且退出。一个守护线程创建的子线程依然是守护线程。

4. 我们如何创建一个线程?

有两种创建线程的方法:一是实现Runnable接口,然后将它传递给Thread的构造函数,创建一个Thread对象;二是直接继承Thread类。若想了解更多可以阅读这篇关于如何在Java中创建线程的文章。

5. 有哪些不同的线程生命周期?

当我们在Java程序中新建一个线程时,它的状态是New当我们调用线程的start()方法时,状态被改变为Runnable。线程调度器会为Runnable线程池中的线程分配CPU时间并且讲它们的状态改变为Running其他的线程状态还有WaitingBlocked Dead。读这篇文章可以了解更多关于线程生命周期的知识。

6. 可以直接调用Thread类的run()方法么?

当然可以,但是如果我们调用了Threadrun()方法,它的行为就会和普通的方法一样,为了在新的线程中执行我们的代码,必须使用Thread.start()方法。

7. 如何让正在运行的线程暂停一段时间?

我们可以使用Thread类的Sleep()方法让线程暂停一段时间。需要注意的是,这并不会让线程终止,一旦从休眠中唤醒线程,线程的状态将会被改变为Runnable,并且根据线程调度,它将得到执行。

8. 你对线程优先级的理解是什么?

每一个线程都是有优先级的,一般来说,高优先级的线程在运行时会具有优先权,但这依赖于线程调度的实现,这个实现是和操作系统相关的(OS dependent)。我们可以定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线程前执行。线程优先级是一个int变量(1-10)1代表最低优先级,10代表最高优先级。

9. 什么是线程调度器(Thread Scheduler)和时间分片(Time Slicing)

线程调度器是一个操作系统服务,它负责为Runnable状态的线程分配CPU时间。一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现。时间分片是指将可用的CPU时间分配给可用的Runnable线程的过程。分配CPU时间可以基于线程优先级或者线程等待的时间。线程调度并不受到Java虚拟机控制,所以由应用程序来控制它是更好的选择(也就是说不要让你的程序依赖于线程的优先级)。

10. 在多线程中,什么是上下文切换(context-switching)

上下文切换是存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。上下文切换是多任务操作系统和多线程环境的基本特征。

11. 你如何确保main()方法所在的线程是Java程序最后结束的线程?

我们可以使用Thread类的joint()方法来确保所有程序创建的线程在main()方法退出前结束。这里有一篇文章关于Thread类的joint()方法

12.线程之间是如何通信的?

当线程间是可以共享资源时,线程间通信是协调它们的重要的手段。Object类中wait()\notify()\notifyAll()方法可以用于线程间通信关于资源的锁的状态。点击这里有更多关于线程wait, notifynotifyAll.

13.为什么线程通信的方法wait(), notify()notifyAll()被定义在Object类里?

Java的每个对象中都有一个锁(monitor,也可以成为监视器) 并且wait()notify()等方法用于等待对象的锁或者通知其他线程对象的监视器可用。在Java的线程中并没有可供任何对象使用的锁和同步器。这就是为什么这些方法是Object类的一部分,这样Java的每一个类都有用于线程间通信的基本方法

14. 为什么wait(), notify()notifyAll()必须在同步方法或者同步块中被调用?

当一个线程需要调用对象的wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的notify()方法。同样的,当一个线程需要调用对象的notify()方法时,它会释放这个对象的锁,以便其他在等待的线程就可以得到这个对象锁。由于所有的这些方法都需要线程持有对象的锁,这样就只能通过同步来实现,所以他们只能在同步方法或者同步块中被调用。

15. 为什么Thread类的sleep()yield()方法是静态的?

Thread类的sleep()yield()方法将在当前正在执行的线程上运行。所以在其他处于等待状态的线程上调用这些方法是没有意义的。这就是为什么这些方法是静态的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些方法。

16.如何确保线程安全?

Java中可以有很多方法来保证线程安全——同步,使用原子类(atomicconcurrent classes),实现并发锁,使用volatile关键字,使用不变类和线程安全类。在线程安全教程中,你可以学到更多。

17. volatile关键字在Java中有什么作用?

当我们使用volatile关键字去修饰变量的时候,所以线程都会直接读取该变量并且不缓存它。这就确保了线程读取到的变量是同内存中是一致的。

18. 同步方法和同步块,哪个是更好的选择?

同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。

19.如何创建守护线程?

使用Thread类的setDaemon(true)方法可以将线程设置为守护线程,需要注意的是,需要在调用start()方法前调用这个方法,否则会抛出IllegalThreadStateException异常。

20. 什么是ThreadLocal?

ThreadLocal用于创建线程的本地变量,我们知道一个对象的所有线程会共享它的全局变量,所以这些变量不是线程安全的,我们可以使用同步技术。但是当我们不想使用同步的时候,我们可以选择ThreadLocal变量。

每个线程都会拥有他们自己的Thread变量,它们可以使用get()\set()方法去获取他们的默认值或者在线程内部改变他们的值。ThreadLocal实例通常是希望它们同线程状态关联起来是private static属性。在ThreadLocal例子这篇文章中你可以看到一个关于ThreadLocal的小程序。

21. 什么是Thread Group?为什么建议使用它?

ThreadGroup是一个类,它的目的是提供关于线程组的信息。

ThreadGroup API比较薄弱,它并没有比Thread提供了更多的功能。它有两个主要的功能:一是获取线程组中处于活跃状态线程的列表;二是设置为线程设置未捕获异常处理器(ncaught exception handler)。但在Java 1.5Thread类也添加了setUncaughtExceptionHandler(UncaughtExceptionHandler eh) 方法,所以ThreadGroup是已经过时的,不建议继续使用。

1

2

3

4

5

6

 

 

t1.setUncaughtExceptionHandler(newUncaughtExceptionHandler(){

 

 

@Override

 

 

publicvoiduncaughtException(Threadt,Throwablee){

 

 

System.out.println("exception

 occured:"+e.getMessage());

 

 

}

 

 

});

 

22. 什么是Java线程转储(Thread Dump),如何得到它?

线程转储是一个JVM活动线程的列表,它对于分析系统瓶颈和死锁非常有用。有很多方法可以获取线程转储——使用ProfilerKill -3命令,jstack工具等等。我更喜欢jstack工具,因为它容易使用并且是JDK自带的。由于它是一个基于终端的工具,所以我们可以编写一些脚本去定时的产生线程转储以待分析。读这篇文档可以了解更多关于产生线程转储的知识。

23. 什么是死锁(Deadlock)?如何分析和避免死锁?

死锁是指两个以上的线程永远阻塞的情况,这种情况产生至少需要两个以上的线程和两个以上的资源。

分析死锁,我们需要查看Java应用程序的线程转储。我们需要找出那些状态为BLOCKED的线程和他们等待的资源。每个资源都有一个唯一的id,用这个id我们可以找出哪些线程已经拥有了它的对象锁。

避免嵌套锁,只在需要的地方使用锁和避免无限期等待是避免死锁的通常办法,阅读这篇文章去学习如何分析死锁

24. 什么是Java Timer类?如何创建一个有特定时间间隔的任务?

java.util.Timer是一个工具类,可以用于安排一个线程在未来的某个特定时间执行。Timer类可以用安排一次性任务或者周期任务。

java.util.TimerTask是一个实现了Runnable接口的抽象类,我们需要去继承这个类来创建我们自己的定时任务并使用Timer去安排它的执行。

这里有关于java Timer的例子

25. 什么是线程池?如何创建一个Java线程池?

一个线程池管理了一组工作线程,同时它还包括了一个用于放置等待执行的任务的队列。

java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池。线程池例子展现了如何创建和使用线程池,或者阅读ScheduledThreadPoolExecutor例子,了解如何创建一个周期任务。

Java并发面试问题

1. 什么是原子操作?在Java Concurrency API中有哪些原子类(atomic classes)

原子操作是指一个不受其他操作影响的操作任务单元。原子操作是在多线程环境下避免数据不一致必须的手段。

int++并不是一个原子操作,所以当一个线程读取它的值并加1时,另外一个线程有可能会读到之前的值,这就会引发错误。

为了解决这个问题,必须保证增加操作是原子的,在JDK1.5之前我们可以使用同步技术来做到这一点。到JDK1.5java.util.concurrent.atomic包提供了intlong类型的装类,它们可以自动的保证对于他们的操作是原子的并且不需要使用同步。可以阅读这篇文章来了解Javaatomic

2. Java Concurrency API中的Lock接口(Lockinterface)是什么?对比同步它有什么优势?

Lock接口比同步方法和同步块提供了更具扩展性的锁操作。他们允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的条件对象。

它的优势有:

·        可以使锁更公平

·        可以使线程在等待锁的时候响应中断

·        可以让线程尝试获取锁,并在无法获取锁的时候立即返回或者等待一段时间

·        可以在不同的范围,以不同的顺序获取和释放锁

阅读更多关于锁的例子

3. 什么是Executors框架?

Executor框架同java.util.concurrent.Executor接口在Java5中被引入。Executor框架是一个根据一组执行策略调用,调度,执行和控制的异步任务的框架。

无限制的创建线程会引起应用程序内存溢出。所以创建一个线程池是个更好的的解决方案,因为可以限制线程的数量并且可以回收再利用这些线程。利用Executors框架可以非常方便的创建一个线程池,阅读这篇文章可以了解如何使用Executor框架创建一个线程池

4. 什么是阻塞队列?如何使用阻塞队列来实现生产者-消费者模型?

java.util.concurrent.BlockingQueue的特性是:当队列是空的时,从队列中获取或删除元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。

阻塞队列不接受空值,当你尝试向队列中添加空值的时候,它会抛出NullPointerException

阻塞队列的实现都是线程安全的,所有的查询方法都是原子的并且使用了内部锁或者其他形式的并发控制。

BlockingQueue 接口是java collections框架的一部分,它主要用于实现生产者-消费者问题。

阅读这篇文章了解如何使用阻塞队列实现生产者-消费者问题。

5. 什么是CallableFuture?

Java 5concurrency包中引入了java.util.concurrent.Callable 接口,它和Runnable接口很相似,但它可以返回一个对象或者抛出一个异常。

Callable接口使用泛型去定义它的返回类型。Executors类提供了一些有用的方法去在线程池中执行Callable内的任务。由于Callable任务是并行的,我们必须等待它返回的结果。java.util.concurrent.Future对象为我们解决了这个问题。在线程池提交Callable任务后返回了一个Future对象,使用它我们可以知道Callable任务的状态和得到Callable返回的执行结果。Future提供了get()方法让我们可以等待Callable结束并获取它的执行结果。

阅读这篇文章了解更多关于CallableFuture的例子

6. 什么是FutureTask?

FutureTaskFuture的一个基础实现,我们可以将它同Executors使用处理异步任务。通常我们不需要使用FutureTask类,单当我们打算重写Future接口的一些方法并保持原来基础的实现是,它就变得非常有用。我们可以仅仅继承于它并重写我们需要的方法。阅读Java FutureTask例子,学习如何使用它。

7.什么是并发容器的实现?

Java集合类都是快速失败的,这就意味着当集合被改变且一个线程在使用迭代器遍历集合的时候,迭代器的next()方法将抛出ConcurrentModificationException异常。

并发容器支持并发的遍历和并发的更新。

主要的类有ConcurrentHashMap, CopyOnWriteArrayList CopyOnWriteArraySet,阅读这篇文章了解如何避免ConcurrentModificationException

8. Executors类是什么?

ExecutorsExecutorExecutorServiceScheduledExecutorServiceThreadFactoryCallable类提供了一些工具方法。

Executors可以用于方便的创建线程池。

 

很多核心Java面试题来源于多线程(Multi-Threading)和集合框架(Collections Framework),理解核心线程概念时,娴熟的实际经验是必需的。这篇文章收集了 Java 线程方面一些典型的问题,这些问题经常被高级工程师所问到。

0.Java 中多线程同步是什么?

在多线程程序下,同步能控制对共享资源的访问。如果没有同步,当一个 Java 线程在修改一个共享变量时,另外一个线程正在使用或者更新同一个变量,这样容易导致程序出现错误的结果。

1.解释实现多线程的几种方法?

Java 线程可以实现 Runnable 接口或者继承 Thread 类来实现,当你打算多重继承时,优先选择实现 Runnable

2.Thread.start () Thread.run()有什么区别?

Thread.start ()方法(native)启动线程,使之进入就绪状态,当 cpu 分配时间该线程时,由 JVM 调度执行 run ()方法。

3.为什么需要 run () start ()方法,我们可以只用 run ()方法来完成任务吗?

我们需要run ()&start ()这两个方法是因为 JVM 创建一个单独的线程不同于普通方法的调用,所以这项工作由线程的 start 方法来完成,start 由本地方法实现,需要显示地被调用,使用这俩个方法的另外一个好处是任何一个对象都可以作为线程运行,只要实现了 Runnable 接口,这就避免因继承了 Thread 类而造成的 Java 的多继承问题。

4.什么是ThreadLocal 类,怎么使用它?

ThreadLocal 是一个线程级别的局部变量,并非本地线程ThreadLocal 为每个使用该变量的线程提供了一个独立的变量副本,每个线程修改副本时不影响其它线程对象的副本(译者注)

下面是线程局部变量(ThreadLocal variables)的关键点:

一个线程局部变量(ThreadLocalvariables)为每个线程方便地提供了一个单独的变量。

ThreadLocal 实例通常作为静态的私有的(privatestatic)字段出现在一个类中,这个类用来关联一个线程。

当多个线程访问ThreadLocal 实例时,每个线程维护 ThreadLocal 提供的独立的变量副本。

常用的使用可在DAO 模式中见到,当 DAO 类作为一个单例类时,数据库链接(connection)被每一个线程独立的维护,互不影响。(基于线程的单例)

ThreadLocal 难于理解,下面这些引用连接有助于你更好的理解它。

Good article on ThreadLocal on IBMDeveloperWorks 》、《理解 ThreadLocal》、《Managing data : Good example》、《Refer Java API Docs

5.什么时候抛出InvalidMonitorStateException 异常,为什么?

调用wait ()/notify ()/notifyAll ()中的任何一个方法时,如果当前线程没有获得该对象的锁,那么就会抛出 IllegalMonitorStateException 的异常(也就是说程序在没有执行对象的任何同步块或者同步方法时,仍然尝试调用 wait ()/notify ()/notifyAll ())。由于该异常是 RuntimeExcpetion 的子类,所以该异常不一定要捕获(尽管你可以捕获只要你愿意).作为 RuntimeException,此类异常不会在wait (),notify (),notifyAll ()的方法签名提及。

6.Sleep ()suspend () wait ()之间有什么区别?

Thread.sleep ()使当前线程在指定的时间处于非运行Not Runnable)状态。线程一直持有对象的监视器。比如一个线程当前在一个同步块或同步方法中,其它线程不能进入该块或方法中。如果另一线程调用了 interrupt ()方法,它将唤醒那个睡眠的线程。

注意:sleep()是一个静态方法。这意味着只对当前线程有效,一个常见的错误是调用t.sleep (),(这里的t是一个不同于当前线程的线程)。即便是执行t.sleep (),也是当前线程进入睡眠,而不是t线程。t.suspend ()是过时的方法,使用 suspend ()导致线程进入停滞状态,该线程会一直持有对象的监视器,suspend ()容易引起死锁问题。

object.wait ()使当前线程出于不可运行状态,和 sleep ()不同的是 wait object 的方法而不是 thread。调用 object.wait ()时,线程先要获取这个对象的对象锁,当前线程必须在锁对象保持同步,把当前线程添加到等待队列中,随后另一线程可以同步同一个对象锁来调用 object.notify (),这样将唤醒原来等待中的线程,然后释放该锁。基本上 wait ()/notify () sleep ()/interrupt ()类似,只是前者需要获取对象锁。

7.在静态方法上使用同步时会发生什么事?

同步静态方法时会获取该类的“Class”对象,所以当一个线程进入同步的静态方法中时,线程监视器获取类本身的对象锁,其它线程不能进入这个类的任何静态同步方法。它不像实例方法,因为多个线程可以同时访问不同实例同步实例方法。

8.当一个同步方法已经执行,线程能够调用对象上的非同步实例方法吗?

可以,一个非同步方法总是可以被调用而不会有任何问题。实际上,Java 没有为非同步方法做任何检查,锁对象仅仅在同步方法或者同步代码块中检查。如果一个方法没有声明为同步,即使你在使用共享数据 Java 照样会调用,而不会做检查是否安全,所以在这种情况下要特别小心。一个方法是否声明为同步取决于临界区访问(critial section access),如果方法不访问临界区(共享资源或者数据结构)就没必要声明为同步的。

下面有一个示例说明:Common 类有两个方法 synchronizedMethod1() method1()MyThread 类在独立的线程中调用这两个方法。

  1. public class Common {  
  2.    
  3. public synchronized void synchronizedMethod1() {  
  4. System.out.println ("synchronizedMethod1 called");  
  5. try {  
  6. Thread.sleep (1000);  
  7. } catch (InterruptedException e) {  
  8. e.printStackTrace ();  
  9. }  
  10. System.out.println ("synchronizedMethod1 done");  
  11. }  
  12. public void method1() {  
  13. System.out.println ("Method 1 called");  
  14. try {  
  15. Thread.sleep (1000);  
  16. } catch (InterruptedException e) {  
  17. e.printStackTrace ();  
  18. }  
  19. System.out.println ("Method 1 done");  
  20. }  
  1. public class MyThread extends Thread {  
  2. private int id = 0;  
  3. private Common common;  
  4.    
  5. public MyThread (String name, int no, Common object) {  
  6. super(name);  
  7. common = object;  
  8. id = no;  
  9. }  
  10.    
  11. public void run () {  
  12. System.out.println ("Running Thread" + this.getName ());  
  13. try {  
  14. if (id == 0) {  
  15. common.synchronizedMethod1();  
  16. } else {  
  17. common.method1();  
  18. }  
  19. } catch (Exception e) {  
  20. e.printStackTrace ();  
  21. }  
  22. }  
  23.    
  24. public static void main (String[] args) {  
  25. Common c = new Common ();  
  26. MyThread t1 = new MyThread ("MyThread-1", 0, c);  
  27. MyThread t2 = new MyThread ("MyThread-2", 1, c);  
  28. t1.start ();  
  29. t2.start ();  
  30. }  
  31. }  

这里是程序的输出:

  1. Running ThreadMyThread-1  
  2. synchronizedMethod1 called  
  3. Running ThreadMyThread-2  
  4. Method 1 called  
  5. synchronizedMethod1 done  
  6. Method 1 done 

 

结果表明即使synchronizedMethod1()方法执行了,method1()也会被调用。

9.在一个对象上两个线程可以调用两个不同的同步实例方法吗?

不能,因为一个对象已经同步了实例方法,线程获取了对象的对象锁。所以只有执行完该方法释放对象锁后才能执行其它同步方法。看下面代码示例非常清晰:Common 类有 synchronizedMethod1() synchronizedMethod2()方法,MyThread 调用这两个方法。

  1. public class Common {  
  2. public synchronized void synchronizedMethod1() {  
  3. System.out.println ("synchronizedMethod1 called");  
  4. try {  
  5. Thread.sleep (1000);  
  6. } catch (InterruptedException e) {  
  7. e.printStackTrace ();  
  8. }  
  9. System.out.println ("synchronizedMethod1 done");  
  10. }  
  11.    
  12. public synchronized void synchronizedMethod2() {  
  13. System.out.println ("synchronizedMethod2 called");  
  14. try {  
  15. Thread.sleep (1000);  
  16. } catch (InterruptedException e) {  
  17. e.printStackTrace ();  
  18. }  
  19. System.out.println ("synchronizedMethod2 done");  
  20. }  
  1. public class MyThread extends Thread {  
  2. private int id = 0;  
  3. private Common common;  
  4.    
  5. public MyThread (String name, int no, Common object) {  
  6. super(name);  
  7. common = object;  
  8. id = no;  
  9. }  
  10.    
  11. public void run () {  
  12. System.out.println ("Running Thread" + this.getName ());  
  13. try {  
  14. if (id == 0) {  
  15. common.synchronizedMethod1();  
  16. } else {  
  17. common.synchronizedMethod2();  
  18. }  
  19. } catch (Exception e) {  
  20. e.printStackTrace ();  
  21. }  
  22. }  
  23.    
  24. public static void main (String[] args) {  
  25. Common c = new Common ();  
  26. MyThread t1 = new MyThread ("MyThread-1", 0, c);  
  27. MyThread t2 = new MyThread ("MyThread-2", 1, c);  
  28. t1.start ();  
  29. t2.start ();  
  30. }  

10.什么是死锁

死锁就是两个或两个以上的线程被无限的阻塞,线程之间相互等待所需资源。这种情况可能发生在当两个线程尝试获取其它资源的锁,而每个线程又陷入无限等待其它资源锁的释放,除非一个用户进程被终止。就 JavaAPI 而言,线程死锁可能发生在一下情况。

·        当两个线程相互调用 Thread.join ()

·        当两个线程使用嵌套的同步块,一个线程占用了另外一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。

11.什么是线程饿死,什么是活锁?

线程饿死和活锁虽然不想是死锁一样的常见问题,但是对于并发编程的设计者来说就像一次邂逅一样。

当所有线程阻塞,或者由于需要的资源无效而不能处理,不存在非阻塞线程使资源可用。JavaAPI 中线程活锁可能发生在以下情形:

·        当所有线程在程序中执行 Object.wait (0),参数为 0 wait 方法。程序将发生活锁直到在相应的对象上有线程调用 Object.notify ()或者 Object.notifyAll ()

·        当所有线程卡在无限循环中。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值