- 整理常见的Java面试并发的编程题
1、写一个死锁
import java.util.concurrent.locks.ReentrantLock;
/**
* 死锁
*
* @author wenei
* @date 2021-08-14 11:38
*/
public class DeadLock {
private static ReentrantLock lock1 = new ReentrantLock();
private static ReentrantLock lock2 = new ReentrantLock();
public static void main(String[] args) {
new Thread(() -> {
System.out.println("线程1启动");
// 获取lock1
lock1.lock();
try {
// 等待100ms 让线程2有足够的时间先获取lock2
Thread.sleep(100);
// 死锁
lock2.lock();
try {
} finally {
lock2.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock1.unlock();
}
System.out.println("线程1结束");
}).start();
new Thread(() -> {
System.out.println("线程2启动");
// 获取锁2
lock2.lock();
try {
// 等待100ms 让线程2有足够的时间先获取lock1
Thread.sleep(100);
// 死锁
lock1.lock();
try {
} finally {
lock2.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock2.unlock();
}
System.out.println("线程2结束");
}).start();
}
}
/*
线程1启动
线程2启动
*/
可以通过到使用命令jps
查看Java进程。
然后使用jstack [pid]
查看进程是否有死锁。
2、两个线程交替执行
未保证线程1,2的执行顺序。
使用Object的notify和wait方法实现。
/**
* 线程交替运行
*
* @author wenei
* @date 2021-07-02 21:43
*/
public class TwoThread {
public static void main(String[] args) {
TwoThread test = new TwoThread();
Thread thread1 = new Thread(test::print1);
Thread thread2 = new Thread(test::print2);
thread1.start();
thread2.start();
}
private synchronized void print1() {
for (int i = 1; i <= 10; i += 2) {
System.out.println("print1 " + i);
// 唤醒该对象等待队列中的另一个线程
this.notify();
// 当前对象的线程进入等待队列
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("print1执行完毕");
}
private synchronized void print2() {
for (int i = 2; i <= 10; i += 2) {
System.out.println("print2 " + i);
this.notify();
try {
if (i != 10) {
this.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("print2执行完毕");
}
}
/*
print1 1
print2 2
print1 3
print2 4
print1 5
print2 6
print1 7
print2 8
print1 9
print2 10
print1执行完毕
print2执行完毕
Process finished with exit code 0
*/
3、三个线程交替执行
使用interger保证线程的执行顺序。
使用Condition保证线程交替执行。
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 三个线程交替运行
*
* @author wenei
* @date 2021-08-14 9:59
*/
public class ThreeThread {
private static AtomicInteger integer = new AtomicInteger(0);
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Condition c3 = lock.newCondition();
Thread thread1 = new Thread(() -> {
for (int i = 1; i < 30; i += 12) {
try {
lock.lock();
if (integer.get() % 3 != 0) {
c1.await();
}
System.out.println(Thread.currentThread().getName() + ":" + i);
System.out.println(Thread.currentThread().getName() + ":" + (i + 1));
System.out.println(Thread.currentThread().getName() + ":" + (i + 2));
System.out.println(Thread.currentThread().getName() + ":" + (i + 3));
integer.incrementAndGet();
c2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
Thread thread2 = new Thread(() -> {
for (int i = 5; i < 30; i += 12) {
try {
lock.lock();
if (integer.get() % 3 != 1) {
c2.await();
}
System.out.println(Thread.currentThread().getName() + ":" + i);
System.out.println(Thread.currentThread().getName() + ":" + (i + 1));
System.out.println(Thread.currentThread().getName() + ":" + (i + 2));
System.out.println(Thread.currentThread().getName() + ":" + (i + 3));
integer.incrementAndGet();
c3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
Thread thread3 = new Thread(() -> {
for (int i = 9; i < 30; i += 12) {
try {
lock.lock();
if (integer.get() % 3 != 2) {
c3.await();
}
System.out.println(Thread.currentThread().getName() + ":" + i);
System.out.println(Thread.currentThread().getName() + ":" + (i + 1));
System.out.println(Thread.currentThread().getName() + ":" + (i + 2));
System.out.println(Thread.currentThread().getName() + ":" + (i + 3));
integer.incrementAndGet();
c1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
thread1.start();
thread2.start();
thread3.start();
}
}
/*
Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
Thread-1:5
Thread-1:6
Thread-1:7
Thread-1:8
Thread-2:9
Thread-2:10
Thread-2:11
Thread-2:12
Thread-0:13
Thread-0:14
Thread-0:15
Thread-0:16
Thread-1:17
Thread-1:18
Thread-1:19
Thread-1:20
Thread-2:21
Thread-2:22
Thread-2:23
Thread-2:24
Thread-0:25
Thread-0:26
Thread-0:27
Thread-0:28
Thread-1:29
Thread-1:30
Thread-1:31
Thread-1:32
Process finished with exit code 0
*/
4、LRU算法实现
该方式采用链表+HashMap的方式实现。
package scenequestion;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义链表
*
* @author wenei
* @date 2021-08-14 19:53
*/
public class CustomLRUCache {
static class Node {
int key;
int value;
Node next;
Node pre;
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
private final Map<Integer, Node> map = new HashMap<>();
private final int capacity;
// 头节点
private final Node head = new Node(-1, -1);
// 尾节点
private final Node tail = new Node(-1, -1);
public CustomLRUCache(int capacity) {
this.capacity = capacity;
head.next = tail;
tail.pre = head;
}
private int get(Integer key) {
if (!map.containsKey(key)) {
return -1;
} else {
Node node = map.get(key);
// 前移节点
removeAt(node);
addFirst(node);
return map.get(key).value;
}
}
private void put(Integer key, Integer value) {
Node oldNode = map.getOrDefault(key, null);
if (oldNode == null) {
// 如果需要插入节点则判断容器大小
if (map.size() == capacity) {
// 删除尾节点
map.remove(tail.pre.key);
removeAt(tail.pre);
}
oldNode = new Node(key, value);
addFirst(oldNode);
} else {
// 覆盖旧节点的值并前移
oldNode.value = value;
removeAt(oldNode);
addFirst(oldNode);
}
map.put(key, oldNode);
}
private void removeAt(Node node) {
node.pre.next = node.next;
node.next.pre = node.pre;
}
private void addFirst(Node node) {
node.pre = head;
node.next = head.next;
head.next.pre = node;
head.next = node;
}
public static void main(String[] args) {
CustomLRUCache cache = new CustomLRUCache(2);
cache.put(2, 1);
cache.put(1, 1);
cache.put(2, 3);
cache.put(4, 1);
System.out.println(cache.get(1));
System.out.println(cache.get(2));
}
}