`

java多线程最基础的问题及答案(译)

阅读更多

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()方                 法返回后才会继续执行。

 

 

  • 大小: 31.8 KB
  • 大小: 34.7 KB
  • 大小: 42.5 KB
  • 大小: 37.6 KB
  • 大小: 30.8 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics