java多线程的问题---面试必备

1、什么是线程?

线程是指程序在执行过程中,能够执行程序代码的一个执行单元。

4种状态:运行、就绪、挂起和结束

线程是程序执行的最小单元,一个进程可以拥有多个线程,各个线程之间共享程序的内存空间(代码段、数据段和堆空间)以及一些进程级的资源,但是各个线程拥有自己的栈空间。


2、为什么使用多线程?

多线程可以减少程序的响应时间。可以把耗时的线程分配到一个单独的线程去执行

与进程相比,线程的创建和切换开销更小。运行于同一进程内的线程共享代码段、数据段,线程的启动或切换的开销比进程要少很多。

多cpu或多核的计算机本身就具有执行多线程的能力,如果使用单个线程,将无法重复利用计算机资源


3、同步和异步?

多线程的环境中,经常碰见数据的共享问题,当多个线程需要访问同一个资源时,他们需要一某种顺序来确保该资源在某个时刻只能被一个线程使用。

实现同步操作,必须要获得每一个线程对象的锁。以保证同一时刻只有一个线程能够进入临界区(访问互斥资源的代码块),并且在这个锁被释放之前,其他线程就不能再进入临界区。关键字:synchronized。以很大的系统开销为代价。

实现同步的方式:

1)同步代码块,

synchronized(任意对象){线程要操作的共享数据}

2)同步方法,

推荐public synchronized void method(){

可能产生线程安全问题的代码}

wait()方法与notify()方法:在synchronized代码被执行期间,线程可以调用对象的wait()方法,释放对象锁,进入等待状态,并且可以调用notify()方法或notifyAll()方法通知正在等待的其他线程。

notify()方法仅唤醒一个线程并允许它去获得锁,notifyAll()方法唤醒所有的等待这个对象的线程并允许他们去获得锁。

JDK1.5新增Lock接口(java.util.concurrent.locks)以及他的实现类ReetrantLock(重入锁),用于实现多线程的同步, lock实现了比使用synchronized方法和语句可获得更广泛的锁定操作。

void lock() //以阻塞的方式获取锁

tryLock() //以非阻塞的方式获取锁

tryLock(long timeout,TimeUnit unit) //如果获取了锁,立即返回true,否则会等待参数给定的时间单元,

lockInterruptibly() //如果获取了锁,立即返回true,当前线程处于休眠状态,直至获得锁,他与lock()方法的区别是如果lock()方法获取不到锁,会一直处于阻塞状态。

void unlock() //释放锁

异步与非阻塞类似,每个线程包含了运行时自身所需要的数据或方法,因此,在进行输入输出处理时,不必关心其他线程的状态或行为。


4、如何实现java多线程?

三种方法(1)继承Thread类,重写run方法。

启动线程的唯一方法是通过Thread类的start()方法。

(2)实现Runnable接口,并实现该接口的run()方法 推荐

步骤:自定义类并实现runnable接口,实现run方法;创建Thread对象,用实现Runnable接口的对象作为参数实例化该Thread对象;调用Thread的start方法。

(3)实现callable接口,重写call()方法 callable接口实际上是属于Executor框架中的功能类,提供了比Runnable更强大的功能,callable可以在任务结束后提供一个返回值,runnable无法提供这个功能,callable中的call()方法可以抛出异常,Runnable的run方法不能抛出异常;运行callable可以拿到一个Future对象,future对象表示异步计算的结果。

声明线程池:ExecutorService threadPool = Executors.newSingleThreadExecutor();

池中添加线程:Future<String> future =threadPool.submit(new CallableTest());

try{

syso("wait thread to finish");

syso(future.get());//等待线程结束,并获取返回结果

}


5、sleep()和wait()方法区别?

sleep()和wait()都是使线程暂停执行一段时间的方法

区别 1)原理不同 sleep方法是Thread类的静态方法,是线程用来控制自身流程的,他会使此线程暂停一段时间,机会让给其他线程,时间到后,线程会自行“苏醒” wait方法是object类的方法,用于线程间的通信,这个方法会使当前拥有该对象锁的进程等待,直到其他线程调用notify()方法时才醒来。

2)对锁的处理机制不同。 sleep方法只是让线程暂停,不涉及线程间的通信,因此,调用sleep()方法并不会释放锁,而wait()不同,线程会释放掉他占用的锁,从而使线程所在的对象中的其他synchronized数据可以被别的线程使用。

3)使用区域不同。wait必须放在同步控制方法或同步语句块中使用,而sleep()方法则可以放在任何地方使用。 推荐使用wait()方法。

引申:sleep方法和yield方法的区别?

1)sleep给其他线程运行时不考虑优先级,yield会给同优先级或更高优先级的线程运行机会

