多线程

现在又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 作为上限。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值