java多线程面试重点-金沙1005

java多线程面试重点_java 多线程面试总结java并发编程问题是面试过程中很容易遇到的问题,提前准备是解决问题的最好办法,将试题总结起来,时常查看会有奇效。现在有t1、t2、t3三个线程,你怎样保证t2在t1执行完后执行,t3在t2执行完后执行?这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉。这个多线程问题比较简单,可以用join方法实现。核心:thread.join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程b中调用了线程a的join()方法,直到线程a执

java并发编程问题是面试过程中很容易遇到的问题,提前准备是解决问题的最好办法,将试题总结起来,时常查看会有奇效。

现在有t1、t2、t3三个线程,你怎样保证t2在t1执行完后执行,t3在t2执行完后执行?

这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉。这个多线程问题比较简单,可以用join方法实现。

核心:

thread.join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。
比如在线程b中调用了线程a的join()方法,直到线程a执行完毕后,才会继续执行线程b。
想要更深入了解,建议看一下join的源码,也很简单的,使用wait方法实现的。

t.join(); //调用join方法,等待线程t执行完毕
t.join(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多线程面试重点_java 多线程面试总结

为防止网络爬虫,请关注公众号回复”口令”

激活idea 激活clion
datagrip dataspell
dotcover dotmemory
dottrace goland
phpstorm pycharm
resharper reshac
rider rubymine
webstorm 全家桶

在java中lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它?

这个题的原答案我认为不是很全面。
lock接口 和 readwritelock接口 如下:

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的实现类基本都支持非公平锁(默认)和公平锁,synchronized只支持非公平锁,当然,在大部分情况下,非公平锁是高效的选择。

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 {
    private final readwritelock lock = new reentrantreadwritelock();
    private final lock readlock = lock.readlock();
    private final lock writelock = lock.writelock();
    private final map map;
    public readerandwriter(map 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 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 rw;
        private final string key = "x";
        testrunnable(readerandwriter 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/yield/sleep/interrupt/join等,他们是线程级别的方法,所以并不会太关心锁的具体逻辑。
– object的线程有关方法是:wait/wait(事件参数)/notify/notifyall,他们是对象的方法,所以使用的时候就有点憋屈了,必须当前线程获取了本对象的锁才能使用,否则会报异常。但他们能更细粒度的控制锁,可以释放锁。

用java实现阻塞队列。

这是一个相对艰难的多线程面试问题,它能达到很多的目的。第一,它可以检测侯选者是否能实际的用java线程写程序;第二,可以检测侯选者对并发场景的理解,并且你可以根据这个问很多问题。如果他用wait()和notify()方法来实现阻塞队列,你可以要求他用最新的java 5中的并发类来再写一次。

下面是实现了阻塞的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 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 是一个有界的阻塞队列,其内部实现是将对象放到一个数组里。有界也就意味着,它不能够存储无限多数量的元素。它有一个同一时间能够存储元素数量的上限。你可以在对其初始化的时候设定这个上限,但之后就无法对这个上限进行修改了(译者注:因为它是基于数组实现的,也就具有数组的特性:一旦初始化,大小就无法修改)。
-delayqueue:delayqueue 对元素进行持有直到一个特定的延迟到期。注入其中的元素必须实现 java.util.concurrent.delayed 接口。
-linkedblockingqueue:linkedblockingqueue 内部以一个链式结构(链接节点)对其元素进行存储。如果需要的话,这一链式结构可以选择一个上限。如果没有定义上限,将使用 integer.max_value 作为上限。
-priorityblockingqueue:priorityblockingqueue 是一个无界的并发队列。它使用了和类 java.util.priorityqueue 一样的排序规则。你无法向这个队列中插入 null 值。所有插入到 priorityblockingqueue 的元素必须实现 java.lang.comparable 接口。因此该队列中元素的排序就取决于你自己的 comparable 实现。
-synchronousqueue:synchronousqueue 是一个特殊的队列,它的内部同时只能够容纳单个元素。如果该队列已有一元素的话,试图向队列中插入一个新元素的线程将会阻塞,直到另一个线程将该元素从队列中抽走。同样,如果该队列为空,试图向队列中抽取一个元素的线程将会阻塞,直到另一个线程向队列中插入了一条新的元素。据此,把这个类称作一个队列显然是夸大其词了。它更多像是一个汇合点。

blocingqueue的实现大多是通过 lock锁的多条件(condition)阻塞控制,下面我们自己写一个简单版:

import java.util.linkedlist;
import java.util.list;
import java.util.concurrent.locks.condition;
import java.util.concurrent.locks.lock;
import java.util.concurrent.locks.reentrantlock;
/**
 * 模仿arrayblockingqueue实现阻塞队列
 * @author xuexiaolei
 * @version 2017年11月01日
 */
public class myblocingqueue2 {
    private final list list;
    private final int limit;//有大小限制的
    private final lock lock = new reentrantlock();
    private final condition notfull = lock.newcondition();
    private final condition notempty = lock.newcondition();
    public myblocingqueue2(int limit) {
        this.limit = limit;
        this.list = new linkedlist();
    }
    public void put(e e) throws interruptedexception {
        lock.lock();
        try {
            while (list.size() == limit){
                notfull.await();
            }
            list.add(e);
            notempty.signalall();
        }finally {
            lock.unlock();
        }
    }
    public e take() throws interruptedexception {
        lock.lock();
        try {
            while (list.size() == 0){
                notempty.await();
            }
            e remove = (e) list.remove(0);
            notfull.signalall();
            return remove;
        }finally {
            lock.unlock();
        }
    }
}

用java写代码来解决生产者——消费者问题。

与上面的问题很类似,但这个问题更经典,有些时候面试都会问下面的问题。在java中怎么解决生产者——消费者问题,当然有很多解决方法,我已经分享了一种用阻塞队列实现的方法。有些时候他们甚至会问怎么实现哲学家进餐问题。

生产者、消费者有很多的实现方法:
– 用wait() / notify()方法
– 用lock的多condition方法
– blockingqueue阻塞队列方法

可以发现在上面实现阻塞队列题中,blockingqueue的实现基本都用到了类似的实现,将blockingqueue的实现方式稍微包装一下就成了一个生产者-消费者模式了。

import java.util.random;
import java.util.concurrent.arrayblockingqueue;
import java.util.concurrent.blockingqueue;
/**
 * 用阻塞队列快速实现生产者-消费者
 * @author xuexiaolei
 * @version 2017年11月01日
 */
public class produceandconsumer {
    public static void main(string[] args) {
        final blockingqueue list = new arrayblockingqueue(10);
        procude procude = new procude(list);
        consumer consumer = new consumer(list);
        procude.start();
        consumer.start();
    }
    static class procude extends thread{
        private final blockingqueue list;
        procude(blockingqueue list) {
            this.list = list;
        }
        @override public void run() {
            while(true){
                try {
                    integer take = list.take();
                    system.out.println("消费数据:"   take);
//                    thread.sleep(1000);
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
            }
        }
    }
    static class consumer extends thread{
        private final blockingqueue list;
        consumer(blockingqueue list) {
            this.list = list;
        }
        @override public void run() {
            while (true){
                try {
                    int i = new random().nextint(100);
                    list.put(i);
                    system.out.println("生产数据:"   i);
                    thread.sleep(1000);
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
            }
        }
    }
}

此处不再详细地写另外几种实现方式了:wait() / notify()方法、lock的多condition方法、信号量等,甚至可以考虑用cyclicbarrier、countdownlatch也可以实现生产者-消费者的,难易程度、效率不一样罢了。

用java写一个会导致死锁的程序,你将怎么解决?

这是我最喜欢的java线程面试问题,因为即使死锁问题在写多线程并发程序时非常普遍,但是很多侯选者并不能写deadlock free code(无死锁代码?),他们很挣扎。只要告诉他们,你有n个资源和n个线程,并且你需要所有的资源来完成一个操作。为了简单这里的n可以替换为2,越大的数据会使问题看起来更复杂。通过避免java中的死锁来得到关于死锁的更多信息。

/**
 * 简单死锁程序
 *      locka、lockb分别是两个资源,线程a、b必须同是拿到才能工作
 *      但a线程先拿locka、再拿lockb
 *      线程先拿lockb、再拿locka
 * @author xuexiaolei
 * @version 2017年11月01日
 */
public class simpledeadlock {
    public static void main(string[] args) {
        object locka = new object();
        object lockb = new object();
        a a = new a(locka, lockb);
        b b = new b(locka, lockb);
        a.start();
        b.start();
    }
    static class a extends thread{
        private final object locka;
        private final object lockb;
        a(object locka, object lockb) {
            this.locka = locka;
            this.lockb = lockb;
        }
        @override public void run() {
            synchronized (locka){
                try {
                    thread.sleep(1000);
                    synchronized (lockb){
                        system.out.println("hello a");
                    }
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
            }
        }
    }
    static class b extends thread{
        private final object locka;
        private final object lockb;
        b(object locka, object lockb) {
            this.locka = locka;
            this.lockb = lockb;
        }
        @override public void run() {
            synchronized (lockb){
                try {
                    thread.sleep(1000);
                    synchronized (locka){
                        system.out.println("hello b");
                    }
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
            }
        }
    }
}

产生死锁的四个必要条件:
– 互斥条件:一个资源每次只能被一个进程使用。
– 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
– 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
– 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

如何避免死锁?
– 从死锁的四个必要条件来看,破坏其中的任意一个条件就可以避免死锁。但互斥条件是由资源本身决定的,不剥夺条件一般无法破坏,要实现的话得自己写更多的逻辑。
– 避免无限期的等待:用lock.trylock(),wait/notify等方法写出请求一定时间后,放弃已经拥有的锁的程序。
– 注意锁的顺序:以固定的顺序获取锁,可以避免死锁。
– 开放调用:即只对有请求的进行封锁。你应当只想你要运行的资源获取封锁,比如在上述程序中我在封锁的完全的对象资源。但是如果我们只对它所属领域中的一个感兴趣,那我们应当封锁住那个特殊的领域而并非完全的对象。
– 最后,如果能避免使用多个锁,甚至写出无锁的线程安全程序是再好不过了。

什么是原子操作,java中的原子操作是什么?

非常简单的java线程面试问题,接下来的问题是你是否需要同步一个原子操作。

原子操作是不可分割的操作,一个原子操作中间是不会被其他线程打断的,所以不需要同步一个原子操作。
多个原子操作合并起来后就不是一个原子操作了,就需要同步了。
i 不是一个原子操作,它包含 读取-修改-写入 操作,在多线程状态下是不安全的。
另外,java内存模型允许将64位的读操作或写操作分解为2个32位的操作,所以对long和double类型的单次读写操作并不是原子的,注意使用volitile使他们成为原子操作。

java中的volatile关键是什么作用?怎样使用它?在java中它跟synchronized方法有什么不同?

自从java 5和java内存模型改变以后,基于volatile关键字的线程问题越来越流行。应该准备好回答关于volatile变量怎样在并发环境中确保可见性。

volatile关键字的作用是:保证变量的可见性。
在java内存结构中,每个线程都是有自己独立的内存空间(此处指的线程栈)。当需要对一个共享变量操作时,线程会将这个数据从主存空间复制到自己的独立空间内进行操作,然后在某个时刻将修改后的值刷新到主存空间。这个中间时间就会发生许多奇奇怪怪的线程安全问题了,volatile就出来了,它保证读取数据时只从主存空间读取,修改数据直接修改到主存空间中去,这样就保证了这个变量对多个操作线程的可见性了。换句话说,被volatile修饰的变量,能保证该变量的 单次读或者单次写 操作是原子的。

但是线程安全是两方面需要的 原子性(指的是多条操作)和可见性。volatile只能保证可见性,synchronized是两个均保证的。
volatile轻量级,只能修饰变量;synchronized重量级,还可修饰方法。
volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞。

什么是竞争条件(race condition)?你怎样发现和解决的?

这是一道出现在多线程面试的高级阶段的问题。大多数的面试官会问最近你遇到的竞争条件,以及你是怎么解决的。有些时间他们会写简单的代码,然后让你检测出代码的竞争条件。可以参考我之前发布的关于java竞争条件的文章。在我看来这是最好的java线程面试问题之一,它可以确切的检测候选者解决竞争条件的经验。关于这方面最好的书是《java并发编程实战》。

竞争条件,在《java并发编程实战》叫做竞态条件:指设备或系统出现不恰当的执行时序,而得到不正确的结果。

下面是个最简单的例子,是一个单例模式实现的错误示范:

@notthreadsafe
public class lazyinitrace {
    private expensiveobject instance = null;
    public expensiveobject getinstance() {
        if (instance == null)
            instance = new expensiveobject();
        return instance;
    }
}

在上述例子中,表现一种很常见的竞态条件类型:“先检查后执行”。根据某个检查结果来执行进一步的操作,但很有可能这个检查结果是失效的!还有很常见的竞态条件“读取-修改-写入”三连,在多线程条件下,三个小操作并不一定会放在一起执行的。

如何对待竞态条件?
首先,警惕复合操作,当多个原子操作合在一起的时候,并不一定仍然是一个原子操作,此时需要用同步的手段来保证原子性。
另外,使用本身是线程安全的类,这样在很大程度上避免了未知的风险。

你将如何使用thread dump?你将如何分析thread dump?

在unix中你可以使用kill -3,然后thread dump将会打印日志,在windows中你可以使用”ctrl break”。非常简单和专业的线程面试问题,但是如果他问你怎样分析它,就会很棘手。

sigquit(kill -3 pid)用来打印java进程trace,并不会影响程序运行,不用担心他把程序杀死了;sigusr1(kill -10 pid)可触发进程进行一次强制gc。

java线程的状态转换介绍

后续分析要用到,所以此处穿插一下这个点:
java线程的状态转换

  • 新建状态(new)
    用new语句创建的线程处于新建状态,此时它和其他java对象一样,仅仅在堆区中被分配了内存。
  • 就绪状态(runnable)
    当一个线程对象创建后,其他线程调用它的start()方法,该线程就进入就绪状态,java虚拟机会为它创建方法调用栈和程序计数器。处于这个状态的线程位于可运行池中,等待获得cpu的使用权。
  • 运行状态(running)
    处于这个状态的线程占用cpu,执行程序代码。只有处于就绪状态的线程才有机会转到运行状态。
  • 阻塞状态(blocked)
    阻塞状态是指线程因为某些原因放弃cpu,暂时停止运行。当线程处于阻塞状态时,java虚拟机不会给线程分配cpu。直到线程重新进入就绪状态,它才有机会转到运行状态。
    阻塞状态可分为以下3种:
    1. 位于对象等待池中的阻塞状态(blocked in object’s wait pool):当线程处于运行状态时,如果执行了某个对象的wait()方法,java虚拟机就会把线程放到这个对象的等待池中,这涉及到“线程通信”的内容。
    2. 位于对象锁池中的阻塞状态(blocked in object’s lock pool):当线程处于运行状态时,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他线程占用,java虚拟机就会把这个线程放到这个对象的锁池中,这涉及到“线程同步”的内容。
    3. 其他阻塞状态(otherwise blocked):当前线程执行了sleep()方法,或者调用了其他线程的join()方法,或者发出了i/o请求时,就会进入这个状态。
  • 死亡状态(dead)
    当线程退出run()方法时,就进入死亡状态,该线程结束生命周期。

我们运行之前的那个死锁代码simpledeadlock.java,然后尝试输出信息(/*这是注释,作者自己加的*/):

/* 时间,jvm信息 */
2017-11-01 17:36:28
full thread dump java hotspot(tm) 64-bit server vm (25.144-b01 mixed mode):
/* 线程名称:destroyjavavm
编号:#13
优先级:5
系统优先级:0
jvm内部线程id:0x0000000001c88800
对应系统线程id(nativethread id):0x1c18
线程状态: waiting on condition [0x0000000000000000]  (等待某个条件)
线程详细状态:java.lang.thread.state: runnable  及之后所有*/
"destroyjavavm" #13 prio=5 os_prio=0 tid=0x0000000001c88800 nid=0x1c18 waiting on condition [0x0000000000000000]
   java.lang.thread.state: runnable
"thread-1" #12 prio=5 os_prio=0 tid=0x0000000018d49000 nid=0x17b8 waiting for monitor entry [0x0000000019d7f000]
/* 线程状态:阻塞(在对象同步上)
    代码位置:at com.leo.interview.simpledeadlock$b.run(simpledeadlock.java:56)
    等待锁:0x00000000d629b4d8 
    已经获得锁:0x00000000d629b4e8*/
   java.lang.thread.state: blocked (on object monitor)
    at com.leo.interview.simpledeadlock$b.run(simpledeadlock.java:56)
    - waiting to lock <0x00000000d629b4d8> (a java.lang.object)
    - locked <0x00000000d629b4e8> (a java.lang.object)
"thread-0" #11 prio=5 os_prio=0 tid=0x0000000018d44000 nid=0x1ebc waiting for monitor entry [0x000000001907f000]
   java.lang.thread.state: blocked (on object monitor)
    at com.leo.interview.simpledeadlock$a.run(simpledeadlock.java:34)
    - waiting to lock <0x00000000d629b4e8> (a java.lang.object)
    - locked <0x00000000d629b4d8> (a java.lang.object)
"service thread" #10 daemon prio=9 os_prio=0 tid=0x0000000018ca5000 nid=0x1264 runnable [0x0000000000000000]
   java.lang.thread.state: runnable
"c1 compilerthread2" #9 daemon prio=9 os_prio=2 tid=0x0000000018c46000 nid=0xb8c waiting on condition [0x0000000000000000]
   java.lang.thread.state: runnable
"c2 compilerthread1" #8 daemon prio=9 os_prio=2 tid=0x0000000018be4800 nid=0x1db4 waiting on condition [0x0000000000000000]
   java.lang.thread.state: runnable
"c2 compilerthread0" #7 daemon prio=9 os_prio=2 tid=0x0000000018be3800 nid=0x810 waiting on condition [0x0000000000000000]
   java.lang.thread.state: runnable
"monitor ctrl-break" #6 daemon prio=5 os_prio=0 tid=0x0000000018bcc800 nid=0x1c24 runnable [0x00000000193ce000]
   java.lang.thread.state: runnable
    at java.net.socketinputstream.socketread0(native method)
    at java.net.socketinputstream.socketread(socketinputstream.java:116)
    at java.net.socketinputstream.read(socketinputstream.java:171)
    at java.net.socketinputstream.read(socketinputstream.java:141)
    at sun.nio.cs.streamdecoder.readbytes(streamdecoder.java:284)
    at sun.nio.cs.streamdecoder.implread(streamdecoder.java:326)
    at sun.nio.cs.streamdecoder.read(streamdecoder.java:178)
    - locked <0x00000000d632b928> (a java.io.inputstreamreader)
    at java.io.inputstreamreader.read(inputstreamreader.java:184)
    at java.io.bufferedreader.fill(bufferedreader.java:161)
    at java.io.bufferedreader.readline(bufferedreader.java:324)
    - locked <0x00000000d632b928> (a java.io.inputstreamreader)
    at java.io.bufferedreader.readline(bufferedreader.java:389)
    at com.intellij.rt.execution.application.appmainv2$1.run(appmainv2.java:64)
"attach listener" #5 daemon prio=5 os_prio=2 tid=0x0000000017781800 nid=0x524 runnable [0x0000000000000000]
   java.lang.thread.state: runnable
"signal dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001778f800 nid=0x1b08 waiting on condition [0x0000000000000000]
   java.lang.thread.state: runnable
"finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001776a800 nid=0xdac in object.wait() [0x0000000018b6f000]
   java.lang.thread.state: waiting (on object monitor)
    at java.lang.object.wait(native method)
    - waiting on <0x00000000d6108ec8> (a java.lang.ref.referencequeue$lock)
    at java.lang.ref.referencequeue.remove(referencequeue.java:143)
    - locked <0x00000000d6108ec8> (a java.lang.ref.referencequeue$lock)
    at java.lang.ref.referencequeue.remove(referencequeue.java:164)
    at java.lang.ref.finalizer$finalizerthread.run(finalizer.java:209)
"reference handler" #2 daemon prio=10 os_prio=2 tid=0x0000000017723800 nid=0x1670 in object.wait() [0x00000000189ef000]
   java.lang.thread.state: waiting (on object monitor)
    at java.lang.object.wait(native method)
    - waiting on <0x00000000d6106b68> (a java.lang.ref.reference$lock)
    at java.lang.object.wait(object.java:502)
    at java.lang.ref.reference.tryhandlepending(reference.java:191)
    - locked <0x00000000d6106b68> (a java.lang.ref.reference$lock)
    at java.lang.ref.reference$referencehandler.run(reference.java:153)
"vm thread" os_prio=2 tid=0x000000001771b800 nid=0x604 runnable 
"gc task thread#0 (parallelgc)" os_prio=0 tid=0x0000000001c9d800 nid=0x9f0 runnable 
"gc task thread#1 (parallelgc)" os_prio=0 tid=0x0000000001c9f000 nid=0x154c runnable 
"gc task thread#2 (parallelgc)" os_prio=0 tid=0x0000000001ca0800 nid=0xcd0 runnable 
"gc task thread#3 (parallelgc)" os_prio=0 tid=0x0000000001ca2000 nid=0x1e58 runnable 
"vm periodic task thread" os_prio=2 tid=0x0000000018c5a000 nid=0x1b58 waiting on condition 
jni global references: 33
/* 此处可以看待死锁的相关信息! */
found one java-level deadlock:
=============================
"thread-1":
  waiting to lock monitor 0x0000000017729fc8 (object 0x00000000d629b4d8, a java.lang.object),
  which is held by "thread-0"
"thread-0":
  waiting to lock monitor 0x0000000017727738 (object 0x00000000d629b4e8, a java.lang.object),
  which is held by "thread-1"
java stack information for the threads listed above:
===================================================
"thread-1":
    at com.leo.interview.simpledeadlock$b.run(simpledeadlock.java:56)
    - waiting to lock <0x00000000d629b4d8> (a java.lang.object)
    - locked <0x00000000d629b4e8> (a java.lang.object)
"thread-0":
    at com.leo.interview.simpledeadlock$a.run(simpledeadlock.java:34)
    - waiting to lock <0x00000000d629b4e8> (a java.lang.object)
    - locked <0x00000000d629b4d8> (a java.lang.object)
found 1 deadlock.
/* 内存使用状况,详情得看jvm方面的书 */
heap
 psyounggen      total 37888k, used 4590k [0x00000000d6100000, 0x00000000d8b00000, 0x0000000100000000)
  eden space 32768k, 14% used [0x00000000d6100000,0x00000000d657b968,0x00000000d8100000)
  from space 5120k, 0% used [0x00000000d8600000,0x00000000d8600000,0x00000000d8b00000)
  to   space 5120k, 0% used [0x00000000d8100000,0x00000000d8100000,0x00000000d8600000)
 paroldgen       total 86016k, used 0k [0x0000000082200000, 0x0000000087600000, 0x00000000d6100000)
  object space 86016k, 0% used [0x0000000082200000,0x0000000082200000,0x0000000087600000)
 metaspace       used 3474k, capacity 4500k, committed 4864k, reserved 1056768k
  class space    used 382k, capacity 388k, committed 512k, reserved 1048576k

为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?

这是另一个非常经典的java多线程面试问题。这也是我刚开始写线程程序时候的困惑。现在这个问题通常在电话面试或者是在初中级java面试的第一轮被问到。这个问题的回答应该是这样的,当你调用start()方法时你将创建新的线程,并且执行在run()方法里的代码。但是如果你直接调用run()方法,它不会创建新的线程也不会执行调用线程的代码。

简单点来说:
new一个thread,线程进入了新建状态;调用start()方法,线程进入了就绪状态,当分配到时间片后就可以开始运行了。
start()会执行线程的相应准备工作,然后自动执行run()方法的内容。是真正的多线程工作。
而直接执行run()方法,会把run方法当成一个mian线程下的普通方法去执行,并不会在某个线程中执行它,这并不是多线程工作。

java中你怎样唤醒一个阻塞的线程?

这是个关于线程和阻塞的棘手的问题,它有很多解决方法。如果线程遇到了io阻塞,我并且不认为有一种方法可以中止线程。如果线程因为调用wait()、sleep()、或者join()方法而导致的阻塞,你可以中断线程,并且通过抛出interruptedexception来唤醒它。我之前写的《how to deal with blocking methods in java》有很多关于处理线程阻塞的信息。

这个我们先简单粗暴地对某些阻塞方法进行分类:
– 会抛出interruptedexception的方法:wait、sleep、join、lock.lockinterruptibly等,针对这类方法,我们在线程内部处理好异常(要不完全内部处理,要不把这个异常抛出去),然后就可以实现唤醒。
– 不会抛interruptedexception的方法:socket的i/o,同步i/o,lock.lock等。对于i/o类型,我们可以关闭他们底层的通道,比如socket的i/o,关闭底层套接字,然后抛出异常处理就好了;比如同步i/o,关闭底层channel然后处理异常。对于lock.lock方法,我们可以改造成lock.lockinterruptibly方法去实现。

在java中cyclibarriar和countdownlatch有什么区别?

这个线程问题主要用来检测你是否熟悉jdk5中的并发包。这两个的区别是cyclicbarrier可以重复使用已经通过的障碍,而countdownlatch不能重复使用。

还要注意一点的区别:
countdownlatch : 一个线程(或者多个), 等待另外n个线程完成某个事情之后才能执行。
cyclicbarrier : n个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。
这样应该就清楚一点了,对于countdownlatch来说,重点是那个“一个线程”, 是它在等待,而另外那n的线程在把“某个事情”做完之后可以继续等待,可以终止。而对于cyclicbarrier来说,重点是那n个线程,他们之间任何一个没有完成,所有的线程都必须等待。
从api上理解就是countdownlatch有主要配合使用两个方法countdown()和await(),countdown()是做事的线程用的方法,await()是等待事情完成的线程用个方法,这两种线程是可以分开的(下面例子:countdownlatchtest2),当然也可以是同一组线程(下面例子:countdownlatchtest);cyclicbarrier只有一个方法await(),指的是做事线程必须大家同时等待,必须是同一组线程的工作。

countdownlatch例子:

import java.util.concurrent.countdownlatch;
import java.util.concurrent.executorservice;
import java.util.concurrent.executors;
/**
 * 线程都准备完成后一起执行的例子
 * @author xuexiaolei
 * @version 2017年11月02日
 */
public class countdownlatchtest {
    private final static int thread_num = 10;
    public static void main(string[] args) {
        countdownlatch lock = new countdownlatch(thread_num);
        executorservice exec = executors.newcachedthreadpool();
        for (int i = 0; i < thread_num; i  ) {
            exec.submit(new countdownlatchtask(lock, "thread-" i));
        }
        exec.shutdown();
    }
    static class countdownlatchtask implements runnable{
        private final countdownlatch lock;
        private final string threadname;
        countdownlatchtask(countdownlatch lock, string threadname) {
            this.lock = lock;
            this.threadname = threadname;
        }
        @override public void run() {
            //循环多次是为了证明,countdownlatch只会阻挡一次
            for (int i = 0; i < 3; i  ) {
                system.out.println(threadname   " 准备完成");
                lock.countdown();
                try {
                    lock.await();
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
                system.out.println(threadname   " 执行完成");
            }
        }
    }
}
import java.util.concurrent.countdownlatch;
import java.util.concurrent.executorservice;
import java.util.concurrent.executors;
/**
 * 各个线程执行完成后,主线程做总结性工作的例子
 * @author xuexiaolei
 * @version 2017年11月02日
 */
public class countdownlatchtest2 {
    private final static int thread_num = 10;
    public static void main(string[] args) {
        countdownlatch lock = new countdownlatch(thread_num);
        executorservice exec = executors.newcachedthreadpool();
        for (int i = 0; i < thread_num; i  ) {
            exec.submit(new countdownlatchtask(lock, "thread-" i));
        }
        try {
            lock.await();
        } catch (interruptedexception e) {
            e.printstacktrace();
        }
        system.out.println("大家都执行完成了,做总结性工作");
        exec.shutdown();
    }
    static class countdownlatchtask implements runnable{
        private final countdownlatch lock;
        private final string threadname;
        countdownlatchtask(countdownlatch lock, string threadname) {
            this.lock = lock;
            this.threadname = threadname;
        }
        @override public void run() {
            system.out.println(threadname   " 执行完成");
            lock.countdown();
        }
    }
}

cyclicbarrier例子:

import java.util.concurrent.*;
/**
 *
 * @author xuexiaolei
 * @version 2017年11月02日
 */
public class cyclicbarriertest {
    private final static int thread_num = 10;
    public static void main(string[] args) {
        cyclicbarrier lock = new cyclicbarrier(thread_num, new runnable() {
            @override public void run() {
                system.out.println("大家都准备完成了");
            }
        });
        executorservice exec = executors.newcachedthreadpool();
        for (int i = 0; i < thread_num; i  ) {
            exec.submit(new countdownlatchtask(lock, "thread-" i));
        }
        exec.shutdown();
    }
    static class countdownlatchtask implements runnable{
        private final cyclicbarrier lock;
        private final string threadname;
        countdownlatchtask(cyclicbarrier lock, string threadname) {
            this.lock = lock;
            this.threadname = threadname;
        }
        @override public void run() {
            for (int i = 0; i < 3; i  ) {
                system.out.println(threadname   " 准备完成");
                try {
                    lock.await();
                } catch (brokenbarrierexception e) {
                    e.printstacktrace();
                } catch (interruptedexception e) {
                    e.printstacktrace();
                }
                system.out.println(threadname   " 执行完成");
            }
        }
    }
}

什么是不可变对象,它对写并发应用有什么帮助?

另一个多线程经典面试问题,并不直接跟线程有关,但间接帮助很多。这个java面试问题可以变的非常棘手,如果他要求你写一个不可变对象,或者问你为什么string是不可变的。

immutable objects(不可变对象)就是那些一旦被创建,它们的状态就不能被改变的objects,每次对他们的改变都是产生了新的immutable的对象,而mutable objects(可变对象)就是那些创建后,状态可以被改变的objects.

如何在java中写出immutable的类?
1. immutable对象的状态在创建之后就不能发生改变,任何对它的改变都应该产生一个新的对象。
2. immutable类的所有的属性都应该是final的。
3. 对象必须被正确的创建,比如:对象引用在对象创建过程中不能泄露(leak)。
4. 对象应该是final的,以此来限制子类继承父类,以避免子类改变了父类的immutable特性。
5. 如果类中包含mutable类对象,那么返回给客户端的时候,返回该对象的一个拷贝,而不是该对象本身(该条可以归为第一条中的一个特例)

使用immutable类的好处:
1. immutable对象是线程安全的,可以不用被synchronize就在并发环境中共享
2.immutable对象简化了程序开发,因为它无需使用额外的锁机制就可以在线程间共享
3. immutable对象提高了程序的性能,因为它减少了synchroinzed的使用
4. immutable对象是可以被重复使用的,你可以将它们缓存起来重复使用,就像字符串字面量和整型数字一样。你可以使用静态工厂方法来提供类似于valueof()这样的方法,它可以从缓存中返回一个已经存在的immutable对象,而不是重新创建一个。

/**
 * 不可变对象
 * @author xuexiaolei
 * @version 2017年11月03日
 */
public class immutableobjectperson {
    private final string name;
    private final string sex;
    public immutableobjectperson(string name, string sex) {
        this.name = name;
        this.sex = sex;
    }
    public string getname() {
        return name;
    }
    public string getsex() {
        return sex;
    }
}

你在多线程环境中遇到的常见的问题是什么?你是怎么解决它的?

多线程和并发程序中常遇到的有memory-interface、竞争条件、死锁、活锁和饥饿。问题是没有止境的,如果你弄错了,将很难发现和调试。这是大多数基于面试的,而不是基于实际应用的java线程问题。

此类问题请大家面试的时候提前准备,方便交流,如果实在找不出来,可以想想自己平时解决问题的思路,总结下来告诉考官。

js555888金沙老品牌的版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由思创斯整理,转载请注明出处:https://ispacesoft.com/141633.html

(0)

相关推荐

  • java jdk 动态代理(aop)使用及实现原理分析「建议收藏」代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息​一、什么是代理?二、java动态代理类三、jdk的动态代理怎么使用?四、动态代理怎么实现的?五、结论一、什么是代理?代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。代理模式uml图:简单结构示意图:为了保持行为的…

  • 黑马springboot2:java代码方式配置 package com.itheima.config; import com.alibaba.druid.pool.druiddatasource; import org.springframework.beans.factory.annotation.value; import org.sprin …

  • java程序设计智慧树网课答案_java编程基础及应用课后答案云课堂智慧职教java职业证书题库答案更多相关问题老子说“我有一颗愚人之心”,“若婴儿未孩”。下面哪一个选项最近此义:“大多数人都认为x是真的,所以x是真的。”属于()的论证方式。“大学语文”课程的前身是“大一国文”课程。()权益法下核算的长期股权投资,会导致投资企业投资收益发生增减变动的是( )“大众创业,万众创新”号召是在哪一年提出的?“大弦嘈嘈如急雨,小弦切切如私语。嘈嘈切切错杂弹,大珠小…

    2022年12月27日
  • leetcode.76 minimum window substring (java)leetcode.76 minimum window substring given a string s and a string t, find the minimum window in s which will contain all the characters in t in compl …

  • npoi开发文档_java interface简介:npoi是poi(apatch的一个开源项目)项目的.net版本,最初的poi只用于java来操作excelorword等微软ole2组件项目。使用npoi可以完成在你没有安装office或者相应环境的机器上对word/excel文档进行读写。使用案例分享(npoi针对datatable导出excel):完成此任务应该准备的dll:npoi.dll,金沙1005官网下载链接:http://npoi…

  • java截取字符串的常见方法「建议收藏」转自:https://blog.csdn.net/zjx2016/article/details/74557301在项目中经常会遇到截取字符串的需求,这里重点介绍两种常见的截取字符串方法。方法一:通过split()将正则传入split()。返回的是一个字符串数组类型。不过通过这种方式截取会有很大的性能损耗,因为分析正则非常耗时。stringstr="53285964@qq.co…

    2022年11月28日
  • java环境变量配置有什么用_java环境变量大一时装jdk时对着网上的摆弄了好久,然后自己出了个图文教程在同学间流传甚广,最近同学帮大一的问我怎么装,此处将当时的教程完整的复制过来。希望能解决你们的问题。应用java程序之前必须设置系统变量,就像每个生物都得有适合自己生存的环境一样,只有设置成功了才能成功运行jav软件建议按照系统默认路径安装到c盘,如果你是高手就算了。系统变量设置步骤:(以下图片教…

  • java.library.path在哪?[亲测有效]classpath类的路径,在编译运行java程序时,如果有调用到其他类的时候,在classpath中寻找需要的类。一般这个路径是你的java项目引用的jdk下的jre目录的bin目录。java/jdk/jre/bin.path路径,是java编译时需要调用的程序(如java,javac等)所在的地方。

发表回复

您的电子邮箱地址不会被公开。

联系金沙1005

关注“java架构师必看”公众号

回复4,添加站长微信。

附言:ispacesoft.com网而来。

关注微信
网站地图