1.当单例模式遇到多线程
单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。
但是如果在多线程并发执行情况下,可能会出现多个实例的现象,解决办法如下:
//懒汉式
public class Single2 {
private static Single2 s = null;
private Single2() {
}
public static Single2 getInstance() {
if (s == null) {
synchronized (Single2.class) {
if (s == null) {
s = new Single2();
}
}
}
return s;
}
}
当然对于饿汉式,不存在这个问题;
//饿汉式
public class Single {
private static final Single s = new Single();
private Single(){}
public static Single getInstance(){
return s;
}
}
2.多线程间通信(生产者和消费者问题)
如果程序中只有2个线程,一个负责生产,一个负责消费,那么如下就可以解决:
public class Resource {
private String name;
private String sex;
private boolean flag;
public synchronized void set(String name, String sex) {
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void out() {
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(this.name + " " + this.sex);
flag = false;
this.notify();
}
public boolean getFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
但是如果有多个生产线程和消费线程的时候,上面的做法就不能正确执行了,还可能会出现死锁的现象。可以采取一下的方式:
public synchronized void set(String name, String sex) {
while (flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name = name;
this.sex = sex;
flag = true;
this.notifyAll();
}
public synchronized void out() {
while (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(this.name + " " + this.sex);
flag = false;
this.notifyAll();
}
采用while防止线程被唤醒后不去判断Flag而直接向下执行, 为了保证一定能唤醒需要的线程,采取notifyAll()方式,将线程池中所有被wait的线程都唤醒
但是这样会带来新的难题,如果每次都把所有被wait的线程唤醒,很影响执行效率。
为此在JDK1.5以后,有了新的处理方式:
接口 Lock
Lock
实现提供了比使用synchronized
方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的
Condition对象。
Condition
将Object
监视器方法(wait
、notify
和 notifyAll
)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待
set(wait-set)。其中,Lock
替代了synchronized
方法和语句的使用,Condition
替代了 Object 监视器方法的使用。
使用也十分方便,可以改成如下:
public class Res {
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition pro_con = lock.newCondition();
private Condition cum_con = lock.newCondition();
public void set(String name) {
lock.lock();
try {
while (flag) {
try {
pro_con.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName() + "produce"
+ this.name);
flag = true;
cum_con.signal();
} catch (Exception e) {
// TODO: handle exception
} finally {
lock.unlock();
}
}
public void out() {
lock.lock();
try {
while (!flag) {
try {
cum_con.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "consume"
+ this.name);
flag = false;
pro_con.signal();
} catch (Exception e) {
// TODO: handle exception
} finally {
lock.unlock();
}
}
}
在查看JDK时候看到下面代码,对数组的多线程处理,很有思路
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}