java多线程最基础的问题
原文(http://java-success.blogspot.com.au/2011/09/java-multi-threading-interview.html)
1、Q:为什么面试官喜欢问多线程的知识?
A:因为它不简单,而且如果想写出可拓展、高吞吐的系统它是必备的知识。
2、Q:进程和线程的区别?
A:线程是运行在进程内部的,进程可以包含多个线程,线程有时也叫轻量级的进程。
note:JVM是一个进程,运行于其中的线程是共享堆内存的。这就是为什么这些线程可以访问同一个对 象。线程有自己的栈内存。这也是(一个线程的方法调用以及它的局部变量对于其他线程是线程 安全的)原因。
3、Q:创建线程的不同方法?
A:
继承Thread类
class Counter extends Thread { //method where the thread execution will start public void run(){ //logic to execute in a thread } //let’s see how to start the threads public static void main(String[] args){ Thread t1 = new Counter(); Thread t2 = new Counter(); t1.start(); //start the first thread. This calls the run() method. t2.start(); //this starts the 2nd thread. This calls the run() method. } }
实现Runnable接口
class Counter extends Base implements Runnable{ //method where the thread execution will start public void run(){ //logic to execute in a thread } //let us see how to start the threads public static void main(String[] args){ Thread t1 = new Thread(new Counter()); Thread t2 = new Thread(new Counter()); t1.start(); //start the first thread. This calls the run() method. t2.start(); //this starts the 2nd thread. This calls the run() method. } }
使用Executor framework(线程池的方式高效)
import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Sum implements Callable<String> { private static final int NO_OF_THREADS = 3; int maxNumber; public Sum(int maxNumber) { this.maxNumber = maxNumber; } /** method where the thread execution will start * this can return a value */ public String call(){ int sum = 0; for (int i = 0; i <= maxNumber; i++) { sum += maxNumber; } return Thread.currentThread().getName() + " count is " + sum; } /** main thread. Alwyas there by default. **/ public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(NO_OF_THREADS); // create a pool of 3 threads List<Future<String>> list = new ArrayList<Future<String>>(10); // provides facility to return results asynchronously for (int i = 10000; i < 10100; i++) { Callable<String> worker = new Sum(i); // create worker threads Future<String> submit = executor.submit(worker); // add callables to the work queue list.add(submit); // provides facility to return results asynchronously } //process the results asynchronously when each thread completes its task for (Future<String> future : list) { try { System.out.println("Thread " + future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } executor.shutdown(); System.out.println("Finished all threads"); } }
4、Q:你会选择哪种创建线程的方式,为什么?
A:选择实现Runnable接口的方式,因为这样当你可以继承其他类,因为你可以实现多个接口,不能继承 多个类。上面的例子中我们还需要继承Base类,所以选择实现Runnable是比较好的选择。我们再看 看前两个例子里是如何开始执行线程的。面向对象的思想是你应该继承一个类,使得子类behavior不 同于它的父类,通过实现Runnable而不是继承Therad,你是在说Counter是Base的子类并且会作为 一个线程运行。
5、Q:简单介绍下线程的状态?(原文是high-level status 不知什么意思)
A:话不多说,直接上图
Runnable:执行start()后线程变为Runnable状态,但不一定是立刻运行,它缓存于内存(pooled), 等待线程调度器(基于线程优先级)的调度。
MyThread aThread = new MyThread(); aThread.start(); //becomes runnable
Running:进程正在执行当前线程的状态。它一直运行直到阻塞或者自愿放弃执行(通过调用 Thread.yield())。由于线程切换的优先级高,所以不应该频繁使用yield。
Wailting:线程处于阻塞状态,等待一些外部进程执行完,比如文件io。通过调用wait()使线程进入等待 状态,直到其他线程调用notify()或者notifyAll()。
Sleeping:线程被强制睡眠,通过调用两个overload的方法
thread.sleep(milliseconds) Thread.sleep(milliseconds,nanoseconds)
Dead:线程完成工作。
Blocked on I/O: Will move to runnable after I/O condition like reading bytes of data etc changes.
Blocked on synchronization : will move to running when a lock is acquired.
6、Q:yield和sleeping的不同,sleep()和wait()的不同?
A:执行yield()线程由running进入runnable状态,执行sleep()线程由running状态进入waiting状态。
wait(1000)使线程最多sleep1秒,如果notify()或notifyAll()被执行,线程会sleep少于一秒。
7、Q:为了线程安全而阻塞代码为什么叫‘synchronized’,而不是叫‘lock’或者‘locked’?
A:当方法或代码块被‘synchronized’修饰时,共享数据的内存(例如 heep)是‘synchronized’。就是说:
当进入‘synchronized’修饰并且锁被其它线程拥有的代码块或方法时,开始执行前线程会关注内存中被 锁的对象的任何改变来确数据的最新(就是确保多线程不会出现数据不一致问题)。
当synchronized块执行完,线程准备释放锁时,所有对被锁对象的改变会被写回主堆内存,这样其它 线程就会回去对象的最新信息。
这就是为什么叫‘synchronized’,而不叫‘locked’,这也是哪些不可变对象天生就是线程安全的原因, 一旦被创建就不可以被改变。
8、Q:在monitor内部synchronization是如何实现的?(注 monitor A monitor is mechanism to control concurrent access to an object.监控是一种控制并发访问的机制。)你可以使用什么级别的同步?同 步方法和同步代码块的区别?
A:java中每个对象都有个锁,通过使用'synchronized'关键字,线程可以从对象那获取 锁,‘synchronized’可以应用于方法级别(粗粒度锁,可能会影响性能)或者代码块级别(细粒度 锁)。经常使用方法级别的显得过于粗糙。锁定整个方法会阻止对方法内任何资源的访问。
对于静态方法,你获取的是类级别的锁。
-------------------------------------------下面是稍微复杂一点的问题 -------------------------------------------------------
9、Q:为什么线程同步重要?
A:没有同步控制两个线程可以同时修改同一个对象,这会引起脏数据和错误,synchronized也会引起死 锁。
10、Q:解释下ThreadLocal类?
A:ThreadLocal简化了并发编程,通过使得它内部的对象不在线程间共享。它可以封装多线程环境下的 类使得它们线程安全,也可以创建per-thread-singleton
11、Q:什么事守护线程(daemon thread)?
A:守护线程是服务线程或后台线程,它们通常拥有较低的优先级并且提供基本的服务,GC就是一个例 子。所有非守护进程完成了,jvm也就停止了。jvm有一个默认的主线程,它是非守护线程,默认创 建的线程都是非守护线程,Thread.setDaemon(true)使线程变为守护线程。
12、Q:线程是如何通信的?用stack怎样实现生产者消费者通信?
A:wait(),notify(),notifyAll()都是线程间通信的方法。生产者和消费者作为两个线程同时读写同一个文 件,这个文件得加synchronized,生产者完成生产得通知消费者消费,消费者消费完成得通知生产 者生产。
public class ConsumerProducer { private int count; public synchronized void consume() { while (count == 0) { // keep waiting if nothing is produced to consume try { wait(); // give up lock and wait } catch (InterruptedException e) { // keep trying } } count--; // consume System.out.println(Thread.currentThread().getName() + " after consuming " + count); } public synchronized void produce() { count++; //produce System.out.println(Thread.currentThread().getName() + " after producing " + count); notifyAll(); // notify waiting threads to resume } }
public class ConsumerProducerTest implements Runnable { boolean isConsumer; ConsumerProducer cp; public ConsumerProducerTest(boolean isConsumer, ConsumerProducer cp) { this.isConsumer = isConsumer; this.cp = cp; } public static void main(String[] args) { ConsumerProducer cp = new ConsumerProducer(); //shared by both threads to communicate Thread producer = new Thread(new ConsumerProducerTest(false, cp)); Thread consumer = new Thread(new ConsumerProducerTest(true, cp)); producer.start(); consumer.start(); } @Override public void run() { for (int i = 1; i <= 10; i++) { if (!isConsumer) { cp.produce(); } else { cp.consume(); } } //try with introducing a sleep for 100ms. } }
13、Q:为什么wait()、notify()、notifyAll()定义在Object类中,而不是Thread类中?
A:因为每个对象都有一个与之对应的Monitor,是线程可以请求获取锁或者放弃l锁。
14、Q:join()方法做什么用的?
A:t.join(5000)是说当前线程等待t线程完成,最多等5秒,t.join()是等待直到t线程完成。
import java.util.Date; public class RunnableTask implements Runnable { @Override public void run() { Thread thread = Thread.currentThread(); System.out.println(thread.getName() + " at " + new Date()); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class TaskManager { public static void main(String[] args) throws InterruptedException { RunnableTask task = new RunnableTask(); //threads 1-3 are run sequentially Thread thread1 = new Thread(task, "Thread-1"); Thread thread2 = new Thread(task, "Thread-2"); Thread thread3 = new Thread(task, "Thread-3"); thread1.start(); //invokes run() on RunnableTask thread1.join(); // main thread blocks (for 10 seconds) thread2.start(); //invokes run() on RunnableTask thread2.join(); // main thread blocks (for 10 seconds) thread3.start(); //invokes run() on RunnableTask thread3.join(); // main thread blocks (for 10 seconds) Thread thread4 = new Thread(task, "Thread-4"); Thread thread5 = new Thread(task, "Thread-5"); Thread thread6 = new Thread(task, "Thread-6"); thread4.start(); //invokes run() on RunnableTask thread5.start(); //invokes run() on RunnableTask thread6.start(); //invokes run() on RunnableTask } }
Thread-1 at Fri Mar 02 16:59:22 EST 2012 Thread-2 at Fri Mar 02 16:59:32 EST 2012 Thread-3 at Fri Mar 02 16:59:42 EST 2012 Thread-4 at Fri Mar 02 16:59:47 EST 2012 Thread-6 at Fri Mar 02 16:59:47 EST 2012 Thread-5 at Fri Mar 02 16:59:47 EST 2012
15、Q:两个不同的线程同时执行到同一个对象的两个不同的synchronized方法,它们会都继续执行吗?
A:不会,只有一个线程可以获取方法的锁对于一个对象。每个对象有一个同步锁。
16、Q:解释线程的IO阻塞?
A:有时线程也会阻塞而不是因为对象锁,多个线程同时需要执行IO就会引起IO阻塞,当处于 synchronized内的代码io阻塞时,会使整个类都冻上(frozen)。
17、Q:假设你有循环引用的对象,当你的程序不在使用它是(栈内存不再保留对循环的引用)时,它们会 被垃圾回收吗?
A:会被回收。
18、Q:Which of the following is true?
a) wait( ), notify( ) ,notifyall( ) are defined as final & can be called only from within a synchronized method
b) Among wait( ), notify( ), notifyall( ) the wait() method only throws IOException
c) wait( ),notify( ),notifyall( ) & sleep () are methods of object class
A: a and b. The c is wrong because the sleep method is a member of the Thread class.The other methods are members of the Object class.
19、Q:列举多线程的引发的问题,说明如何引发这些问题的?
A:DeadLock, LiveLock, and Starvation
http://www.cnblogs.com/yuwenxing/archive/2012/05/31/2528751.html
20、Q:你直接调用run()方法(而不是通过start()方法)会发生什么?
A:直接调用run()方法会同步的执行run()方法(在同一个线程中),就和普通方法调用一样。
调用start()方法会执行新的线程和调用run()方法。start()方法是立刻就返回,新线程会等到run()方 法返回后才会继续执行。
相关推荐
10. **多线程**:理解线程的概念,如何创建和管理线程,以及同步和互斥的概念,如synchronized关键字的应用。 11. **异常分类**:系统异常和自定义异常的创建,以及如何使用try-catch-finally结构来处理异常。 12....
8. **多线程**:了解线程的基本概念,如何创建和管理线程,以及同步与互斥。 9. **接口与抽象类**:理解接口的作用,如何定义接口和实现接口,以及抽象类的区别与应用。 10. **枚举与注解**:介绍Java中的枚举类型...
11. **多线程**:Java支持多线程编程,理解线程的创建、同步和通信对提高程序效率至关重要。 12. **文件与目录操作**:Java提供了丰富的API进行文件和目录的操作,如创建、删除、读写文件,以及遍历目录等。 在...
根据给定的头条陕西Java面试题及其答案,我们可以总结出一系列重要的Java知识点: ### 1. 面向对象语言的三大特性 面向对象编程(OOP)的核心特性包括:**封装**、**继承**和**多态**。 - **封装**:通过隐藏对象的...
6. **线程与并发**:掌握Java多线程编程,包括线程的创建、同步与通信,以及线程池的使用。 7. **函数式编程**:了解Java 8引入的Lambda表达式和Stream API,如何进行函数式编程,提高代码的简洁性和效率。 8. **...
6. **多线程**:Java提供内置的多线程支持,学习如何创建和管理线程,以及同步和互斥的概念。 7. **函数式编程**:Java 8引入了Lambda表达式和Stream API,使得函数式编程风格在Java中成为可能,这对于优化代码和...
解析:volatile 能够部分解决多线程环境中的数据同步问题,但无法保证原子性,因此在需要原子操作的场景下需要配合其他同步机制使用。 问题 10:Java 中的集合框架有哪些主要的接口和类,它们之间有什么关联?答案...
17. 线程与Runnable:Java中,线程的执行由`run()`方法决定。创建线程有两种方式:继承Thread类并重写`run()`,或实现Runnable接口并实现`run()`方法。 18. 关键字(键或码):在数据库中,表SC的关键字是学号和课...
英译汉,通信方面的一些知识,比较简单。 软件V模型填空。 V模型大体可以划分为下面几个不同的阶段步骤,既需求分析、概要设计、祥细设计、编码、单元测试、集成测试、系统测试、验收测试。 3、填空题:基础知识,...
第二部分 C#程序设计基础.28 第四章 数 据 类 型 .28 4.1 值 类 型 .28 4.2 引 用 类 型 .33 4.3 装箱和拆箱 .39 4.4 小 结 .42 第五章 变量和常量 .44 5.1 变 量 .44 5.2 常 量 .46 5.3 小 结 .47 ...