伪共享问题很难被发现,因为线程可能访问完全不同的全局变量,内存中却碰巧在很相近的位置上。如其他诸多的并发问题,避免伪共享的最基本方式是仔细审查代码,根据缓存行来调整你的数据结构。
16)用 wait-notify 写一段代码来解决生产者-消费者问题?解析:这是常考的基础类型的题,只要记住在同步块中调用 wait() 和 notify()方法,如果阻塞,通过循环来测试等待条件。
答:
import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; /** * Java program to solve Producer Consumer problem using wait and notify * method in Java. Producer Consumer is also a popular concurrency design pattern. * * @author Javin Paul */ public class ProducerConsumerSolution { public static void main(String args[]) { Vector sharedQueue = new Vector(); int size = 4; Thread prodThread = new Thread(new Producer(sharedQueue, size), "Producer"); Thread consThread = new Thread(new Consumer(sharedQueue, size), "Consumer"); prodThread.start(); consThread.start(); } } class Producer implements Runnable { private final Vector sharedQueue; private final int SIZE; public Producer(Vector sharedQueue, int size) { this.sharedQueue = sharedQueue; this.SIZE = size; } @Override public void run() { for (int i = 0; i < 7; i++) { System.out.println("Produced: " + i); try { produce(i); } catch (InterruptedException ex) { Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, ex); } } } private void produce(int i) throws InterruptedException { // wait if queue is full while (sharedQueue.size() == SIZE) { synchronized (sharedQueue) { System.out.println("Queue is full " + Thread.currentThread().getName() + " is waiting , size: " + sharedQueue.size()); sharedQueue.wait(); } } // producing element and notify consumers synchronized (sharedQueue) { sharedQueue.add(i); sharedQueue.notifyAll(); } } } class Consumer implements Runnable { private final Vector sharedQueue; private final int SIZE; public Consumer(Vector sharedQueue, int size) { this.sharedQueue = sharedQueue; this.SIZE = size; } @Override public void run() { while (true) { try { System.out.println("Consumed: " + consume()); Thread.sleep(50); } catch (InterruptedException ex) { Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex); } } } private int consume() throws InterruptedException { // wait if queue is empty while (sharedQueue.isEmpty()) { synchronized (sharedQueue) { System.out.println("Queue is empty " + Thread.currentThread().getName() + " is waiting , size: " + sharedQueue.size()); sharedQueue.wait(); } } // Otherwise consume element and notify waiting producer synchronized (sharedQueue) { sharedQueue.notifyAll(); return (Integer) sharedQueue.remove(0); } } } Output: Produced: 0 Queue is empty Consumer is waiting , size: 0 Produced: 1 Consumed: 0 Produced: 2 Produced: 3 Produced: 4 Produced: 5 Queue is full Producer is waiting , size: 4 Consumed: 1 Produced: 6 Queue is full Producer is waiting , size: 4 Consumed: 2 Consumed: 3 Consumed: 4 Consumed: 5 Consumed: 6 Queue is empty Consumer is waiting , size: 0 17)用 Java 写一个线程安全的单例模式(Singleton)?解析:有多种方法,但重点掌握的是双重校验锁。
答:
1.饿汉式单例
饿汉式单例是指在方法调用前,实例就已经创建好了。下面是实现代码:
public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } }2.加入 synchronized 的懒汉式单例
所谓懒汉式单例模式就是在调用的时候才去创建这个实例,我们在对外的创建实例方法上加如 synchronized 关键字保证其在多线程中很好的工作:
public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }3.使用静态内部类的方式创建单例
这种方式利用了 classloder 的机制来保证初始化 instance 时只有一个线程,它跟饿汉式的区别是:饿汉式只要 Singleton 类被加载了,那么 instance 就会被实例化(没有达到 lazy loading 的效果),而这种方式是 Singleton 类被加载了,instance 不一定被初始化。只有显式通过调用 getInstance() 方法时才会显式装载 SingletonHoder 类,从而实例化 singleton
public class Singleton { private Singleton() { } private static class SingletonHolder {// 静态内部类 private static Singleton singleton = new Singleton(); } public static Singleton getInstance() { return SingletonHolder.singleton; } }4.双重校验锁