Java面试系列06:Java基础之并发编程(先写着,后面再增加)

本文详细探讨了Java中创建线程的三种主要方式,包括继承Thread类、实现Runnable接口及使用线程池。同时,文章深入讲解了线程同步机制,如synchronized关键字和Lock接口的使用,以及volatile关键字确保变量可见性的原理。最后,分析了线程的五种基本状态,帮助读者全面理解Java线程的生命周期。

看大佬的吧:
https://blog.youkuaiyun.com/z69183787/article/details/86229514
https://blog.youkuaiyun.com/kai46385076/article/details/97621035

1.创建线程有几种不同的方式?你喜欢哪一种?为什么?

有三种方式可以用来创建线程:
1.继承Thread类:在run中写要执行的代码,myThread.start();开启新线程
2.实现Runnable接口:

1.创建Runnable实现类的实例:定义Runnable接口的实现类,
并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
2.并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
//创建线程对象
//myThread是Runnable接口的实现类的对象
Thread t = new Thread(myThread, "小强");
3.调用线程对象的start()方法来启动线程。

3.线程池 JAVA线程池原理,执行流程
线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
使用线程池中线程对象的步骤:

  1. 创建线程池对象。
  2. 创建Runnable接口子类对象。(task)
  3. 提交Runnable接口子类对象。(take task)
  4. 关闭线程池(一般不做)。
			// 创建线程池对象
			//包含2个线程对象
			ExecutorService service = Executors.newFixedThreadPool(2);
			// 创建Runnable实例对象
			MyRunnable r = new MyRunnable();
			// 从线程池中获取线程对象,然后调用MyRunnable中的run()
			service.submit(r);
			// 注意:submit方法调用结束后,程序并不终止,
			// 是因为线程池控制了线程的关闭。
			
			// 将使用完的线程又归还到了线程池中
			// 关闭线程池
			//service.shutdown();

实现Runnable接口比继承Thread类所具有的优势:

  1. 适合多个相同的程序代码的线程去共享同一个资源。
  2. 可以避免java中的单继承的局限性。
  3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
  4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

2. 线程同步、JAVA中同步机制

1. synchronized
同步代码块: synchronized 关键字可以用于方法中的某个区块中,表示对这个区块的资源实行互斥访问。

		//synchronized(同步锁){
		//	需要同步操作的代码
		//}
		Object lock = new Object();
		synchronized (lock) {
			。。。
		}

**同步方法:**使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。

		public synchronized void method(){
			可能会产生线程安全问题的代码
		}

2.Lock锁:

		//public void lock() :加同步锁。 
		//public void unlock() :释放同步锁。
		Lock lock = new ReentrantLock();
		public void run() {
			lock.lock();
			//需要同步操作的代码
			...
			lock.unlock();
		}

ReentrantLock和synchronized

3.volatile
请谈谈 volatile 有什么特点,为什么它能保证变量对所有线程的可见性?
关键字 volatile 是 Java 虚拟机提供的最轻量级的同步机制。当一个变量被定义成 volatile 之后,具备两种特性:
保证此变量对所有线程的可见性。当一条线程修改了这个变量的值,新值对于其他线程是可以立即得知的。而普通变量做不到这一点。
禁止指令重排序优化。普通变量仅仅能保证在该方法执行过程中,得到正确结果,但是不保证程序代码的执行顺序。
链接1
链接2

3.线程的五大状态

原文链接:https://blog.youkuaiyun.com/peter_teng/article/details/10197785
在这里插入图片描述
1.新建状态(New):
当用new操作符创建一个线程时, 例如new Thread®,线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码。
2.就绪状态(Runnable)
一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。
3.运行状态(Running)
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
4. 阻塞状态(Blocked)
线程运行过程中,可能由于各种原因进入阻塞状态:
1>线程通过调用sleep方法进入睡眠状态;
2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
3>线程试图得到一个锁,而该锁正被其他线程持有;
4>线程在等待某个触发条件;

所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
5. 死亡状态(Dead)
有两个原因会导致线程死亡:
1) run方法正常退出而自然死亡,
2) 一个未捕获的异常终止了run方法而使线程猝死。
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值