<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

一:Java多线程常见问题:
1、为什么使用多线程?
   首先多线程可以增加在多核CPU条件下的处理能力。
   其次能够快速响应用户的GUI请求。
   然后在阻塞I/O的情况下可以异步处理。
   这个接受抬扛,具体的可以看应用场景,但是使用多线程是趋势。

 

2、启动线程的两种方式区别,联系,以及使用的趋势?
   启动线程可以通过继承Thread,实现Runnable接口。
   继承Thread的方式会是类看起来笨重,多些了Thread类的信息,而这些信息可能是该类本身不需要关注的。
   使用Runnable接口方式可以让类再继承别的类,同时我觉得最重要的是标识了实现类是一个可以被驱动的任务。
   继承Thread的方式就好比工厂本身自己制作,加工原料。而实现Runnable的方式是把原料给我运来,让工厂去加工制作。这个所谓的原料就是具体的任务类。
   Java5里面的java.util.concurrency包里,我们可以看到驱动任务以后都由ExecutorService来操作了,它的execute方法需要的参数就是一个Runnable实现类。

 

3、Synchronized的另一种替代方式,lock机制
   Java5中提供了Lock机制,这种比较细粒度的控制并发和互斥,不过对程序员的要求也比较高,Lock机制在抛出异常或者return后不会释放锁,所以使用Lock都是这样的方式:
Try{ lock.lock} finally {lock.unlock}
   Brian Goetz IBM DW上发表一篇文章说,ReentrantLock在高并发的环境下性能和吞吐量是synchronized6倍左右,个人觉得所谓的高并发环境需要如何定义,如果我们十分明显的发现synchronized是我们性能的瓶颈,那么使用比较傻瓜式的synchronized更稳妥些。
4、Tcp并发的数控制在和Cpu个数差不多就好,这个结论来自于Java Tcp/Ip Socket 这本书,如果并发数太多,必定消耗过多的系统资源,导致不能提供正常的服务, 比如可以使用ExecutorService newFixedThreadPoolpoolSize)而不是newCacheThreadPool(),尽管后者在一般情况下是被证明运行良好的。
5、wait(), notify(), notifyAll(),yield(), join(),sleep()方法的区别?
使用的区别我就不说了,如果这个地方搞不懂的话可以补补课了,呵呵。
第一点区别:那就是wait(), notify()notifyAll()地方一定有synchronizedsleep()或者join()方法不一定有synchronized,这是因为有wait(), notify()notifyAll()一定有锁,有锁的话必定有synchronized
第二点区别:wait(), notify(), notifyAll()调用完成以后,都释放对象的锁,而yield(), join(),sleep()这个方法是不释放锁的。
第三点区别:wait(), notify(), notifyAll()Object的方法,而且是final的,大家这个有没有注意呢,所有类默认都有中几个方法,而且直接拿来用就好了,当然要明白怎么用才行。
6、javadoublelong++--都不是原子的操作,如果要使用它们的时候要格外小心。
   对于longdouble变量,jvm把他们作为2个原子性的32位值来对待,而不是一个原子性的64位。++-- 两个操作符是因为jvmload,然后再update,然后store,可见这个不是一个原子的操作。
7、synchronized同步方法的性能要比synchronized同步代码块差,个人觉得这个要看具体情况而定,如果在一个大的方法内仅仅需要同步的是一小部分,那么可以考虑使用同步块,而不必使用同步整个方法从而锁定这个方法所用使用到的对象而降低性能。在方法内使用synchronized(this) synchronized void method() 是同样的效果。
8、Volatile synchronized两个关键字的作用。Volatile保证可见性,不保证可见性,而synchronized即保证可见性又保证原子性。
9、notifyAll()是不是通知在同一个classloader内所有等待的对象。这种说法是不准确的,notifyAll() 通知所有等待这个对象锁的对象。这句话貌似有点模糊。
10、Synchronized 持有对象的锁,即别的线程不能访问该线程所持对象的synchronized方法,只能该线程自己访问该对象的synchronized方法,并且锁的计数加1,等到锁的计数到0的时候释放锁。
11、注意类变量有可能产生critical sectionThinking in java有这样的例子。
12、处理多线程的一般解决办法:
首先找到critical section,即找到线程需要共享访问的对象,变量等。
然后找到读写这些对象,变量的地方,加上synchronized关键字。
13、sleep() 是可以被打断的,synchronized 锁定不能打断,还有IOblock也是不能打断的,Java5 newIO已经可以支持打断的IO
14、ThreadLocalSynchronized区别
之所有说他们俩的区别,因为网上总是拿他们两个做比较,个人认为ThreadLocal是一个对象保存多个副本,Synchronized 是操作一个对象;ThreadLocal操作的对象不涉及到对象的共享互斥,Synchronized主要解决的就是对象的互斥共享
二: Java的内存模型
首先理解两个概念:主存储器和工作存储器。
主存储器对所有的对象都是可见的,工作存储器只对对象自己本身可见。下面画图理解下:

 

 打个的比方:主存储器相当于教室里面的黑板,工作存储器相当于学生手中的笔记本,
 老师相当于java虚拟机,学生相当于线程。
 
 学生(线程)无法直接在黑板(主存储器)上计算,只能先将黑板的内容(需要操作对象的字段)抄写在自己的笔记本(工作存储器)上,然后根据老师(java虚拟机)的调度将内容写到黑板上。学生只能看到自己笔记本上的内容,无法看到别的同学(别的线程)笔记本上的内容,如果想让别的同学看到自己的结果,必须把自己算的结果写到黑板上才行。如果想要别的同学计算的结果,那得要求更新自己笔记本上的内容,要一直保持着最新的状态,因为状态的更新就是别的同学把计算结果更新到黑板了。

 

Synchronized内存同步的工作原理:

 

当线程想要进入Synchronized时,如果工作存储器有存在未映像到主存储器的工作拷贝,该内容就会被强制写入到主存储器(store-->write)的操作,之前的操作结果全部写入到主存储器,其他的线程都能看到所作的更改。如果想要引用到主存的内容,那么执行read-->load操作。这样工作存储器的内容就会和主存同步。

 

Volatile 可见性的原理:
如果某个字段标示为volatile的话,那么就会进行内存的同步。当线程引用volatile字段时,通常会发生从主存到工作存储的拷贝操作;相反,如果将值指定给写着volatile的字段后,工作存储的内容就会直接映像到主存储器。

 

三:进阶的一些读物和我参考的资料

 

1、Think In Java(4th) 21章, 这个有些例子比较拗,看懂需要耐心还得一些基础。
2、Java 多线程设计模式 一个日本的哥们写的,后面的附录和前两章还是不错的,例子有些粗糙。
3、Java 技术手册(5th 这本书里面有Java5 多线程的知识。
4、Effective Java 2nd 这本书说了几个多线程要注意的问题。
5、深入Java虚拟机(2nd)这本书说了监视器的原理。
6、Java TCP/ICP Socket编程 这本书写了怎么使用多线程编写网络程序,用到了Java5的内容。
7、Java Thread 第二版和第三版 老的版本对理解Java1.4的多线程并发还是有好处的。
8、Java 并发编程实践  这本书我没有怎么看,Thinking In Java 作者推荐的。
9、concurrent programming in java: design principles and patterns 这本书是并发的经典大师写的
10IBM DW 上关于多线程的文章都很好,建议大家如果有兴趣可以去研究下。