面试题
1、要求用线程顺序打印A1B2C3…Z26
解法一(LockSupport): 代码如下:
static Thread t1 = null, t2 = null;
public static void main(String[] args) {
t1 = new Thread(() -> {
for (char a ='A'; a <= 'Z'; a++) {
System.out.print(a);
LockSupport.unpark(t2);
LockSupport.park();
}
});
t2 = new Thread(() -> {
for (int i = 1; i <= 26; i++) {
LockSupport.park();
System.out.print(i);
LockSupport.unpark(t1);
}
});
t1.start();
t2.start();
}
如上代码可能会有个疑问就是说为什么t2要直接把LockSupport.park()放最前面? 这样的目的是为了保障在t1和t2在运行中争抢CPU时间片的时候如果是t2获取到了CPU执行时间片那么就会直接让t2 park在那里,等待t1执行unpark再输出对应的数字,就保证了t1的执行顺序一定在t2之前。
第二种解法: 使用sychronized_wait_notify 解法一(使用countdownlatch来控制哪个线程先执行):
private static CountDownLatch latch = new CountDownLatch(1);
public static void main(String[] args) {
final Object o = new Object();
new Thread(() -> {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o) {
for (int i = 1; i <= 26; i++) {
System.out.print(i);
try {
o.notify();
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify();
}
}).start();
new Thread(() -> {
latch.countDown();
synchronized (o) {
for (char c = 'A'; c <= 'Z'; c++) {
System.out.print(c);
try {
o.notify();
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify();
}
}).start();
}
解法二:
private static volatile boolean t2Started = false;
public static void main(String[] args) {
final Object o = new Object();
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
new Thread(() -> {
synchronized (o) {
while(!t2Started) {
try {
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (char c : aI) {
System.out.print(c);
try {
o.notify();
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify();
}
}, "t1").start();
new Thread(() -> {
synchronized (o) {
for (char c : aC) {
System.out.print(c);
t2Started = true;
try {
o.notify();
o.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
o.notify();
}
}, "t2").start();
}
通过volatile修饰来控制哪个线程先执行
第三种解法Lock_Condition:
private static CountDownLatch latch = new CountDownLatch(1);
public static void main(String[] args) {
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
new Thread(() -> {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
try {
for (int i = 1; i <= 26; i++) {
System.out.print(i);
condition2.signal();
condition1.await();
}
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t1").start();
new Thread(() -> {
lock.lock();
try {
for (char c = 'A'; c <= 'Z'; c++) {
System.out.print(c);
latch.countDown();
condition1.signal();
condition2.await();
}
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}, "t2").start();
}
第四种解法CAS:
enum ReadyToRun {T1, T2}
static volatile ReadyToRun r = ReadyToRun.T1;
public static void main(String[] args) {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
new Thread(() -> {
for (char c : aI) {
while (r != ReadyToRun.T1) {
}
System.out.print(c);
r = ReadyToRun.T2;
}
}, "t1").start();
new Thread(() -> {
for (char c : aC) {
while (r != ReadyToRun.T2) {
}
System.out.print(c);
r = ReadyToRun.T1;
}
}, "t2").start();
}
其中volatile保证线程间的可见性。 也可以用AutomicInteger
static AtomicInteger threadNo = new AtomicInteger(1);
public static void main(String[] args) {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
new Thread(() -> {
for (char c : aI) {
while (threadNo.get() != 1) {
}
System.out.print(c);
threadNo.set(2);
}
}, "t1").start();
new Thread(() -> {
for (char c : aC) {
while (threadNo.get() != 2) {
}
System.out.print(c);
threadNo.set(1);
}
}, "t2").start();
}
第五种解法BlockingQueue:
static BlockingQueue<String> q1 = new ArrayBlockingQueue(1);
static BlockingQueue<String> q2 = new ArrayBlockingQueue(1);
public static void main(String[] args) throws Exception {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
new Thread(() -> {
for (char c : aI) {
System.out.print(c);
try {
q1.put("ok");
q2.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t1").start();
new Thread(() -> {
for (char c : aC) {
try {
q1.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(c);
try {
q2.put("ok");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "t2").start();
}
第六种解法(PipedStream): 相当于两个线程之间建立一个虚拟的管道(但是效率非常的低)然后通过管道之间互相通信。 如果第一个线程要往第二个线程上面写的话 那么第一个线程有个outputstream第二个线程有个inputstream
public static void main(String[] args) throws Exception {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
PipedInputStream input1 = new PipedInputStream();
PipedInputStream input2 = new PipedInputStream();
PipedOutputStream output1 = new PipedOutputStream();
PipedOutputStream output2 = new PipedOutputStream();
input1.connect(output2);
input2.connect(output1);
String msg = "Your Turn";
new Thread(() -> {
byte[] buffer = new byte[9];
try {
for (char c : aI) {
input1.read(buffer);
if (new String(buffer).equals(msg)) {
System.out.print(c);
}
output1.write(msg.getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}, "t1").start();
new Thread(() -> {
byte[] buffer = new byte[9];
try {
for (char c : aC) {
System.out.print(c);
output2.write(msg.getBytes());
input2.read(buffer);
if (new String(buffer).equals(msg)) {
continue;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}, "t2").start();
}
第七种解法(TransferQueue):
public static void main(String[] args) {
char[] aI = "1234567".toCharArray();
char[] aC = "ABCDEFG".toCharArray();
TransferQueue<Character> queue = new LinkedTransferQueue<Character>();
new Thread(() -> {
try {
for (char c : aI) {
System.out.print(queue.take());
queue.transfer(c);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t1").start();
new Thread(() -> {
try {
for (char c : aC) {
queue.transfer(c);
System.out.print(queue.take());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t2").start();
}
线程池的几个常用类
Callable
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> c = new Callable() {
@Override
public String call() throws Exception {
return "Hello Callable";
}
};
ExecutorService service = Executors.newCachedThreadPool();
Future<String> future = service.submit(c);
System.out.println(future.get());
service.shutdown();
}
其中Callable只能代表一个任务,但不能作为一个Future来用。
FutureTask
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<Integer> task = new FutureTask<>(() -> {
TimeUnit.MILLISECONDS.sleep(500);
return 1000;
});
new Thread(task).start();
System.out.println(task.get());
}
其中FutureTask可以作为任务来用,也可以做为Future来用,因为它不仅存了任务也存了执行的结果。 原因在于FutureTask实现了RunnableFuture 而RunnableFuture又继承了Runnable,及Future,其中Runnable存任务,Future存结果。 Call Callable ->Runnable+返回值 Future->存储执行的将来才会产生的结果 FutureTask->Future+Runnable
CompletableFuture
对各个任务进行组合处理
- 假设你能够提供一个服务,这个服务查询各大电商网站同一类产品的价格并汇总展示
public static void main(String[] args) throws ExecutionException, InterruptedException {
long start, end;
start = System.currentTimeMillis();
CompletableFuture<Double> futureTM = CompletableFuture.supplyAsync(() -> priceOfTM());
CompletableFuture<Double> futureTB = CompletableFuture.supplyAsync(() -> priceOfTB());
CompletableFuture<Double> futureJD = CompletableFuture.supplyAsync(() -> priceOfJD());
CompletableFuture.allOf(futureTM, futureTB, futureJD).join();
end = System.currentTimeMillis();
System.out.println("use completable future! " + (end - start));
private static double priceOfTM() {
delay();
return 1.00;
}
private static double priceOfTB() {
delay();
return 2.00;
}
private static double priceOfJD() {
delay();
return 3.00;
}
private static void delay() {
int time = new Random().nextInt(500);
try {
TimeUnit.MILLISECONDS.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("After %s sleep!\n", time);
}
如上通过CompletableFuture让多个任务异步执行,并通过 CompletableFuture.allOf(futureTM, futureTB, futureJD).join()方法收集多个任务的执行结果,执行完毕后再执行后续的业务逻辑。 同时CompletableFuture可以管理多个Future的结果,并对各种结果进行组合处理
|