1 线程和进程的区别

参考答案

线程和进程的区别如下:

1)一个进程至少有一个线程。线程的划分尺度小于进程,使得多线程程序的并发性高。 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

2)线程在执行过程中与进程的区别在于每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。

3)从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用来实现进程的调度和管理以及资源分配。

2 简述线程的状态及其转换

参考答案

线程的状态有五种,本别是New、Runnable、Block、Running以及Dead。

线程之间的状态转换如下:

1)New,创建一个线程,但是线程并没有进行任何的操作。

2)Runnable,新线程从New状态,调用start方法转换到Runnable状态。线程调用start方法向线程调度程序(JVM或者是操作系统)注册一个线程,这个时候一切就绪只等CPU的时间。

3)Running,从Runnable状态到Running状态,线程调度根据调度策略的不同调度不同的线程,被调度执行的线程进入Running状态,执行run方法。

4)Dead状态,从Running状态到Runnable,run方法运行完毕后,线程就会被抛弃,线程就进入Dead状态。

5)Block状态,从Running状态到Block状态,如果线程在运行的状态中因为I/O阻塞、调用了线程的sleep方法以及调用对象的wait方法则线程将进入阻塞状态,直到这些阻塞原因被结束,线程进入到Runnable状态。

3 简述线程的两种创建方式以及它们的区别

参考答案

创建线程的两种方式:

1)使用Thread创建线程。Thread类是线程类,其每一个实例表示一个可以并发运行的线程。我们可以通过继承该类并重写run方法来定义一个具体的线程。其中重写run方法的目的是定义该线程要执行的逻辑。启动线程时调用线程的start()方法而非直接调用run()方法。start()方法会将当前线程纳入线程调度,使当前线程可以开始并发运行。当线程获取时间片段后会自动开始执行run方法中的逻辑。

2)使用Runnable创建线程。实现Runnable接口并重写run方法来定义线程体,然后在创建线程的时候将Runnable的实例传入并启动线程。

两种创建线程方式的区别:

使用Thread创建线程,编写简单,可以直接操纵线程,无需使用Thread.currentThread(),但是不能够再继承其他类。

使用Runnable创建线程可以将线程与线程要执行的任务分离开减少耦合,同时Java是单继承的,定义一个类实现Runnable接口,这样该类还可以继承自其他类。

4 下列关于线程API说法正确的是

A.Thread类的静态方法currentThread方法可以用于获取运行当前代码片段的线程。

B.Thread类的getName方法返回该线程的名称。

C.Thread类的getId方法返回该线程的标识符。

D.Thread类的getPriority方法获取线程的状态

参考答案

本题正确答案为ABC。

D选项说法错误,Thread类的getPriority方法用于返回线程的优先级。

5 编写计时线程,每隔5秒钟输出当前的日期-时间,主线程结束后计时完毕

参考答案

实现此案例需要按照如下步骤进行。

步骤一:创建线程,实现每隔5秒输出当前日期-时间

首先新建类Timing;然后在该类的main方法中,使用内部类创建线程的方式创建线程d;最后,在线程d中,实现每隔5秒输出当前日期-时间,代码如下所示:

import java.text.SimpleDateFormat;
import java.util.Date;

public class Timing {
#cold_bold	public static void main(String[] args) {
#cold_bold		Thread d = new Thread() {
#cold_bold			public void run() {
#cold_bold				while (true) {
#cold_bold					Date d=new Date();
#cold_bold					SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh-ss-mm");
#cold_bold					System.out.println(sdf.format(d));
#cold_bold					try {
#cold_bold						Thread.sleep(5000);
#cold_bold					} catch (InterruptedException e) {
#cold_bold					}
#cold_bold				}
#cold_bold			}
#cold_bold		};
#cold_bold	}
}

步骤二:设置d线程为守护线程

在main方法中,首先,设置d线程为守护线程并启动该线程;然后,使用Thread类的sleep方法使main线程阻塞10秒;最后,输出字符串“main线程结束了”,代码如下所示:

import java.text.SimpleDateFormat;
import java.util.Date;

public class Timing {
	public static void main(String[] args) {
		Thread d = new Thread() {
			public void run() {
				while (true) {
					Date d=new Date();
					SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh-mm-ss");
					System.out.println(sdf.format(d));
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
					}
				}
			}
		};
#cold_bold		d.setDaemon(true);
#cold_bold		d.start();
#cold_bold		try {
#cold_bold			Thread.sleep(10000);
#cold_bold		} catch (InterruptedException e) {
#cold_bold		}
#cold_bold		// 进程中所有前台线程结束后,后台线程强制结束
#cold_bold		System.out.println("main线程结束了");
	}
}

