Java并发基础常见面试题总结

1.什么是进程,什么是线程?

进程是程序的一次执行过程,是系统执行的基本单位,运行一个程序也是从创建进程到进程消亡的过程。
线程又称轻量级进程,是比进程更小的执行单位。一个进程在执行的过程中会分为多个线程。线程间共享进程的堆和方法区,每个线程有自己的程序计数器、虚拟机栈、本地方法栈。

2.请简要描述进程与线程的关系,区别,优缺点?

一个进程可以包含多个线程,线程之间共享当前进程的堆和方法区,线程运行期间会相互影响,但各进程之间相互独立。线程执行时性能开销小,进程开销较大。

3.程序计数器为什么是私有的?

程序计数器有两个功能:
1.字节码解释器通过改变程序计数器来读取指令,从而实现对代码的控制。
2.多线程程序运行期间在线程切换时,会通过程序计数器记录切换之前指令的执行位置。从而恢复切换之前的正确运行状态。
程序记录器的存在是为了线程切换后能恢复到正确的执行位置。

4.虚拟机栈和本地方法栈为什么是私有的?

虚拟机栈:每个Java方法在执行时会创建一个栈帧存放局部变量表、操作数和常量池引用等信息,从方法的调用到执行完成,对应着一个栈帧在虚拟机中入栈和出栈的过程。
本地方法栈:和虚拟机栈作用类似。虚拟机栈时为虚拟机执行java方法而服务,本地方法栈为虚拟机使用的Native方法服务。
虚拟机栈和本地方法栈保证了线程的局部变量不被其他线程访问到。

5.堆和方法区的作用?

堆和方法区都是所有线程共享的资源。堆是进程中最大的一块内存,用来存放所有创建的对象,方法区用来存放已加载的类信息、常量、静态变量。

6.并发和并行的区别

并发:同一时间段内,多个任务都在执行(单位时间内不一定同时执行)。
并行:单位时间内,多个任务同时执行。

7.为什么要使用多线程?

线程是比进程更小的程序执行单位,拥有更小的性能开销。现在的计算机大多为多核,单线程只会占用一个cpu,其他cpu则处于闲置状态,没有得到充分的利用。再者,时代对系统的并发性提出了更高的要求,利用多线程机制可以提高系统的并发能力和性能。

8.线程的生命周期和状态

线程的五种基本状态:新建、就绪、运行、阻塞、死亡。
线程的状态转换
新建

线程对象新建后,进入新建状态

就绪

线程调用start()方法后,进入就绪状态,等待CPU调度。

运行

CPU从可运行线程池中调度就绪线程,获得CPU使用权,进入运行状态

阻塞

处于运行状态的线程因为某种原因,暂停对CPU的使用权,进入阻塞状态,直至其进入到就绪状态,等待CPU的调度执行。
1.等待阻塞
线程调用wait()方法,使线程进入等待阻塞状态,需要依靠其他线程的通知才能重新进入就绪状态。
2.同步阻塞
线程在获取synchronized同步锁失败(因锁被其他线程占用),进入到同步阻塞状态。在获取到锁后,进入就绪状态。
3.其他阻塞
线程通过调用sleep()方法、join()方法和IO请求,使得进程进入到阻塞状态,当sleep()状态超时,join()方法等待进程终止或者超时、IO请求处理完毕,线程转入就绪状态。

死亡:

线程执行完毕或异常退出,该线程结束生命周期。

线程创建方式:

1.继续Thread类并重写run方法。

public class Test extends Thread {
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("This is myThread");;
	}
    public static void main(String[] args) {
    	Test test = new Test();
    	test.start();
    }
}

2.实现Runnable接口并重写run方法。

public class Test implements Runnable {
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("This is myThread implements Runnable");
	}
    public static void main(String[] args) {
    	Test test = new Test();
    	Thread td = new Thread(test);//td才是真正的线程对象
    	td.start();
    }
}

线程状态控制

1.线程休眠sleep()
sleep()是静态方法,最好别用Thread的实例对象调用,sleep()方法睡眠的始终是当前正在运行的线程,而不是调用它的线程对象。

