关闭

多线程(多线程面试题)

71人阅读 评论(0) 收藏 举报

作用:

使用多线程技术可以使系统同时运行多个执行体,这样可以加快线程的响应时间,提高计算机资源使用率。

线程生命周期

1.新生态

用new 运算符和Thtead类或子类建立线程对象后,该线程对象就处于新生态。处于新生态的线程有自己的内存空间,通过调用start()方法后进入就绪态。

2.就绪态

处于就绪态的线程已具备运行条件,但还未分配到cpu,因为线程将进入线程队列,等待系统为其分配cpu。一旦获得cpu,线程就进入运行态并自动调用自己的run()方法。

3.运行态

在运行态的线程执行自己run()方法中的代码,直到调用其他方法而终止,或等待某资源而阻塞,或完成任务而死亡。

4.阻塞态

执行sleep()方法,或等待I/O设备资源让出cpu并暂时终止自己的运行,进入阻塞态。

5.死亡态

死亡态有两个原因:一个是正常运行的线程完成它所有的工作;一个是被强制终止,如执行stop()方法或destroy()方法。

继承Thread实现多线程

package com.neusoft.thread;

public class ThreadDome extends Thread {
	
	String s;
	int m,count=0;
	ThreadDome(String str,int mm){
		s=str;
		m=mm;
	}
	
	public void run(){
		try {
		while(true){
			
			System.out.print(s);
			
				sleep(m);
			
			count++;
			if(count>=20)
				break;
			
		}
		System.out.println("finished !"+s);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return;
			}
		
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ThreadDome threadA =new ThreadDome("A ",50);
		ThreadDome threadB =new ThreadDome("B ",100);
		threadA.start();
		threadB.start();

	}

}

运行结果:


调用Runnable接口实现多线程

package com.neusoft.thread;

public class Thread_Runnable implements Runnable{
	String s;
	int m,count=0;
	Thread_Runnable(String str ,int mm){
		s=str;
		m=mm;
	}
	
	public void run(){
		try {
			while (true){
				System.out.print(s);
				Thread.sleep(m);
				if(++count>=20)
					break;
			}
			System.out.println("finishes!"+s);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Thread_Runnable threadA=new Thread_Runnable("A ",50);
		Thread_Runnable threadB=new Thread_Runnable("B ",100);
		Thread threadAA=new Thread(threadA);
		Thread threadBB=new Thread(threadB);
		threadAA.start();
		threadBB.start();

	}

}

运行结果:


小结:

优先使用runnable接口实现多线程:

不论是那种方式,最后都需要通过Thread类的实例调用start()方法来开始线程的执行,start()方法通过java虚拟机调用线程中定义的run方法来执行该线程。通过查看java源程序中的start()方法的定义可以看到,它是通过调用操作系统的start0方法来实现多线程的操作的。

但是一般在系统的开发中遇到多线程的情况的时候,以实现Runnable接口的方式为主要方式。这是因为实现接口的方式有很多的优点:

1、就是通过继承Thread类的方式时,线程类就无法继承其他的类来实现其他一些功能,实现接口的方式就没有这中限制;

2.也是最重要的一点就是,通过实现Runnable接口的方式可以达到资源共享的效果。

但是其实两种方式是有密切联系的:

我们通过实现接口Runnable建立的MyThread类,而Thread类也是实现了Runnable接口的子类。如果我们想启动线程,需要通过Thread类中的start()方法,Thread类中的start()方法来调用MyThread类中run方法来执行该线程。这个实现是典型的代理模式


Join()方法:

Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

线程同步:

实现线程同步操作的方法是在共享内存变量的方法前加synchronized修饰符。在程序运行过程中,如果某线程调用synchronized修饰的方法,在该线程结束此方法的运行前,其他线程不能运行该方法。

package com.neusoft.thread;

public class Thread_Synchronized implements Runnable {
	//模拟银行存取款
	private static float s=2000;                    //原有存款
	private static float temp;
	public static synchronized void deposit(float amt){                 //存款
		temp=s;
		temp+=amt;
		try {
			int mm=(int) (1000*Math.random());
			Thread.sleep(mm);
			System.out.print(mm+"毫秒,");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		s=temp;
		System.out.println(s);
	}
	
	

	public void run() {
		// TODO Auto-generated method stub
		for(int i=1;i<=4;i++){

			Thread_Synchronized.deposit(100);
		}
		
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Thread_Synchronized threadA=new Thread_Synchronized();
		Thread_Synchronized threadB=new Thread_Synchronized();
		
	
		Thread threadAA=new Thread(threadA);
		Thread threadBB=new Thread(threadB);
		threadAA.start();
	    threadBB.start();

	}

}

面试问答:

1.java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?

答:
     有两种实现方法,分别是继承Thread类与实现Runnable接口

     用synchronized关键字修饰同步方法

     反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

2.sleep() 和 wait() 有什么区别?

答:sleep就是正在执行的线程主动让出cpu,cpu去执行其他线程,在sleep指定的时间过后,cpu才会回到这个线程上继续往下执行,如果当前线程进入了同步锁,sleep方法并不会释放锁,即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行。wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify方法),调用wait方法的线程就会解除wait状态和程序可以再次得到锁后继续向下运行。


3.同步和异步有何异同,在什么情况下分别使用他们?

答:如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。
 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率

4.同步有几种实现方法

答: 同步的实现方面有两种,分别是synchronized ,wait与notify
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。


5.启动一个线程是用run()还是start()?

答: 启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态,一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码。




1
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:72次
    • 积分:11
    • 等级:
    • 排名:千里之外
    • 原创:1篇
    • 转载:0篇
    • 译文:0篇
    • 评论:0条
    文章存档