java多线程.docx
- 文档编号:8671969
- 上传时间:2023-02-01
- 格式:DOCX
- 页数:73
- 大小:194.85KB
java多线程.docx
《java多线程.docx》由会员分享,可在线阅读,更多相关《java多线程.docx(73页珍藏版)》请在冰豆网上搜索。
java多线程
JAVA多线程编程
1.线程状态图
说明:
线程共包括以下5种状态。
1.新建状态(New) :
线程对象被创建后,就进入了新建状态。
例如,Threadthread=newThread()。
2.就绪状态(Runnable):
也被称为“可执行状态”。
线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。
例如,thread.start()。
处于就绪状态的线程,随时可能被CPU调度执行。
3.运行状态(Running):
线程获取CPU权限进行执行。
需要注意的是,线程只能从就绪状态进入到运行状态。
4.阻塞状态(Blocked) :
阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。
直到线程进入就绪状态,才有机会转到运行状态。
阻塞的情况分三种:
(01)等待阻塞--通过调用线程的wait()方法,让线程等待某工作的完成。
(02)同步阻塞--线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
(03)其他阻塞--通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。
当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5.死亡状态(Dead) :
线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
这5种状态涉及到的内容包括Object类,Thread和synchronized关键字。
Object类,定义了wait(),notify(),notifyAll()等休眠/唤醒函数。
Thread类,定义了一些列的线程操作函数。
例如,sleep()休眠函数,interrupt()中断函数,getName()获取线程名称等。
synchronized,是关键字;它区分为synchronized代码块和synchronized方法。
synchronized的作用是让线程获取对象的同步锁。
2.Thread和Runnable
2.1.Thread和Runnable简介
Runnable是一个接口,该接口中只包含了一个run()方法。
它的定义如下:
publicinterfaceRunnable{
publicabstractvoidrun();
}
Runnable的作用,实现多线程。
我们可以定义一个类A实现Runnable接口;然后,通过newThread(newA())等方式新建线程。
Thread是一个类。
Thread本身就实现了Runnable接口。
它的声明如下:
publicclassThreadimplementsRunnable{}
Thread的作用,实现多线程。
2.2.Thread和Runnable的异同点
Thread和Runnable的相同点:
都是“多线程的实现方式”。
Thread和Runnable的不同点:
Thread是类,而Runnable是接口;Thread本身是实现了Runnable接口的类。
我们知道“一个类只能有一个父类,但是却能实现多个接口”,因此Runnable具有更好的扩展性。
此外,Runnable还可以用于“资源的共享”。
即,多个线程都是基于某一个Runnable对象建立的,它们会共享Runnable对象上的资源。
通常,建议通过“Runnable”实现多线程!
2.3.Thread和Runnable的多线程示例
2.4.Thread的多线程示例
下面通过示例更好的理解Thread和Runnable,借鉴网上一个例子比较具有说服性的例子。
1//ThreadTest.java源码
2classMyThreadextendsThread{
3privateintticket=10;
4publicvoidrun(){
5for(inti=0;i<20;i++){
6if(this.ticket>0){
7System.out.println(this.getName()+"卖票:
ticket"+this.ticket--);
8}
9}
10}
11};
12
13publicclassThreadTest{
14publicstaticvoidmain(String[]args){
15//启动3个线程t1,t2,t3;每个线程各卖10张票!
16MyThreadt1=newMyThread();
17MyThreadt2=newMyThread();
18MyThreadt3=newMyThread();
19t1.start();
20t2.start();
21t3.start();
22}
23}
运行结果:
Thread-0卖票:
ticket10
Thread-1卖票:
ticket10
Thread-2卖票:
ticket10
Thread-1卖票:
ticket9
Thread-0卖票:
ticket9
Thread-1卖票:
ticket8
Thread-2卖票:
ticket9
Thread-1卖票:
ticket7
Thread-0卖票:
ticket8
Thread-1卖票:
ticket6
Thread-2卖票:
ticket8
Thread-1卖票:
ticket5
Thread-0卖票:
ticket7
Thread-1卖票:
ticket4
Thread-2卖票:
ticket7
Thread-1卖票:
ticket3
Thread-0卖票:
ticket6
Thread-1卖票:
ticket2
Thread-2卖票:
ticket6
Thread-2卖票:
ticket5
Thread-2卖票:
ticket4
Thread-1卖票:
ticket1
Thread-0卖票:
ticket5
Thread-2卖票:
ticket3
Thread-0卖票:
ticket4
Thread-2卖票:
ticket2
Thread-0卖票:
ticket3
Thread-2卖票:
ticket1
Thread-0卖票:
ticket2
Thread-0卖票:
ticket1
结果说明:
(01)MyThread继承于Thread,它是自定义个线程。
每个MyThread都会卖出10张票。
(02)主线程main创建并启动3个MyThread子线程。
每个子线程都各自卖出了10张票。
2.5.Runnable的多线程示例
下面,我们对上面的程序进行修改。
通过Runnable实现一个接口,从而实现多线程。
1//RunnableTest.java源码
2classMyThreadimplementsRunnable{
3privateintticket=10;
4publicvoidrun(){
5for(inti=0;i<20;i++){
6if(this.ticket>0){
7System.out.println(Thread.currentThread().getName()+"卖票:
ticket"+this.ticket--);
8}
9}
10}
11};
12
13publicclassRunnableTest{
14publicstaticvoidmain(String[]args){
15MyThreadmt=newMyThread();
16
17//启动3个线程t1,t2,t3(它们共用一个Runnable对象),这3个线程一共卖10张票!
18Threadt1=newThread(mt);
19Threadt2=newThread(mt);
20Threadt3=newThread(mt);
21t1.start();
22t2.start();
23t3.start();
24}
25}
运行结果:
Thread-0卖票:
ticket10
Thread-2卖票:
ticket8
Thread-1卖票:
ticket9
Thread-2卖票:
ticket6
Thread-0卖票:
ticket7
Thread-2卖票:
ticket4
Thread-1卖票:
ticket5
Thread-2卖票:
ticket2
Thread-0卖票:
ticket3
Thread-1卖票:
ticket1
结果说明:
(01)和上面“MyThread继承于Thread”不同;这里的MyThread实现了Thread接口。
(02)主线程main创建并启动3个子线程,而且这3个子线程都是基于“mt这个Runnable对象”而创建的。
运行结果是这3个子线程一共卖出了10张票。
这说明它们是共享了MyThread接口的。
3.Thread中start()和run()的区别
3.1.start()和run()的区别说明
start():
它的作用是启动一个新线程,新线程会执行相应的run()方法。
start()不能被重复调用。
run() :
run()就和普通的成员方法一样,可以被重复调用。
单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程!
下面以代码来进行说明。
classMyThreadextendsThread{
publicvoidrun(){
...
}
};
MyThreadmythread=newMyThread();
mythread.start()会启动一个新线程,并在新线程中运行run()方法。
而mythread.run()则会直接在当前线程中运行run()方法,并不会启动一个新线程来运行run()。
3.2.start()和run()的区别示例
下面,通过一个简单示例演示它们之间的区别。
源码如下:
1//Demo.java的源码
2classMyThreadextendsThread{
3publicMyThread(Stringname){
4super(name);
5}
6
7publicvoidrun(){
8System.out.println(Thread.currentThread().getName()+"isrunning");
9}
10};
11
12publicclassDemo{
13publicstaticvoidmain(String[]args){
14Threadmythread=newMyThread("mythread");
15
16System.out.println(Thread.currentThread().getName()+"callmythread.run()");
17mythread.run();
18
19System.out.println(Thread.currentThread().getName()+"callmythread.start()");
20mythread.start();
21}
22}
运行结果:
maincallmythread.run()
mainisrunning
maincallmythread.start()
mythreadisrunning
结果说明:
(01)Thread.currentThread().getName()是用于获取“当前线程”的名字。
当前线程是指正在cpu中调度执行的线程。
(02)mythread.run()是在“主线程main”中调用的,该run()方法直接运行在“主线程main”上。
(03)mythread.start()会启动“线程mythread”,“线程mythread”启动之后,会调用run()方法;此时的run()方法是运行在“线程mythread”上。
3.3.start()和run()相关源码(基于JDK1.7.0_40)
Thread.java中start()方法的源码如下:
publicsynchronizedvoidstart(){
//如果线程不是"就绪状态",则抛出异常!
if(threadStatus!
=0)
thrownewIllegalThreadStateException();
//将线程添加到ThreadGroup中
group.add(this);
booleanstarted=false;
try{
//通过start0()启动线程
start0();
//设置started标记
started=true;
}finally{
try{
if(!
started){
group.threadStartFailed(this);
}
}catch(Throwableignore){
}
}
}
说明:
start()实际上是通过本地方法start0()启动线程的。
而start0()会新运行一个线程,新线程会调用run()方法。
privatenativevoidstart0();
Thread.java中run()的代码如下:
publicvoidrun(){
if(target!
=null){
target.run();
}
}
说明:
target是一个Runnable对象。
run()就是直接调用Thread线程的Runnable成员的run()方法,并不会新建一个线程。
4.synchronized关键字
4.1.synchronized原理
在java中,每一个对象有且仅有一个同步锁。
这也意味着,同步锁是依赖于对象而存在。
当我们调用某对象的synchronized方法时,就获取了该对象的同步锁。
例如,synchronized(obj)就获取了“obj这个对象”的同步锁。
不同线程对同步锁的访问是互斥的。
也就是说,某时间点,对象的同步锁只能被一个线程获取到!
通过同步锁,我们就能在多线程中,实现对“对象/方法”的互斥访问。
例如,现在有两个线程A和线程B,它们都会访问“对象obj的同步锁”。
假设,在某一时刻,线程A获取到“obj的同步锁”并在执行一些操作;而此时,线程B也企图获取“obj的同步锁”——线程B会获取失败,它必须等待,直到线程A释放了“该对象的同步锁”之后线程B才能获取到“obj的同步锁”从而才可以运行。
4.2.synchronized基本规则
我们将synchronized的基本规则总结为下面3条,并通过实例对它们进行说明。
第一条:
当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
第二条:
当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。
第三条:
当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的其他的“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
4.2.1.第一条
当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程对“该对象”的该“synchronized方法”或者“synchronized代码块”的访问将被阻塞。
下面是“synchronized代码块”对应的演示程序。
1classMyRunableimplementsRunnable{
2
3@Override
4publicvoidrun(){
5synchronized(this){
6try{
7for(inti=0;i<5;i++){
8Thread.sleep(100);//休眠100ms
9System.out.println(Thread.currentThread().getName()+"loop"+i);
10}
11}catch(InterruptedExceptionie){
12}
13}
14}
15}
16
17publicclassDemo1_1{
18
19publicstaticvoidmain(String[]args){
20Runnabledemo=newMyRunable();//新建“Runnable对象”
21
22Threadt1=newThread(demo,"t1");//新建“线程t1”,t1是基于demo这个Runnable对象
23Threadt2=newThread(demo,"t2");//新建“线程t2”,t2是基于demo这个Runnable对象
24t1.start();//启动“线程t1”
25t2.start();//启动“线程t2”
26}
27}
运行结果:
t1loop0
t1loop1
t1loop2
t1loop3
t1loop4
t2loop0
t2loop1
t2loop2
t2loop3
t2loop4
结果说明:
run()方法中存在“synchronized(this)代码块”,而且t1和t2都是基于"demo这个Runnable对象"创建的线程。
这就意味着,我们可以将synchronized(this)中的this看作是“demo这个Runnable对象”;因此,线程t1和t2共享“demo对象的同步锁”。
所以,当一个线程运行的时候,另外一个线程必须等待“运行线程”释放“demo的同步锁”之后才能运行。
如果你确认,你搞清楚这个问题了。
那我们将上面的代码进行修改,然后再运行看看结果怎么样,看看你是否会迷糊。
修改后的源码如下:
1classMyThreadextendsThread{
2
3publicMyThread(Stringname){
4super(name);
5}
6
7@Override
8publicvoidrun(){
9synchronized(this){
10try{
11for(inti=0;i<5;i++){
12Thread.sleep(100);//休眠100ms
13System.out.println(Thread.currentThread().getName()+"loop"+i);
14}
15}catch(InterruptedExceptionie){
16}
17}
18}
19}
20
21publicclassDemo1_2{
22
23publicstaticvoidmain(String[]args){
24Threadt1=newMyThread("t1");//新建“线程t1”
25Threadt2=newMyThread("t2");//新建“线程t2”
26t1.start();//启动“线程t1”
27t2.start();//启动“线程t2”
28}
29}
代码说明:
比较Demo1_2和Demo1_1,我们发现,Demo1_2中的MyThread类是直接继承于Thread,而且t1和t2都是MyThread子线程。
幸运的是,在“Demo1_2的run()方法”也调用了synchronized(this),正如“Demo1_1的run()方法”也调用了synchronized(this)一样!
那么,Demo1_2的执行流程是不是和Demo1_1一样呢?
运行结果:
t1loop0
t2loop0
t1loop1
t2loop1
t1loop2
t2loop2
t1loop3
t2loop3
t1loop4
t2loop4
结果说明:
如果这个结果一点也不令你感到惊讶,那么我相信你对synchronized和this的认识已经比较深刻了。
否则的话,请继续阅读这里的分析。
synchronized(this)中的this是指“当前的类对象”,即synchronized(this)所在的类对应的当前对象。
它的作用是获取“当前对象的同步锁”。
对于Demo1_2中,synchronized(this)中的this代表的是MyThread对象,而t1和t2是两个不同的MyThread对象,因此t1和t2在执行synchronized(this)时,获取的是不同对象的同步锁。
对于Demo1_1对而言,synchronized(this)中的this代表的是MyRunable对象;t1和t2共同一个MyRunable对象,因此,一个线程获取了对象的同步锁,会造成另外一个线程等待。
4.2.2.第二条
当一个线程访问“某对象”的“synchronized方法”或者“synchronized代码块”时,其他线程仍然可以访问“该对象”的非同步代码块。
下面是“synchronized代码块”对应的演示程序。
1classCount{
2
3//含有synchronized同步块的方法
4publicvoidsynMethod(){
5synchronized(this){
6try{
7for(inti=0;i<5;i++){
8Thread.sleep(100);//休眠100ms
9System.out.println(Thread.currentThread().getName()+"synMethodloop"+i);
10}
11}catch(InterruptedExceptionie){
12}
13}
14}
15
16//非同步的方法
17publicvoidnonSynMethod(){
18try{
19for(inti=0;i<5;i++){
20Thread.sleep(100);
21
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- java 多线程