公众号|松花皮蛋的黑板报|JAVA并发高级
松花皮蛋的黑板报
  • 分享在京东工作的技术感悟,还有JAVA技术和业内最佳实践,大部分都是务实的、能看懂的、可复现的

扫一扫
关注公众号

JAVA并发高级

博客首页文章列表 松花皮蛋me 2020-02-01 17:47


1、锁LOCK


1.1 请说明ReentrantLock和ReentrantReadWriteLock的区别


前面只有互斥,后面有互斥和共享。从Node值可以看出来。

 static final class NonfairSync extends Sync {
        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
//arg其实算是信号量
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

1.2 为什么ReentrantLock是可重入的?

从nonfairTryAcquire中可以看到,通过volatile修饰的state可以做到不需要每次都进行CAS判断,所以它是可重入的。

   final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

 
1.3 Lock#lock和Lock#lockInterruptibly的区别

在使用lock#lockInterruptibly锁时,需要在catch中使用Thread.currentThread().interrupt()显式地恢复中断状态,因为当前线程并未消亡,可能还在存活。

其实凡是抛出lockInterruptibly异常时,中断状态其实都会被清掉。因为很多场景下都会判断中断状态,比如acquireInterruptibly时。

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}
public final void acquireInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (!tryAcquire(arg))
        doAcquireInterruptibly(arg);
}

2、条件变量Condition


2.1 Condition使用场景

2.2 请使用Condition实现“生产者-消费者模型”

2.3 请解释Condition await()和signal() 与Object wait()和notify()的相同和差异?


Thread是一种特殊的Object,JVM Thread回调Java Thread.run()方法,同时Thread提供一些native方法来获取JVM Thread状态,当JVM Thread执行后,自动就notify()了。也就是当线程thread.isAlive()==false时,thread.wait()会被自动释放掉。


3、屏障BARRIER


3.1 请说明CountDownLatch与CyclicBarrier的区别


前者是一次性的,不可循环的。后者可以通过reset(不推荐)重置计数。

public class CyclicBarrierQuestion {
    public static void main(String[] args) throws InterruptedException {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 20; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    action();
                    try {
                        //先计数减一,再判断是否应该阻塞。
                        //当计数>0的时候,才会被阻塞
                        //当计数=0时,永远不会阻塞。
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        //等待 3ms
        executorService.awaitTermination(3, TimeUnit.MILLISECONDS);
        //在这个例子中,这个是空操作。
        cyclicBarrier.reset();
        System.out.println("done");
        executorService.shutdown();
    }

    public static void action() {
        System.out.println("当前线程名为:" + Thread.currentThread().getName());
    }
}

这个例子中会输出20次“当前线程名为:…”。

3.2 请说明Semaphore的使用场景

3.3 请通过Java 1.4的语法实现一个CountDownLatch

public class LegacyCountDownLatch {
    private int count;

    private LegacyCountDownLatch(int count) {
        this.count = count;
    }

    public void await() throws InterruptedException {
        if (Thread.interrupted()) {
            throw new InterruptedException();
        }
        synchronized (this) {
            while (count > 0) {
                wait();
            }
        }
    }

    public void countDown() {
        synchronized (this) {
            if (count < 1) {
                return;
            }
            count--;
            if (count == 0) {
                notifyAll();
            }
        }
    }
}

4、线程池


4.1 请问J.U.C中内建了几种ExecutorService实现?

比如ThreadPoolExecutor、ForkJoinPool


4.2 请分别解释ThreadPoolExecutor构造器参数在运行时的作用?

ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

其中常见的RejectedExecutionHandler有CallerRunsPolicy、AbortPolicy、DiscardPolicy、DiscardOldestPolicy

4.3 如何获取ThreadPoolExecutor中正在运行的线程?

ThreadPoolExecutor#beforeExecute、ThreadPoolExecutor#afterExecute是一种兼容性比较差的方法。ThreadPoolExecutor参数提供了自定义线程创建方式,所以我们可以利用它的ThreadFactory对象记录每次创建的线程。

public interface ThreadFactory {

/**
* Constructs a new {@code Thread}. Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
* create a thread is rejected
*/
Thread newThread(Runnable r);
}

不过main线程是子线程的Root根,所以我们可以通过下面这种方式获取。

Thread mainThread = Thread.currentThread();
ThreadGroup mainThreadGroup = mainThread.getThreadGroup();
Thread[] threads = new Thread[mainThreadGroup.activeCount()];
mainThreadGroup.enumerate(threads, true);
for (Thread thread : threads) {
if (thread.isAlive()) {
System.out.println("线程" + thread.getName());
}
}


5、FUTURE


5.1 如何获取Future对象?

5.2 请举例Future#get()以及get(long,TimeUnit)方法的使用场景?


5.3 如何利用Future优雅地取消一个任务的执行?

public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();

Future<?> future = executorService.submit(new Runnable() {
@Override
public void run() {
action();
}
});
try {
future.get(1, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
throw new RuntimeException(e);
} catch (TimeoutException e) {
//设置中断状态
Thread.currentThread().interrupt();
//尝试取消
future.cancel(true);
}

}

public static void action()
{
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("当前线程名为:"+Thread.currentThread().getName());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}


6、VOLATILE变量


6.1 在Java中,volatile保证的是可见性还是原子性?


原生类型和对象类型都能保证可见性,原生类型能保证原子性,对象类型无法一定保证原子性,因为对象类型存在中间状态。


6.2 在Java中,volatile long和double是线程安全的吗?


是的。


6.3 在Java中,volatile的底层实现是基于什么机制?


内存屏障。


7、原子操作ATOMIC


7.1 为什么AtomicBoolean内部变量使用int实现,而不是boolean?


在虚拟机中boolean类型其实是用int类型来表示的。也就是说boolean其实是通过int类型来实现的。


7.2 在变量原子操作时,Atomic* CAS操作比synchronized关键字哪个更重?


CAS操作是相对重的操作,它是实现synchronized的轻量级锁。
//偏离锁<CAS操作<重锁(完全互斥)


7.3 Atomic* CAS的底层是如何实现的?