题目1 使用3个线程循环打印10遍ABC
方案一
public static void main(String[] args) {
Maj maj = new Maj();
Thread a = new Thread(new ThreadABC(maj,"A"));
Thread b = new Thread(new ThreadABC(maj,"B"));
Thread c = new Thread(new ThreadABC(maj,"C"));
a.start();
b.start();
c.start();
}
static class ThreadABC implements Runnable{
private String content;
private Maj maj;
public ThreadABC(Maj maj, String content) {
this.content = content;
this.maj = maj;
}
@Override
public void run(){
try{
while (true){
if(maj.cnt_a == 10 && maj.cnt_b == 10 && maj.cnt_c == 10){
break;
}
synchronized (maj){
switch (content){
case "A":
if(maj.cnt_a == maj.cnt_b && maj.cnt_a == maj.cnt_c){
System.out.println(content);
maj.cnt_a++;
maj.notifyAll();
}else {
maj.wait();
}
break;
case "B":
if(maj.cnt_a > maj.cnt_b && maj.cnt_b == maj.cnt_c){
System.out.println(content);
maj.cnt_b++;
maj.notifyAll();
}else {
maj.wait();
}
break;
case "C":
if(maj.cnt_a > maj.cnt_c && maj.cnt_a > maj.cnt_c){
System.out.println(content);
maj.cnt_c++;
maj.notifyAll();
}else {
maj.wait();
}
break;
}
}
}
}catch (Exception ex){
}
}
}
static class Maj {
public volatile int cnt_a = 0;
public volatile int cnt_b = 0;
public volatile int cnt_c = 0;
}
但是注意这个方案其实是错误的。会导致程序卡死。但这个仍然是唯一可行的方案。其实严格按照题目要求是不可实现的。
如果我们允许修改原题目给的模版,那么这个就变成了很轻松实现实现的东西。方案二
package org.luzhen.test;
import java.awt.image.VolatileImage;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author: luzhen
* @Date: 2019-02-12 13:09
* @Version 1.0
*/
public class ConditionTest {
static Lock lock = new ReentrantLock();
final static Condition condition1 = lock.newCondition();
final static Condition condition2 = lock.newCondition();
final static Condition condition3 = lock.newCondition();
private volatile int state = 0;
public static void main(String[] args) {
ConditionTest conditionTest = new ConditionTest();
new Thread(() -> {
conditionTest.printA("A", 0,1, condition1, condition2);
}).start();
new Thread(() -> {
conditionTest.printA("B", 1,2, condition2, condition3);
}).start();
new Thread(() -> {
conditionTest.printA("C", 2,0, condition3, condition1);
}).start();
}
/**
*
* @param s 打印的字符串内容
* @param inParameter 初始化的state入参值
* @param expectNextState 控制打印顺序的开关 让对应的哪个线程执行打印
* @param currentCondition 由于3个condition 会创建3个阻塞队列 代表当前condition所对应的那个阻塞队列
* @param nextCondition 通知我们规定的那个线程 从对应的等待队列里进入同步队列中 获取锁 执行打印
*/
public void printA(String s, int inParameter,int expectNextState, Condition currentCondition, Condition nextCondition) {
for (int i = 0; i < 10; i++) {
try {
lock.lock();
while (state != inParameter) {
currentCondition.await();
}
System.out.println(s);
state = expectNextState;
nextCondition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
题目二
写一个简单的callable和future的使用
public class Test {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
Task task = new Task();
Future<Integer> result = executor.submit(task);
executor.shutdown();
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("主线程在执行任务");
try {
System.out.println("task运行结果"+result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("所有任务执行完毕");
}
}
class Task implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("子线程在进行计算");
Thread.sleep(3000);
int sum = 0;
for(int i=0;i<100;i++)
sum += i;
return sum;
}
}
题目三 描述下concurrentHashMap中的ReentrantLock的使用
ConcurrentHashMap中的segment作为ReentrantLock的包装。
static class Segment<K,V> extends ReentrantLock implements Serializable {
private static final long serialVersionUID = 2249069246763182397L;
final float loadFactor;
Segment(float lf) {
this.loadFactor = lf; }
}
必须清楚,JDK8中的segment已经被遗弃了。现在保留这个纯属序列化兼容需要。
那我们看看JDK7中的使用呢?
public V put(K key, V value) {
Segment<K,V> s;
if (value == null)
throw new NullPointerException();
int hash = hash(key.hashCode());
int j = (hash >>> segmentShift) & segmentMask;
if ((s = (Segment<K,V>)UNSAFE.getObject // nonvolatile; recheck
(segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment
s = ensureSegment(j);
return s.put(key, hash, value, false);
}
可见putvalue最后调用的是segment的put
final V put(K key, int hash, V value, boolean onlyIfAbsent) {
HashEntry<K,V> node = tryLock() ? null :
scanAndLockForPut(key, hash, value);
V oldValue;
try {
HashEntry<K,V>[] tab = table;
int index = (tab.length - 1) & hash;
HashEntry<K,V> first = entryAt(tab, index);
for (HashEntry<K,V> e = first;;) {
if (e != null) {
K k;
if ((k = e.key) == key ||
(e.hash == hash && key.equals(k))) {
oldValue = e.value;
if (!onlyIfAbsent) {
e.value = value;
++modCount;
}
break;
}
e = e.next;
}
else {
if (node != null)
node.setNext(first);
else
node = new HashEntry<K,V>(hash, key, value, first);
int c = count + 1;
if (c > threshold && tab.length < MAXIMUM_CAPACITY)
rehash(node);
else
setEntryAt(tab, index, node);
++modCount;
count = c;
oldValue = null;
break;
}
}
} finally {
unlock();
}
return oldValue;
}
private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) {
HashEntry<K,V> first = entryForHash(this, hash);
HashEntry<K,V> e = first;
HashEntry<K,V> node = null;
int retries = -1; // negative while locating node
while (!tryLock()) {
HashEntry<K,V> f; // to recheck first below
if (retries < 0) {
if (e == null) {
if (node == null) // speculatively create node
node = new HashEntry<K,V>(hash, key, value, null);
retries = 0;
}
else if (key.equals(e.key))
retries = 0;
else
e = e.next;
}
else if (++retries > MAX_SCAN_RETRIES) {
lock();
break;
}
else if ((retries & 1) == 0 &&
(f = entryForHash(this, hash)) != first) {
e = first = f; // re-traverse if entry changed
retries = -1;
}
}
return node;
}
可见,当put value抢不到锁的时候,就会循环等待。如果循环若干次也不行吗,那么就直接调用lock来等待锁。
题目四 写一些JUC中锁的例子来证明你懂
- Semaphore
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class TestSemaphore {
public static void main(String[] args) {
// 线程池
ExecutorService exec = Executors.newCachedThreadPool();
// 只能5个线程同时访问
final Semaphore semp = new Semaphore(5);
// 模拟20个客户端访问
for (int index = 0; index < 20; index++) {
final int NO = index;
Runnable run = new Runnable() {
public void run() {
try {
// 获取许可
semp.acquire();
System.out.println("Accessing: " + NO);
Thread.sleep((long) (Math.random() * 10000));
// 访问完后,释放
semp.release();
} catch (InterruptedException e) {
}
}
};
exec.execute(run);
}
// 退出线程池
exec.shutdown();
}
}
- CountDownLatchDemo
package com.example.demo.CountDownLatchDemo;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 主线程等待子线程执行完成再执行
*/
public class CountdownLatchTest1 {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(3);
final CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println("子线程" + Thread.currentThread().getName() + "开始执行");
Thread.sleep((long) (Math.random() * 10000));
System.out.println("子线程"+Thread.currentThread().getName()+"执行完成");
latch.countDown();//当前线程调用此方法,则计数减一
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try {
System.out.println("主线程"+Thread.currentThread().getName()+"等待子线程执行完成...");
latch.await();//阻塞当前线程,直到计数器的值为0
System.out.println("主线程"+Thread.currentThread().getName()+"开始执行...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- CyclicBarrier
public class CyclicBarrierDemo {
static class TaskThread extends Thread {
CyclicBarrier barrier;
public TaskThread(CyclicBarrier barrier) {
this.barrier = barrier;
}
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println(getName() + " 到达栅栏 A");
barrier.await();
System.out.println(getName() + " 冲破栅栏 A");
Thread.sleep(2000);
System.out.println(getName() + " 到达栅栏 B");
barrier.await();
System.out.println(getName() + " 冲破栅栏 B");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
int threadNum = 5;
CyclicBarrier barrier = new CyclicBarrier(threadNum, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 完成最后任务");
}
});
for(int i = 0; i < threadNum; i++) {
new TaskThread(barrier).start();
}
}
}