面试笔试经典

一、生产者消费者模式

思路:

两类线程:
生产者线程生产共享数据,消费者线程消费共享数据。
当共享数据满时,阻塞生产者继续生产数据放置入内;
当共享数据为空时,阻塞消费者继续消费数据。

1.使用Object的wait/notify的消息通知机制;
2.使用Lock的Condition的await/signal的消息通知机制。

注意点:1. while(条件判断) ,2. wait() / notifyAll() await() / signalAll()

模板:

// The standard idiom for calling the wait method in Java
synchronized (sharedObject) {
    while (condition) {
    	sharedObject.wait();   
        // (Releases lock, and reacquires on wakeup)
    }
    // do action based upon condition e.g. take or put into queue
}

1.1 synchronized wait() / notifyAll() 代码:

import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ProducerConsumer {

    private static LinkedList<Integer> linkedList = new LinkedList<>();

    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(15);
        for (int i = 0; i < 5; i++) {
            service.submit(new Producer(linkedList, 8));
        }

        for (int i = 0; i < 8; i++) {
            service.submit(new Consumer(linkedList));
        }
    }



    static class Producer implements Runnable {

        private LinkedList<Integer> list;
        private int maxLength;

        public Producer(LinkedList<Integer> list, int maxLength) {
            this.list = list;
            this.maxLength = maxLength;
        }

        @Override
        public void run() {
            while (true) {
                synchronized (list) {
                    try {
                        while (list.size() == maxLength) {
                            list.wait();
                        }

                        Random random = new Random();
                        int element = random.nextInt();
                        list.add(element);
                        System.out.println("Producer " + Thread.currentThread().getName() + " " + element);

                        list.notifyAll();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    static class Consumer implements Runnable {

        private List<Integer> list;

        public Consumer(List<Integer> list) {
            this.list = list;
        }

        @Override
        public void run() {
            while (true) {
                synchronized (list) {
                    try {
                        while (list.size() == 0) {
                            list.wait();
                        }

                        int element = list.remove(0);
                        System.out.println("Consumer " + Thread.currentThread().getName() + " " + element);

                        list.notifyAll();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

}

面试笔试经典_第1张图片
1.2 lock await() / signalAll() 代码

lock.lock
try{…
}catch(){
…业务代码
}finally{
lock.unlock()
}

import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ProducerConsumer {

    private static Lock lock = new ReentrantLock();
    private static Condition full = lock.newCondition();
    private static Condition empty = lock.newCondition();

    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();

        ExecutorService service = Executors.newFixedThreadPool(15);
        for (int i = 0; i < 5; i++) {
            service.submit(new Producer(list, 8, lock));
        }

        for (int i = 0; i < 10; i++) {
            service.submit(new Consumer(list, lock));
        }


    }

    static class Producer implements Runnable {
        List<Integer> list;
        int maxLength;
        Lock lock;

        public Producer(List list, int maxLength, Lock lock) {
            this.list = list;
            this.maxLength = maxLength;
            this.lock = lock;
        }

        @Override
        public void run() {
            while (true) {
                lock.lock();
                try {
                    while (list.size() == maxLength) {
                        full.await();
                    }

                    Random random = new Random();
                    int element = random.nextInt();
                    list.add(element);
                    System.out.println("Producer " + Thread.currentThread().getName() + element);

                    empty.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }


    static class Consumer implements Runnable {
        List<Integer> list;
        Lock lock;

        public Consumer(List<Integer> list, Lock lock) {
            this.lock = lock;
            this.list = list;
        }

        @Override
        public void run() {
            while (true) {
                lock.lock();

                try {
                    while (list.size() == 0) {
                        empty.await();
                    }

                    int element = list.remove(0);
                    System.out.println("Consumer " + Thread.currentThread().getName() + element);

                    full.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        }
    }
}

面试笔试经典_第2张图片


二、按序打印

Lock condition

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SequencePrint {

    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printA();
            }
        }, "A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printB();
            }
        }, "B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                data.printC();
            }
        }, "C").start();

    }
}

class Data {

    Lock lock = new ReentrantLock();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();
    Condition condition3 = lock.newCondition();
    int num = 1;

    public void printA() {
        lock.lock();
        try {
            while (num != 1) {
                condition1.await();
            }

            num = 2;
            System.out.println(Thread.currentThread().getName() +
                    "=> AAAAAAAAA");
            condition2.signal();
        }catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void printB() {
        lock.lock();
        try {
            while (num != 2) {
                condition2.await();
            }
            num = 3;
            System.out.println(Thread.currentThread().getName() +
                    "=> BBBBBBBBB");
            condition3.signal();
        }catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void printC() {
        lock.lock();
        try {
            while (num != 3) {
                condition3.await();
            }
            num = 1;
            System.out.println(Thread.currentThread().getName() +
                    "=> CCCCCCCCC");
            condition1.signal();
        }catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

A=> AAAAAAAAA
B=> BBBBBBBBB
C=> CCCCCCCCC
A=> AAAAAAAAA
B=> BBBBBBBBB
C=> CCCCCCCCC
A=> AAAAAAAAA
B=> BBBBBBBBB
C=> CCCCCCCCC
A=> AAAAAAAAA
B=> BBBBBBBBB
C=> CCCCCCCCC
A=> AAAAAAAAA
B=> BBBBBBBBB
C=> CCCCCCCCC
A=> AAAAAAAAA
B=> BBBBBBBBB
C=> CCCCCCCCC
A=> AAAAAAAAA
B=> BBBBBBBBB
C=> CCCCCCCCC
A=> AAAAAAAAA
B=> BBBBBBBBB
C=> CCCCCCCCC
A=> AAAAAAAAA
B=> BBBBBBBBB
C=> CCCCCCCCC
A=> AAAAAAAAA
B=> BBBBBBBBB
C=> CCCCCCCCC

三、单例

1. 懒汉

// 问题1: 创建多个singleton
// 多个线程判断为null, 由于singleton没有被volatile修饰,
// 所以当一个线程已经new 了一个singleton, 其他线程不可见,
// 获得锁后,依然会new Singleton(); 创建多个singleton  

// 问题2: 指令重排
// singleton = new Singleton();  
// 三步: 分配内存空间; init初始化; singleton指向内存空间
// 指令重排 分配内存空间; singleton指向内存空间;init初始化
// 造成singleton != null, 但是没有初始化
// 所以会创建,没有初始化的单例对象

// 加在方法上又太笨重,获取和创建singleton都要阻塞等待。
public class LazyMan {

    private LazyMan() {};

    private static LazyMan singleton;

    public static LazyMan getInstance() {
        if(singleton == null) {
            synchronized (LazyMan.class) {
                singleton = new LazyMan();
            }
        }
        return singleton;
    }
}

50个线程打印得到的单例对象:
面试笔试经典_第3张图片

2 DCL双重校验锁

双重校验null:
第一重:提高效率,获取线程不需要加锁,只有创建线程的时候加锁。
第二重:以前singleton没有加volatile时,多个线程进入代码,判断singleton为null, 其中一个线程获取锁,new单例对象,退出程序,释放锁。由于singleton对其他线程不可见,所以线程依然认为singleton为null, 又创建了一个单例对象。这里已经有了volatile,本不需要,安全起见,增加了null判断。

volatile: 禁止指令重排,因为已经有了第二层判断null, 所以这里的volatile主要作用是禁止指令重排。

public class DCLLazyMan {

    private DCLLazyMan() {}

    private volatile static DCLLazyMan singleton;
    
    public static DCLLazyMan getInstance() {
        if (singleton == null) {
            synchronized (DCLLazyMan.class) {
                if(singleton == null) {
                    singleton = new DCLLazyMan();
                }
            }
        }
        
        return singleton;
    }
}
public class Hungry {

    private Hungry() {}
    
    private static final Hungry singleton = new Hungry();
    
    public static Hungry getInstance() {
        return singleton;
    }
}

3 饿汉

类加载时,new出来的单例对象,不会有线程安全的问题

public class Hungry {

    private Hungry() {}

    private static final Hungry singleton = new Hungry();

    public static Hungry getInstance() {
        return singleton;
    }
}

4 内部类

延迟加载:加载Holder类时,不会加载其静态内部类,用到时才加载。
得到单例对象时,是加载静态内部类时,生成的单例对象,没有线程安全问题。

public class Holder {

    private Holder() {}

    public static class InnerClass {
        private static final Holder HOLDER = new Holder();
    }

    public static Holder getInstance() {
        return InnerClass.HOLDER;
    }
}

5枚举

枚举也是一个类,默认是单例的,且不允许通过反射创建枚举类对象,所以是最好的单例模式

public enum EnumSingleton {

    INSTANCE;
    
    public EnumSingleton getInstance() {
        return INSTANCE;
    }
    
}

你可能感兴趣的:(Java基础)