Java 多线程总结
Posted fuchenxuan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 多线程总结相关的知识,希望对你有一定的参考价值。
总结多线程的一些知识点以及面试题,标红是特别要注意的地方。
一.传统线程机制:
什么是线程?
线程就是一条程序的执行线索,一行代码一行代码按时间一直向下执行,所执行的路线就是一条线程。如果这是还有另一个线索同时执行,也就是两个代码并行执行,这就是多线程。
创建线程的两种方式?
创建线程的第一种方式:继承Thread类
步骤:(1)定义类继承Thread(2)覆写Thread类中的run方法(3)调用线程的start方法,该方法两个作用:启动线程,调用run方法
创建线程的第二种方式:实现Runnable接口
步骤:1.定义类实现Runnable接口;2.覆盖Runnable接口中的run方法,将线程要运行的代码存放在该run方法中
3.通过Thread类建立线程对象;4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
new Thread(t);5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
实现方式和继承方式有什么区别呢?(面试题经常考)
实现方式好处:避免了单继承的局限性,如果继承了一个类就不能再继承其他类了,在定义线程时,建议使用实现方式。
两种方式区别:
继承Thread线程代码存放Thread子类run方法中
实现Runnable,线程代码存放在接口的子类的run方法
使用多线程机制会提高程序的运行效率吗?
不会,因为多个线程都获取cpu的执行权,cpu执行到谁,谁就运行,明确一点,在某个时刻,只能有一个程序在运行。(多核除外)cpu在做着快速的切换,以达到看上去是同时运行的效果,我们可以形象把多线程的运行行为在互相抢夺cpu的执行权,这就是多线程的一个特性:随机性。即谁抢到谁执行,至于执行多长,cpu说的算。但是这样就好像是一个厨师在一个桌子上做馒头变成不停的换到别的桌子上做馒头,做的效率不会提高,而且性能还会更低。
为什么会有多线程下载?
多线程运行效率并不会快,但是多线程会抢带宽,比如一个线程10k的下载,我们用20个线程就变成了200K,就是20个线程一起下载,这样就快啦。
二.传统定时器技术:
定时器:
定时器在我们做应用的时候也经常被用到,Timer这个类就是定时器,我们new一个Timer,然后使用它的schedule方法进行调度,这个方法里面需要两个参数,一个是我们要执行的任务,需要创建一个TimerTask对象,还有一个执行时间,单位是毫秒。在TimerTask对象里的run方法中写我们要执行的任务。如果调用的是schedule的三个参数方法,这三个参数的最后一个是一个相隔时间,即从执行时间开始,每隔这个相隔的时间就执行一次。
写成TimerTask的子类,没用到这个任务,都要new一下。任务里嵌套任务可以实现任务的不断循环。
android里没有定时器,但是有HandlerMessage,也是这样的一个思想,handler发送一个message,响应完了再发一个,响应完了再发一个。实现了定时器的效果。
开源框架Quartz:
如果想要做一些比如说是每天的某个时刻给提醒或者从周一到周五的某时刻提醒而周末不提醒,这时候我们就可以使用一个javase的开源框架Quartz,它是一个调度器,里面有非常多调度的调度集,我们使用它的时候,要下载它的api,把他的jar包导入工程,使用里面的job接口,执行里面响应的方法即可实现,国内这的这类资料很少,我都是看一些外文网站上了解到的。如果我们以后的应用使用到了类似这样的技术,我可以很快的把它应用上。
三.传统线程互斥技术
互斥:
我们在使用多线程的时候,经常会遇到互斥问题,比如说两个同时运行的代码同时访问一个对象,两个线程都要取里面的数据,并对其进行修改,生活中最典型的例子就是银行账户,比如说一个商家的账户,他的客户给他汇钱的时候,他同时取这个账户里的钱,有可能在我们取钱的过程中,客户完成了汇款操作,这样我们就会从最开始的金额里减去我们取得钱,客户汇款的金额并没有加上,这样我们不就是要亏钱了嘛,这就是多线程中的互斥问题。我们肯定不能让这样的。
线程安全问题可以用银行转账来解释。
多线程问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程就参与进来执行,导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行多次中,其他线程不可以参与执行
Java对于多线程的安全问题提供了专业的解决方法,这就是同步代码块
synchronized(对象)
需要被同步的代码
哪些代码需要被同步,就看哪些语句操作共享数据。如果整个方法都需要被同步,那么就在这个方法中加上synchronized关键字。这里的对象可比作是锁,持有锁的线程可以在同步中执行,没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁,这里的对象也可比作是火车上的卫生间。
同步的前提:
1.必须有两个或者两个以上的线程 2.必须是多个线程使用同一个锁,必须保证同步中只能有一个线程在运行
使用同步的好处:解决了多线程的安全问题
使用同步的弊端是:多个线程需要判断锁,较为消耗资源,越安全越麻烦。
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不再是this。因为静态方法中也不可以定义this,静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象,类名.class 该对象的类型是Class,静态的同步方法,使用的锁是该方法所在类的字节码文件对象,类名.calss。
四.传统线程同步通信技术
面试题:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着再回到主线程再循环100次,如此循环50次,请写出程序。
解答:两个线程的代码要参照同一个变量,即这两个线程的代码要共享数据,所以,把这两个线程的执行代码搬到同一个类中去。定义一个类,写两个有同步方法(方法上加synchronized关键字)即子线程和主线程,方法里有一个标记位,用来控制哪个线程执行,如果不应该那个线程走,就this.wait(),并在方法最后this.notify(),然后在main方法中的子线程调用新创建类中的子线程方法,再调用新创建类中的主线程方法。
以上是关于Java 多线程总结的主要内容,如果未能解决你的问题,请参考以下文章