public class Test extends Thread {
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("childThread"); 
	}
    public static void main(String[] args){
    	Test test = new Test();
    	test.start();
    	try {
			Thread.sleep(1000);
			System.out.println("main");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
}
//运行结果:
childThread//打印出此结果后隔1s打印main
main

2.线程让步yield()
该方法使线程从运行态转为就绪态,CPU从就绪状态队列中只会选择与该线程优先级相同或者是优先级更高的线程去执行。

public class Test extends Thread {
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("childThread"); 
	}
    public static void main(String[] args){
    	Test test = new Test();
    	test.start();
    	for(int i= 0; i < 4; i++){   		
    		Thread.yield();//主线程放弃使用cpu转为就绪状态,但cpu调度也可能继续调用主线程
    		System.out.println("main");
    	}
    }
}
//运行结果://结果不固定
main                |  main
childThread			|  main
main				|  main
main				|  main
main				|  childThread

3.线程合并join()
线程合并即将多个并发线程合并为一个单一线程来执行。一个线程的执行必须要等到其他线程执行完毕后才可执行。

public class Test extends Thread {
	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println("childThread"); 
	}
    public static void main(String[] args){
    	Test test = new Test();
    	test.start();
//    	try {
//			test.join(); 
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
    	System.out.println("main");
    }
}
//运行结果:
当有注释时打印:main childThread
无注释时打印:等待test线程执行结束再执行主线程 childThread main

sleep()和yield()的区别:
1.sleep()方法使线程进入阻塞状态,等睡眠时间结束,线程进入就绪状态;yield()方法使正在执行的线程进入就绪状态,有可能刚进入就绪状态,又被调度到CPU状态。
2.sleep()方法需抛出InterruptedException,需要捕获或者抛出。而yield()方法不需要。
3.sleep方法比yield方法有更好的可移植性,通常不要依靠yield方法控制并发线程的执行。

9.sleep() 方法和 wait() 方法区别和共同点?

1.两者都可以暂停线程运行。
2.wait()通常用于线程间交互/通信,sleep()通常用于暂停执行。
3.调用wait()方法的进程需要其他进程调用同一个对象上的notify()方法才能苏醒,而sleep()在睡眠时间超时后自动苏醒。

10. 为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?

调用start()方法会使线程进入就绪状态,经cpu调度后会自动执行run()方法,而直接调用run()方法会把它当作一个普通方法,在main()线程中执行,不是真正的多线程。

参考博文:
1.https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md
2.https://www.jianshu.com/p/19228c30ffed

  • 7
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中,高并发试题关注线程安全、并发控制、锁机制、多线程设计模式和性能优化等内容。以下是一些见的面试问题: 1. **什么是线程安全?** - 线程安全是指多个线程同时访问共享资源时,不会出现数据不一致或程序行为不可预测的情况。 2. **Java中哪些集合类是线程安全的?** - Java集合框架中的`ConcurrentHashMap`、`CopyOnWriteArrayList`、`CopyOnWriteArraySet`等是线程安全的。 3. **什么是`synchronized`关键字?** - `synchronized`用于同步代码块或方法,防止多个线程同时执行,保证了对共享资源的互斥访问。 4. **区别`synchronized`和`ReentrantLock`?** - `synchronized`更简单直接,但没有提供锁的粒度控制;`ReentrantLock`提供了更多的灵活性,比如可中断锁和公平锁。 5. **什么是阻塞队列?** - 如`BlockingQueue`,如`ConcurrentLinkedQueue`、`ArrayBlockingQueue`等,它们在并发场景下用于任务的生产和消费。 6. **`volatile`关键字的作用是什么?** - 它保证了多线程环境下的可见性和内存一致性,尤其在变量可能被多个线程修改的情况下。 7. **什么是死锁?如何避免死锁?** - 死锁是指两个或多个线程相互等待对方释放资源而造成的一种僵局。避免方法包括避免循环等待资源、设置超时等。 8. **什么是线程池?** - 线程池可以复用已有线程,控制并发数,提高系统性能和资源利用率。 9. **Spring框架中的`@Async`是什么?** - Spring的异步处理注解,用于将任务提交给后台执行,避免阻塞主线程。 10. **`ExecutorService`和`Future`接口的区别?** - `ExecutorService`负责执行任务,`Future`则提供了对任务执行结果的访问和状态检查。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值