现在又T1,T2,T3,三个线程,你怎样保证T2在T1执行完成后执行,T3在T2执行后完成后执行?
解决办法:
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。
比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才能继续执行线程B。也可以用wait方法实现。想要更深入的了解,建议看一下join的源码。
t.join();//调用join方法,等待线程t执行完毕
t.jion(1000);//等待t线程,等待时间是1000毫秒
代码实现:
`public static void main(String[] args) {
method01();
method02();
}
/**
* 第一种实现方式,顺序写死在线程代码的内部了,有时候不方便
*/
private static void method01() {
Thread t1 = new Thread(new Runnable() {
@Override public void run() {
System.out.println("t1 is finished");
}
});
Thread t2 = new Thread(new Runnable() {
@Override public void run() {
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2 is finished");
}
});
Thread t3 = new Thread(new Runnable() {
@Override public void run() {
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t3 is finished");
}
});
t3.start();
t2.start();
t1.start();
}
/**
* 第二种实现方式,线程执行顺序可以在方法中调换
*/
private static void method02(){
Runnable runnable = new Runnable() {
@Override public void run() {
System.out.println(Thread.currentThread().getName() + "执行完成");
}
};
Thread t1 = new Thread(runnable, "t1");
Thread t2 = new Thread(runnable, "t2");
Thread t3 = new Thread(runnable, "t3");
try {
t1.start();
t1.join();
t2.start();
t2.join();
t3.start();
t3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
在Java中Lock接口相对于synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保证他的完整性,你会怎么样去实现它?
Lock接口和ReawWriteLock接口 如下:
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
整体来说Lock是synchronized的扩展版,Lock提供了无条件的,可轮询的(tryLock方法),定时的(tryLock带参方法),可中断的(lockinterruptibly),可多条件队列的(newCondition方法)锁操作。另外Lock的实现基本都支持非公平锁(默认)和公平锁,synchronization只支持非公平锁,当然,在大部分情况下,非公平锁是高效的选择。
ReadWriteLock是对Lock的运用,具体的实现类是ReentrantReadWriteLock,下面用这个类来实现读写类型的高效缓存:
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 用ReadWriteLock读写锁来实现一个高效的Map缓存
* Created by LEO on 2017/10/30.
*/
public class ReaderAndWriter<K, V> {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
private final Map<K, V> map;
public ReaderAndWriter(Map<K, V> map) {
this.map = map;
}
/************* 这是用lock()方法写的 ********************/
// public V put(K key, V value){
// writeLock.lock();
// try {
// return map.put(key, value);
// }finally {
// writeLock.unlock();
// }
// }
// public V get(K key){
// readLock.lock();
// try {
// return map.get(key);
// }finally {
// readLock.unlock();
// }
// }
/************* 这是用tryLock()方法写的 ********************/
public V put(K key, V value){
while (true){
if(writeLock.tryLock()){
try {
System.out.println("put "+ key +" = " + value);
return map.put(key, value);
}finally {
writeLock.unlock();
}
}
}
}
public V get(K key){
while (true){
if (readLock.tryLock()) {
try {
V v = map.get(key);
System.out.println("get "+ key +" = " + v);
return v;
} finally {
readLock.unlock();
}
}
}
}
/******************** 下面是测试区 *********************************/
public static void main(String[] args) {
final ReaderAndWriter<String, Integer> rw = new ReaderAndWriter<>(new HashMap<>());
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
exec.execute(new TestRunnable(rw));
}
exec.shutdown();
}
static class TestRunnable implements Runnable{
private final ReaderAndWriter<String, Integer> rw;
private final String KEY = "x";
TestRunnable(ReaderAndWriter<String, Integer> rw) {
this.rw = rw;
}
@Override
public void run() {
Random random = new Random();
int r = random.nextInt(100);
//生成随机数,小于30的写入缓存,大于等于30则读取数字
if (r < 30){
rw.put(KEY, r);
} else {
rw.get(KEY);
}
}
}
}
在Java中wait和sleep方法的不同?
通常会在面试中经常被问到的Java线程面试问题。
最大的不同是在等待时wait会释放锁,而sleep一直持有锁。wait通常被用于线程间交互,sleep通常被用于暂停执行。
有关Java多线程的基础知识:
- Java的多线程锁是挂在对象上的,并不是在方法上的。即每个对象都要一个锁,当遇到类似synchronized的同步需要时,就会监视(monitor)每个想使用本对象的线程按照一定的规则来访问,规则也就是在同一时间内只能有一个线程能访问此对象。
- Java中获取锁的单位是线程。当线程A获取了对象B的锁,也就是对象B的持有标记上写的是线程A的唯一标识,只需要同步的情况下,只有线程A能访问对象B。
- Thread常用的方法有:start/stop/yieId/sleep/interrupt/join等,他们是线程级别的方法,所以并不需要太关心锁的具体逻辑。
- Object的线程有关方法是:wait/wait(事件参数)/notify/notifyAll,他们是对象的方法,所以使用的时候就有点憋屈了,必须当前线程获取了本对象的锁才能使用,否则会报异常。但是他们能更细粒度的控制锁,可以释放锁。
用Java实现阻塞队列。
这是一个相对艰难的多线程面试问题,他能达到很多的目的。第一,他可以检测候选者是否能实际的用Java线程写程序;第二,可以检测候选者对并发场景的理解,并且你可以根据这个问题延申出更多的问题。如果他用wait()和notity()方法来实现阻塞队列。
下面是实现了阻塞的take和put方法的阻塞队列(分别用synchronized和wait/notify实现):
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
-
实现了阻塞的take和put方法的阻塞队列
-
分别用synchronized 和 wait/notify 实现
-
@author xuexiaolei
-
@version 2017年11月01日
*/
public class MyBlocingQueue {
private final List list;
private final int limit;//有大小限制的public MyBlocingQueue(int limit) {
this.limit = limit;
this.list = new LinkedList();
}//这是用synchronized写的,在list空或者满的时候效率会低,因为会一直轮询
// public void put(E e){
// while(true){
// synchronized (list){
// if (list.size() < limit) {
// System.out.println("list : " + list.toString());
// System.out.println("put : " + e);
// list.add(e);
// return;
// }
// }
// }
// }
// public E take(){
// while (true) {
// synchronized (list) {
// if (list.size() > 0){
// System.out.println("list : " + list.toString());
// E remove = (E) list.remove(0);
// System.out.println("take : " + remove);
// return remove;
// }
// }
// }
// }//用wait,notify写的,在list空或者满的时候效率会高一点,因为wait释放锁,然后等待唤醒
public synchronized void put(E e){
while (list.size() == limit){
try {
wait();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
System.out.println("list : " + list.toString());
System.out.println("put : " + e);
list.add(e);
notifyAll();
}
public synchronized E take() {
while (list.size() == 0){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("list : " + list.toString());
E remove = (E) list.remove(0);
System.out.println("take : " + remove);
notifyAll();
return remove;
}/******************** 下面是测试区 *********************************/
public static void main(String[] args) {
final MyBlocingQueue myBlocingQueue = new MyBlocingQueue(10);
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
exec.execute(new TestRunnable(myBlocingQueue));
}
exec.shutdown();
}static class TestRunnable implements Runnable{
private final MyBlocingQueue myBlocingQueue;TestRunnable(MyBlocingQueue<Integer> myBlocingQueue) { this.myBlocingQueue = myBlocingQueue; } @Override public void run() { Random random = new Random(); int r = random.nextInt(100); //生成随机数,按照一定比率读取或者放入,可以更改!!! if (r < 30){ myBlocingQueue.put(r); } else { myBlocingQueue.take(); } }
}
}
BlockingQueue介绍:
Java5中提供了BlockingQueue的方法,并且有几个实现,在此介绍一下。
BlockingQueue 具有 4 组不同的方法用于插入、移除以及对队列中的元素进行检查。如果请求的操作不能得到立即执行的话,每个方法的表现也不同。这些方法如下:
Throws exception Special value Blocks Times out
add(e) offer(e) put(e) offer(Object, long, TimeUnit)
remove() poll() take() poll(long, TimeUnit)
element() peek()
- Throws exception 抛异常:如果试图的操作无法立即执行,抛一个异常。
- Special value 特定值:如果试图的操作无法立即执行,返回一个特定的值(常常是 true / false)
- Blocks 阻塞:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行。
- Times out 超时:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行,但等待时间不会超过给定值。返回一个特定值以告知该操作是否成功(典型的是true / false)。
BlockingQueue 的实现类
- ArrayBlockingQueue:ArrayBlockingQueue是一个有界的阻塞队列,其内部实现是将对象放到一个数组里。有界也就意味着,他不能够储存无限多数量的元素。它有一个同一时间能够储存元素数量的上线。你可以在其初始化的时候设定这个上限,但之后就无法对这个上线进行修改l(因为他是基于数组实现的,也就具有数组的特性:一旦初始化,大小就无法修改)。
- DelayQueue:DelayQueue 对元素进行持有直到一个特定的延迟到期。注入其中的元素必须实现 java.util.concurrent.Delayed 接口。
- LinkedBlockingQueue:LinkedBlockingQueue 内部以一个链式结构(链接节点)对其元素进行存储。如果需要的话,这个链式结构可以选者一个上线,将使用 Integer.MAX_VALUE 作为上限。