运行Timing类,控制台输出结果如下:

2014-04-22 10-40-17
2014-04-22 10-40-22
main线程结束了

上述代码中输出了两次日期-时间,main线程结束了,d线程随之结束。这是因为,d线程被设置为守护线程,守护线程的特点是,当进程中只剩下守护线程时,所有守护线程强制终止。

本案例的完整代码如下所示:

import java.text.SimpleDateFormat;
import java.util.Date;

public class Timing {
	public static void main(String[] args) {
		Thread d = new Thread() {
			public void run() {
				while (true) {
					Date d=new Date();
					SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh-mm-ss");
					System.out.println(sdf.format(d));
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
					}
				}
			}
		};
		d.setDaemon(true);
		d.start();
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
		}
		// 进程中所有前台线程结束后,后台线程强制结束
		System.out.println("main线程结束了");
	}
}

6 下列关于sleep和yield说法正确的是

A.sleep方法是用于阻塞线程,直到被中断为止,yield方法是用于获取CPU时间片段。

B.sleep方法用于让出CPU时间片段,yield方法用于使线程阻塞指定毫秒数。

C.sleep方法用于使线程阻塞指定毫秒数,yield方法用于让出CPU时间片段。

D.sleep方法用于获取CPU时间片段,yield用于阻塞线程,直到被中断为止。

参考答案

本题正确答案为C。

Thread类的sleep方法用于使线程阻塞指定毫秒数,yield方法用于让出CPU时间片段。

7 已知类Foo代码如下,下列锁机制不合适的是

Foo类代码如下所示:

class Foo {
  private List list1 = new ArrayList();
  private List list2 = new ArrayList();
  public void add_1(Object obj) {
         list1.add(obj);
  }
  public void remove(Object obj) {
    list1.remove(obj);
  }
  public void add_2(Object obj){
    list2.add(obj);
  }
}

下列锁机制不合适的是()。

A.

class Foo {
  private List list1 = new ArrayList();
  private List list2 = new ArrayList();
  public void add_1(Object obj) {
    synchronized(this) {
         list1.add(obj);
    }
  }
  public void remove(Object obj) {
     synchronized(this) {
         list1.remove(obj);
     }
  }
  public void add_2(Object obj){
     synchronized(this) {
         list2.add(obj);
     }
  }
}

B.

class Foo {
  private List list1 = new ArrayList();
  private List list2 = new ArrayList();
  public void add_1(Object obj) {
    synchronized(list1) {
         list1.add(obj);
    }
  }
  public void remove(Object obj) {
     synchronized(list1) {
         list1.remove(obj);
      }
  }
  public void add_2(Object obj){
     synchronized(list2) {
         list2.add(obj);
     }
  }
}

C.

class Foo {
  private List list1 = new ArrayList();
  private List list2 = new ArrayList();
  public synchronized void add_1(Object obj) {
         list1.add(obj);
  }
  public synchronized  void remove(Object obj) {
    list1.remove(obj);
  }
  public synchronized void add_2(Object obj){
    list2.add(obj);
  }
}

D.

class Foo {
  private List list1 = new ArrayList();
  private List list2 = new ArrayList();
  public void add_1(Object obj) {
    synchronized(this) {
         list1.add(obj);
    }
  }
  public void remove(Object obj) {
     synchronized(this) {
         list1.remove(obj);
    }
  }
  public void add_2(Object obj){
     synchronized(obj) {
         list2.add(obj);
     }
  }
}

参考答案

本题正确答案为ACD。

使用synchroinzed需要对一个对象上锁以保证线程同步,那么这个锁对象的选择应该注意:

1)多个需要同步的线程在访问该同步块时,看到的应该是同一个锁对象引用,否则达不到同步效果。

2) 在使用同步块时,应当尽量在允许的情况下减少同步范围,以提高并发的执行效率。

本题中,AC选项,锁的范围选择的不合适,对集合list1和list2的操作,不应使用同一个锁对象。

D选项,参数对象作为锁对象是不合适的,这是因为,参数可能在不断的变化,不能保证是同一个锁对象。