Java多线程面试题

1.线程有几种状态?

在Java当中,线程通常都有五种状态,创建,就绪,运行,阻塞,死亡

第一是创建状态。在生成线程对象(new操作),并没有调用该对象的start方法,这是线程处于创建状态;

第二是就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,等待cpu分配时间片及其资源环境。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态

第三是运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。

第四是阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend等方法都可以导致线程阻塞。阻塞完后,会变为就绪状态

第五是死亡状态。如果一个线程的run方法执行结束,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪状态。

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

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

简单来说:new一个Thread,线程进入新建状态,调用start,线程处于就绪状态,当分配到时间片就可以开始运行。start()会执行线程相应准备工作,然后自动执行run().而直接执行run(),会把其当成main线程下的一个普通方法执行,并不会在某个线程中执行它

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

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

volatile关键字的作用是:保证变量的可见性。在java内存结构中,每个线程有自己独立的内存空间,这里指线程栈,当需要对一个共享变量操作时,线程会将数据从主存空间复制到自己的独立空间进行操作,然后在某个时刻将修改后的值刷新到主存空间。注意。

这个中间时间就会发生许多奇奇怪怪的线程安全问题了,volatile就出来了,它保证读取数据时只从主存空间读取,修改数据直接修改到主存空间中去,这样就保证了这个变量对多个操作线程的可见性了。换句话说,被volatile修饰的变量,能保证该变量的 单次读或者单次写 操作是原子的。

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

4.什么是原子操作,Java中的原子操作是什么?

非常简单的java线程面试问题,接下来的问题是你是否需要同步一个原子操作。原子操作是不可分割的操作,一个原子操作中间是不会被其他线程打断的。所以不需要同步一个原子操作

i++不是一个原子操作,它包含读取-修改-写入操纵,在多线程下是不安全的,java内存模型允许将64位读写操作分解为2个32位的操作,所以对long和double的单次读写操作并不是原子操作,注意volitile可以使他们成为原子操作

5.现在有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();
        }
    }

join()源码:

/**
         *  Waits at most <code>millis</code> milliseconds for this thread to  
         * die. A timeout of <code>0</code> means to wait forever.    
         */
        //此处A timeout of 0 means to wait forever 字面意思是永远等待,其实是等到t结束后。
        public final synchronized void join(long millis)    throws InterruptedException {
            long base = System.currentTimeMillis();
            long now = 0;
     
            if (millis < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
            
            if (millis == 0) {
                while (isAlive()) {
                    wait(0);
                }
            } else {
                while (isAlive()) {
                    long delay = millis - now;
                    if (delay <= 0) {
                        break;
                    }
                    wait(delay);
                    now = System.currentTimeMillis() - base;
                }
            }
        }

说明:

从代码中,我们可以发现。当millis==0时,会进入while( isAlive() )循环;即只要子线程是活的,主线程就不停的等待。
我们根据上面解释join()作用时的代码来理解join()的用法!
wait()的作用是让“当前线程”等待,而这里的“当前线程”是指当前运行的线程。虽然是调用子线程的wait()方法,但是它是通过“主线程”去调用的;所以,休眠的是主线程,而不是“子线程”!

这样理解: 例子中的Thread t只是一个对象 , isAlive()判断当前对象(例子中的t对象)是否存活, wait()阻塞的是当前执行的线程(例子中的main方法)

可以看出,Join方法实现是通过wait()。 当main线程调用t.join时候,main线程会获得线程对象t的锁(wait 意味着拿到该对象的锁),调用该对象的wait(),直到该对象唤醒main线程 ,比如退出后。这就意味着main 线程调用t.join时,必须能够拿到线程t对象的锁。

 

6.在java中wait和sleep方法的不同?

在等待时wait会释放锁,sleep一直持有锁.wait通常用于线程间交互,sleep通常用于暂停执行。

start stop yield sleep interrupt join(Thread常用方法)

wait/nofity/notifyAll(Object线程有关方法)

 

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

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

/**
 * 简单死锁程序
 *      lockA、lockB分别是两个资源,线程A、B必须同是拿到才能工作
 *      但A线程先拿lockA、再拿lockB
 *      线程先拿lockB、再拿lockA
 * @author liqiang
 * @date   2019-8-11
 */
public class SimpleDeadLock{
    public static void main(String[] args) {
        Object lock1 = new Object();
        Object lock2 = new Object();
        A a = new A(lock1, lock2);
        B b = new B(lock1, lock2);
        a.start();
        b.start();
    }
}

class A extends Thread{
    private final Object lock1;
    private final Object lock2;
    public A(Object lock1,Object lock2){
        this.lock1 = lock1;
        this.lock2 = lock2;
    }
    public void run(){
        synchronized (lock1){

            try{
                Thread.sleep(1000);
                synchronized (lock2){
                    System.out.println("A");
                }
            }catch(InterruptedException e){

            }

        }
    }
}

class B extends Thread{
    private final Object lock1;
    private final Object lock2;
    public B(Object lock1,Object lock2){
        this.lock1 = lock1;
        this.lock2 = lock2;
    }
    public void run(){
        synchronized (lock2){
            try{
                Thread.sleep(1000);
                synchronized (lock1){
                    System.out.println("B");
                }
            }catch(InterruptedException e){

            }

        }
    }
}

 本文参考:

https://blog.youkuaiyun.com/ll666634/article/details/78615505

Java多线程编程核心技术

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值