并发编程常见面试题以及答案

1、现在有 T1、T2、T3 三个线程,你怎样保证 T2 在 T1 执行完后执行,T3 在 T2 执行完后执行?

考察 Thread.join,还有其他concurrent包下工具类的了解。

方法一:可以使用 join 方法实现等待;

方法二:可以使用 CompletableFuture 的 thenCombine 方法

public class TestOrder {

    public static void main(String[] args) {
        Thread thread1 = new Thread() {
            @Override
            public void run() {
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread 1");
            }
        };
        thread1.start();
        try {
            thread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Thread thread2 = new Thread() {
            @Override
            public void run() {
                try {
                    sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread 2");
            }
        };
        thread2.start();
        try {
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Thread thread3 = new Thread() {
            @Override
            public void run() {
                try {
                    sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread 3");
            }
        };
        thread3.start();

    }
}

2、在 java 中 wait 和 sleep 方法的不同?

sleep 是 Thread 类的方法;wait是 Object 的方法。

此外,wait 期间会释放锁,sleep期间会持有锁。wait通常用于线程交互,sleep一般用于暂停执行。

3、什么是竞争条件?你怎样发现和解决竞争?

在并发编程中,由于不恰当的执行时序而出现不正确的结果是一种非常重要的情况,它的正式名字就叫竞态条件。

或者可以说:当某个计算的正确性取决于多个线程的交替执行时序时,那么就会发生竞态条件。

换句话说,就是正确的结果取决于运气。

发生场景:最常见的场景就是 “先检查再执行”

例子:

public ExpensiveObject getInstance() {
    // 先检查后执行,这种场景下会发生竞态条件
    if (instance == null) {
        instance = new ExpensiveObject();
    }
    return instance;
}

4、Java 中你怎样停止或者取消一个线程?

如果不是因为IO阻塞,常见的

https://blog.csdn.net/Prepared/article/details/114164117

5、什么是不可变对象,它对写并发应用有什么帮助?

满足以下条件时,对象才是不可变的:

1、对象创建以后其状态就不能修改;

2、对象的所有域都是final类型;

3、对象是正确创建的(在对象的创建期间,this引用没有逸出)

说明:即使对象中的所有的域都是final类型的,这个对象也仍然是可变的。

6、用 Java 编程一个会导致死锁的程序,你将怎么解决?

两个线程,线程A持有资源C,等待持有资源D;线程B持有资源D,等待持有资源C。

一个常见的场景是转账:银行A,持有转入账户,等待持有转出账户;银行B,持有转出账户,等待持有转入账户。

以下是一个例子,互相持有对方持有的锁

public class Test1001LeftRightDeadLock {

    private final Object left = new Object();

    private final Object right = new Object();

    public void leftRight() {
        synchronized (left) {
            doSomething();
        }
    }

    private void doSomething() {
    }

    private void doSomethingElse() {
    }

    public void rightLeft() {
        synchronized (right) {
            doSomethingElse();
        }
    }
}

7、什么是原子操作,Java 中的原子操作是什么?

假定有两个操作A和B,如果从执行A的线程来看,当另一个线程执行B时,要么将B全部执行完,要不完全不执行B,那么A和B对彼此来说就是原子的。原子操作是指,对于访问同一个状态的所有操作(包括该操作本身)来说,这个操作是一个以原子方式执行的操作,

举例:count++

不是原子操作,先要获取count的值,然后执行+1的操作。

其中获取值是原子操作。

8、Java 中的 volatile 关键是什么作用?怎样使用它?在 Java 中它跟 synchronized 方法有什么不同?

volatile 有两个作用,一个是禁止指令重排序,另一是保证可见性。

synchronized 除了这两个作用之外,还可以锁住资源,限制资源同时只能有一个线程访问。

9、为什么我们调用 start()方法时会执行 run()方法,为什么我们不能直接调用 run()方法?

当你调用 start()方法时你将创建新的线程,并且执行在 run()方法里的代码。
但是如果你直接调用 run()方法,它不会创建新的线程也不会执行调用线程的代码。

10、用 Java 实现阻塞队列

入队操作,如果队列已满,需要等待队列不满,使用await方法;入队之后,通知出队线程;

出队操作,如果队列为空,需要等待队列不空,使用await方法;出队之后,通知入队线程;

public class BlockedQueue<T> {

	final Lock lock = new ReentrantLock();
	// 条件变量:队列不满
	final Condition notFull = lock.newCondition();
	// 条件变量:队列不空
	final Condition notEmpty = lock.newCondition();

	// 入队
	void enq(T x) {
		lock.lock();
		try {
			//  队列已满
			boolean flag = true;
			while (flag) {
				// 等待队列不满
				notFull.await();
			}
			// 省略入队操作...
			//入队后,通知可出队
			notEmpty.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}

	// 出队
	void deq() {
		lock.lock();
		try {
			//  队列已空
			boolean flag = true;
			while (flag) {
				// 等待队列不空
				notEmpty.await();
			}
			// 省略出队操作...
			//出队后,通知可入队
			notFull.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			lock.unlock();
		}
	}
}

猜你喜欢

转载自blog.csdn.net/Prepared/article/details/114803155