Java面试编程相关题目

1. 实现单例模式

首先要明白什么是单例模式,简单来说假如有类 A, 当我们 new 一个 A 的对象的时候,如果是单例模式,先判断是否已经有这个对象了,如果有就返回,没有再创建。这相当于将一个对象和一个类绑定了,该怎么做呢?我们知道,在Java中,如果将一个属性或方法声明为 static 的,那么这个属性或方法就是属于这个类的了,与类绑定了。那办法就呼之欲出了,我们在类中声明一个自身的对象,声明为static的,同时不允许做出修改,加上final关键字。

除此之外,单例模式有两种实现方式,即 饿汉模式 与 懒汉模式。饿汉模式是在类加载的时候直接创建对象,懒汉模式就是当第一次 new 类 A 的对象的时候,才去创建唯一实例。

从以上的描述不难看出,如果是饿汉模式,我们直接将属性初始化即可,如果是懒汉模式,就需要在构造函数中检查是否已经存在相应的对象了,因为要保证单例,当已经存在的时候就不能够再创建了。除此之外,无论是单例还是多例,都需要为外界提供一个 getInstance 函数来获得对象。

饿汉模式

public class Singleton{
	private static final Singleton instance = new Singleton();
	private Singleton(){}
	public static Singleton getInstance(){
		return instance;
	}
}

懒汉模式(方式一)

public class Singleton{
	private static final Singleton instance;
	private Singleton(){}
	public static synchronized Singleton getInstance(){
		if(instance == null){
			this.instance = new Singleton();
		}
		return instance;
	}
}

懒汉模式(方式二:双重校验)

public class Singleton{
	private static final Singleton instance;
	private Singleton(){}
	public static Singleton getInstance(){
		if(instance == null){
			synchronized (Singleton.class){
				if(instance == null){
					this.instance = new Singleton();
				}
			}
		}
		return instance;
	}
}
  • 为什么懒汉模式要加锁?
    因为多线程可能会多次创建,因为要保证单例,所以必须加锁。
  • 为什么要用双重校验?
    因为第一种方式直接在方法上加锁,过于笨重,当创建了实例之后,后面的每一次访问,其实不用创建,但是还是要被加锁,用第二种的对代码块加锁,更加的灵活,更加的符合场景。
  • 为什么双重校验方法里,synchronized 关键字的代码块中还要检验一次?
    同样是为了避免多线程多次创建,如果创建之前不检查,可能别的线程也创建了。
  • 双重校验有什么好处?
    获取对象无需加锁,更加符合场景;创建过程线程安全。

2. 实现三个线程交替打印1-120

这个问题考察的是线程之间的通信,线程之间通信的手段可以通过系统提供的线程间通信方法配合锁、信号量等方法实现,方法很多,会两到三个即可。
方法一:synchronized关键字+wait()+notifyAll()

public class PrintNumber{
    private int num;
    private static final Object lock = new Object();
    public void print(int order){
        while(true){
            synchronized(lock){
                if(num >= 10){break;}
                while(num % 3 == order){
                    try{
                        System.out.println(Thread.currentThread().getName()+num);
                        num++;
                        lock.notifyAll();
                        lock.wait();
                    }catch(Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    public static void main(String[]args){
        PrintNumber test = new PrintNumber();
        Thread t0 = new Thread(()->{test.print(0);},"A");
        Thread t1 = new Thread(()->{test.print(1);},"B");
        Thread t2 = new Thread(()->{test.print(2);},"C");

        t0.start();
        t1.start();
        t2.start();

    }
}

方法二:RentrantLock

import java.util.concurrent.locks.ReentrantLock;

public class Main{
        private static int num;
        private ReentrantLock lock = new ReentrantLock();

        public void print(int order){
            while (true){
                lock.lock();
                if(num >= 40){
                    break;
                }
                if(num % 3 == order){
                    System.out.println(Thread.currentThread().getName()+num);
                    num++;
                }
                lock.unlock();
            }
        }
        public static void main(String[]args){
                Main test = new Main();

                Thread t0 = new Thread(()->{test.print(0);},"A");
                Thread t1 = new Thread(()->{test.print(1);},"B");
                Thread t2 = new Thread(()->{test.print(2);},"C");

                t0.start();
                t1.start();
                t2.start();
        }
}

方法三:Semaphore信号量

3. 实现一个线程池

定义 Runnable 线程

import java.util.Date;

/**
 * 这是一个简单的Runnable类,需要大约5秒钟来执行其任务。
 * @author shuang.kou
 */
public class MyRunnable implements Runnable {

    private String command;

    public MyRunnable(String s) {
        this.command = s;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " Start. Time = " + new Date());
        processCommand();
        System.out.println(Thread.currentThread().getName() + " End. Time = " + new Date());
    }

    private void processCommand() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String toString() {
        return this.command;
    }
}


定义线程池

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolExecutorDemo {

    private static final int CORE_POOL_SIZE = 5;
    private static final int MAX_POOL_SIZE = 10;
    private static final int QUEUE_CAPACITY = 100;
    private static final Long KEEP_ALIVE_TIME = 1L;
    public static void main(String[] args) {

        //使用阿里巴巴推荐的创建线程池的方式
        //通过ThreadPoolExecutor构造函数自定义参数创建
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(QUEUE_CAPACITY),
                new ThreadPoolExecutor.CallerRunsPolicy());

        for (int i = 0; i < 10; i++) {
            //创建WorkerThread对象(WorkerThread类实现了Runnable 接口)
            Runnable worker = new MyRunnable("" + i);
            //执行Runnable
            executor.execute(worker);
        }
        //终止线程池
        executor.shutdown();
        while (!executor.isTerminated()) {
        }
        System.out.println("Finished all threads");
    }
}


你可能感兴趣的:(java,面试,单例模式)