您的位置:首页 > 编程语言 > Java开发

java多线程问题

2016-08-19 11:18 169 查看
(1)Java多线程中调用wait()和sleep()方法有什么不同?

Java程序中wait和sleep都会造成某种形式的暂停,它们可以满足不同的需要。wait()方法用于线程间通信,如果等待条件为真且其他线程被唤醒时它会释放锁,而sleep()方法仅仅释放cpu资源或者让当前线程停止执行一段时间,但不会释放锁。

(2)如何强制启动一个线程

这个问题就像是如何强制进行java垃圾回收,目前还没有方法,虽然你可以使用System.gc()来进行垃圾回收,但不保证能成功。在Java里面没有办法强制启动一个线程,它是被线程调度器控制着且java没有公布相关的API.

(3)写出3条你遵循的多线程最佳实践

1,避免锁定和缩小同步的范围

所花费的代价高昂且上下文切换更耗费时间空间,试试最低限度的使用同步和锁,缩小临界区。因此相对于同步方法我更喜欢同步块,它给我拥有对锁的绝对控制权。

2,多用同步类少用wait和notify

Semaphore同步类简化了编码操作,而用wait和notify很难实现对复杂控制流的控制。

3,多用并发集合少用同步集合

并发集合比同步集合的扩展性更好,所以在并发编程时使用并发集合效果更好。

(4)单例模式的双检锁是什么?

单例模式的几种写法

1)饿汉式单例类

public class Singleton

{

private Singleton() { }

private static Singleton instance = new Singleton();

private static Singleton getInstance(){

return instance;

}

}

饿汉式提前 实例化,没有懒汉式多线程问题,但不管我们是不是调用getInstance,都会存在一个实例在内存中。

2)内部类单例类

public class Singleton{

private Singleton(){ }

private class singletonHolder(){

private static Singleton instance = new Singleton();

}

private static Singleton getInstance(){

return SingletonHolder.instance;

}

}

内部类式中,实现了延迟加载,只有我们调用了getInstance(),才会创建唯一的实例到内存中,并且也解决了懒汉式中多线程的问题,解决的方法是利用了Classloader的特性。

3)懒汉式单例类

public class Singleton{

private Singleton(){ }

private static Singleton instance;

public static Singleton getInstance(){

if(instance == null){

return instance = new Singleton();

}else{

return instance;

}

}

}

在懒汉式中,有线程A和B,当线程A运行到第4行时,跳到线程B,当B也运行到4行时,两个线程的instance都为空,这样就会生成两个实例。解决的办法是同步:

可以同步但是效率不高:

public class Singleton{
private Singleton(){ }
private static Singleton instance;
public static synchroniced Singleton getInstance(){
if(instance == null){
return instance = new Singleton();
}else{
return instance;
}
}
}
这样写程序不会出错,因为整个getInstance()是一个整体的“critical section”,但就是效率不好,因为我们的目的其实只是在第一个初始化instance的时候需要locking(加锁),而后面取用instance的时候,根本不需要线程同步。
于是就有了双检锁的写法

4)双检锁的写法

public class Singleton{
private Singleton(){ }
private static Singleton instance;
public static Singleton getInstance(){
if(instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
思路很简单,就是我们只需要同步初始化instance的那部分代码从而使代码既正确又很有效率。
这就是所谓的“双检锁”机制。
很可惜,这样的写法在很多平台上和优化编译器上市错误的。
原因在于:instance= new Singleton()这行代码在不同的编译器上的行为是无法预知的,一个优化编译器可以合法地如下实现instance = new Singleton();
1,instance = 给新的实体分配 内存
2,调用SIngleton的构造函数
4000
来初始化instance的成员变量
现在想象一下有线程A和B在调用getInstance,线程A进入,在执行步骤1的时候就踢出了Cpu.然后线程B进入, B看到的是instance已经不是null了,(内存已经分配),于是它开始放心地使用instance,但这个是错误的,因为在这一时刻,instance的成员变量还都是缺省值,A还没来得及执行步骤2来完成instance的初始化。
当然编译器也可以这样实现:
1,temp = 分配内存
2,调用temp的构造函数
3,instance = temp
如果编译器的行为是这样的话我们似乎就没有问题了,但事实却不是那么简单,因为我们无法知道某个编译器具体是怎么做的。
双检锁对于基础类型(int)适用,因为基础类型没有调用构造函数这一步。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: