java多线程问题

来源:互联网 发布:上海数据交易中心薪资 编辑:程序博客网 时间:2024/05/22 12:35

(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的构造函数来初始化instance的成员变量

现在想象一下有线程A和B在调用getInstance,线程A进入,在执行步骤1的时候就踢出了Cpu.然后线程B进入, B看到的是instance已经不是null了,(内存已经分配),于是它开始放心地使用instance,但这个是错误的,因为在这一时刻,instance的成员变量还都是缺省值,A还没来得及执行步骤2来完成instance的初始化。

当然编译器也可以这样实现:

1,temp = 分配内存

2,调用temp的构造函数

3,instance = temp

如果编译器的行为是这样的话我们似乎就没有问题了,但事实却不是那么简单,因为我们无法知道某个编译器具体是怎么做的。

双检锁对于基础类型(int)适用,因为基础类型没有调用构造函数这一步。




0 0
原创粉丝点击