多线程
1.什么是线程
一个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程),线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行。
ps:进程是指在系统中正在运行的一个应用程序。每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内。
2.线程的串行
一个线程中任务的执行是串行的。如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务。也就是说,在同一时间内,1个线程只能执行1个任务。
3.什么是多线程
一个进程中可以开启多条线程,每条线程可以并行(同时)执行不同的任务(进程 ->车间,线程->车间工人)。多线程技术可以提高程序的执行效率。
4.多线程的原理
1.同一时间,CPU只能处理1条线程,只有1条线程在工作(执行)
2.多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换)
3.如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
4.如果线程非常非常多,CPU会在N多线程之间调度,CPU会累死,消耗大量的CPU资源。每条线程被调度执行的频次会降低(线程的执行效率降低)
5.多线程的优缺点
1.多线程的优点
1.1能适当提高程序的执行效率
1.2能适当提高资源利用率(CPU、内存利用率)
2.多线程的缺点
2.1开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能
2.2线程越多,CPU在调度线程上的开销就越大
2.3程序设计更加复杂:比如线程之间的通信、多线程的数据共享
6.多线程的安全隐患(资源共享)以及解决
一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源。比如多个线程访问同一个对象、同一个变量、同一个文件,当多个线程访问同一块资源时,很容易引 发数据错乱和数据安全问题。
如何解决
使用互斥锁
@synchronized(锁对象) {
// 需要锁定的代码
}
互斥锁的优缺点
优点:能有效防止因多线程抢夺资源造成的数据安全问题
缺点:需要消耗大量的CPU资源
互斥锁的使用前提:多条线程抢夺同一块资源
相关专业术语:线程同步,多条线程按顺序地执行任务
互斥锁,就是使用了线程同步技术
7.线程间的通信
在一个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信
线程间通信的体现
一个线程传递数据给另一个线程。/在一个线程中执行完特定任务后,转到另一个线程继续执行任务。
java中实现方式:
1.同步(多个线程通过synchronized关键字这种方式来实现线程间的通信。)
由于线程A和线程B持有同一个MyObject类的对象object,尽管这两个线程需要调用不同的方法,但是它们是同步执行的,比如:线程B需要等待线程A执行完了 methodA()方法之后,它才能执行methodB()方法。这样,线程A和线程B就实现了 通信。这种方式,本质上就是“共享内存”式的通信。多个线程需要访问同一个共 享变量,谁拿到了锁(获得了访问权限),谁就可以执行。
2.while轮询的方式
在这种方式下,线程A不断地改变条件,线程ThreadB不停地通过while语句检测这个条件是否成立 ,从而实现了线程间的通信。但是这种方式会浪费CPU资源。之所 以说它浪费资源,是因为JVM调度器将CPU交给线程B执行时,它没做啥“有用”的工作,只是在不断地测试 某个条件是否成立。就类似于现实生活中,某个人一直看着 手机屏幕是否有电话来了,而不是: 在干别的事情,当有电话来时,响铃通知TA电话来了。
3.wait/notify方式
当条件未满足时,线程A调用wait() 放弃CPU,并进入阻塞状态。---不像while轮询那样占用CPU
当条件满足时,线程B调用 notify()通知 线程A,所谓通知线程A,就是唤醒线程A,并让它进入可运行状态。
这种方式的一个好处就是CPU的利用率提高了。
但是也有一些缺点:比如,线程B先执行,一下子添加了5个元素并调用了notify()发送了通知,而此时线程A还执行;当线程A执行并调用wait()时,那它永远就不可能 被唤醒了。因为,线程B已经发了通知了,以后不再发通知了。这说明:通知过早,会打乱程序的执行逻辑。
wait()方法使得当前线程必须要等待,等到另外一个线程调用notify()或者notifyAll()方法。线程调用wait()方法,释放它对锁的拥有权,然后等待另外 的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。
另一个会导致线程暂停的方法:Thread.sleep(),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。
notify()方法会唤醒一个等待当前对象的锁的线程。如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关。
8.线程的状态
1. 新建状态(New):新创建了一个线程对象。
Thread t = new Thread ();
2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
1、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
2、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
3、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或 者I/O处理完毕时,线程重新转入就绪状态。
5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
-------------------------------------------
个性签名:做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!

浙公网安备 33010602011771号