2)执行sleep后转入阻塞状态,而yield方法只是使当前线程重新回到可执行状态,所以有可能在进入到可执行状态后马上又被执行。

3)sleep声明抛出InterruptedException,而yield方法没有声明任何异常。

4)sleep方法比yield方法(跟操作系统有关)具有更好的可移植性。


6、终止线程的方法?

1)Thread.stop(),终止线程时,他会释放已经锁定的所有监事资源。会导致程序执行的不确定性

2)suspend(),这个方法容易产生死锁,调用suspend()方法时不会释放锁,互斥资源的竞争会导致死锁的发生。

java语言已经不推荐这两种方法,那么如何终止线程?让线程自行结束进入dead状态。可以设置一个flag来控制循环是否执行,通过这种方法来让线程离开run()方法从而终止线程,如下所示:

public class MyThread implements Runnable{

    private volatile Boolean flag;

    public void stop(){

        flag = false;

    }

    public void run(){

        while(flag)

        ;//do something

    }

}

问题:当线程处于非运行状态时(sleep方法被调用或当wait方法被调用或I/O阻塞时),这个方法不管用。

此时可以用interrupt来打破阻塞的情况,当interrupt()被调用时,会抛出InterruptedException异常,可以通过在run()方法中捕获这个异常来让线程安全退出。

public class MyThread{
    public static void main(String[] args){
    Thread thread = new Thread(new Runable(){
        public void run(){
            syso("thread go to sleep");
            try{
            //用休眠来模拟线程被阻塞            
                Thread.sleep(5000);
                syso("thread finish");
            }catch(InterruptedException e){
                syso("thread is interrupted");
                }
        }
    });
    thread.start();
    thread.interrupt();
    }
}

如果程序因为IO而停滞,基本上要等到IO完成才能离开这个状态,无法使用Interrupte()方法使程序离开run()方法。思路:触发一个异常,而这个异常与所使用的I/O相关。


7、synchronized与lock的差异?  常常问倒,例如今年的网易面试题

这两锁机制来实现对某个共享资源的同步:synchronized使用Object对象本身的notify,wait,notifyAll调度机制,而lock可以使用condition进行线程间的调度。

区别 1)用法不一样,synchronized在需要被同步的对象中加入。Lock需要显示地指定起始位置和终止位置,synchronized是托管给JVM执行的,Lock的锁定是通过代码实现的。

2)性能不一样

jdk5中增加Lock接口的实现类ReentrantLock,他不仅拥有和Synchronized相同的并发性和内存语义,还多了锁投票,定时锁,等候和中断锁等。在资源竞争不是很激烈时,Synchronized的性能要优于ReentrantLock,但竞争激烈时,Synchronized的性能下降得很快,但ReentrantLock的性能基本保持不变。

3)锁机制不一样

Synchronized获取和释放锁的方式都是在块结构中,当获取多个锁时必须以相反的顺序释放,并且是自动解锁,不会因为出了异常导致锁没有被释放从而引发死锁;而Lock则需要开发人员手动释放,并且必须在finally块中释放,Lock还提供了更强大的功能,他的tyLock()方法可以采用非阻塞的方式去获取锁。

笔试题:当一个线程进入一个对象的一个synchronized()方法后,其他线程是否可进入此对象的其他方法?

当一个线程进入一个对象的一个synchronized()方法后,其他线程是否可进入此对象的其他方法取决于方法本身,如果该方法是非synchronized()方法,那么是可以访问的。如下所示:

如果其他方法是静态方法(使用static修饰的方法),他用的同步锁是当前类的字节码,与非静态的方法不能同步(因为非静态的方法用的是this),因此,静态方法可以被调用。


8、什么是守护线程?

java提供了两种线程:守护线程和用户线程

守护线程称为“服务进程”“后台线程”,指的是在程序运行时在后台提供一种通用服务的线程,这种线程并不属于程序中不可或缺的部分。

守护线程优先级较低,将一个用户线程设置为守护线程的方法就是在调用start()方法启动线程之前调用对象的setDaemon(true)方法,若将以上参数设置为false,则表示的是用户进程模式。当一个守护线程中产生了其他线程,那么这些新产生的线程默认还是守护线程。

守护线程的一个典型的例子是:垃圾回收器。只要JVM启动,它始终在运行,实时监控和管理系统中可以被回收的资源。


9、join()方法的作用是什么?

join方法的作用是让调用该方法的线程在执行完run()方法后,再执行join方法后面的代码。简单点说,就是将两个线程合并,用于实现同步功能。

祝大家面试顺利,找到理想的工作,一起加油!

发布了26 篇原创文章 · 获赞 18 · 访问量 9758

猜你喜欢

转载自blog.csdn.net/qq_28959087/article/details/81781851