39 Java中的多线程.docx

上传人:b****3 文档编号:10754981 上传时间:2023-05-27 格式:DOCX 页数:14 大小:153.91KB
下载 相关 举报
39 Java中的多线程.docx_第1页
第1页 / 共14页
39 Java中的多线程.docx_第2页
第2页 / 共14页
39 Java中的多线程.docx_第3页
第3页 / 共14页
39 Java中的多线程.docx_第4页
第4页 / 共14页
39 Java中的多线程.docx_第5页
第5页 / 共14页
39 Java中的多线程.docx_第6页
第6页 / 共14页
39 Java中的多线程.docx_第7页
第7页 / 共14页
39 Java中的多线程.docx_第8页
第8页 / 共14页
39 Java中的多线程.docx_第9页
第9页 / 共14页
39 Java中的多线程.docx_第10页
第10页 / 共14页
39 Java中的多线程.docx_第11页
第11页 / 共14页
39 Java中的多线程.docx_第12页
第12页 / 共14页
39 Java中的多线程.docx_第13页
第13页 / 共14页
39 Java中的多线程.docx_第14页
第14页 / 共14页
亲,该文档总共14页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

39 Java中的多线程.docx

《39 Java中的多线程.docx》由会员分享,可在线阅读,更多相关《39 Java中的多线程.docx(14页珍藏版)》请在冰点文库上搜索。

39 Java中的多线程.docx

39Java中的多线程

Java中的多线程

1.

(1)进程是正在进行的程序,用来开辟内存空间的;

(2)线程:

就是进程中一个负责程序执行的控制单元(执行路径)。

2.

(1)一个进程中至少有一个线程。

(2)一个进程中允许有多个执行路径,即多线程。

(3)开启多个线程是为了同时运行多部分代码;

(4)任务:

每个线程都具有自己运行的内容,这个内容就可以称为线程要执行的任务。

(5)多线程的好处:

解决了多部分代码同时运行的目的;

(6)多线程的弊端:

多线程过多,使效率降低。

因为程序的执行都是在CPU中做着快速的切换完成的,且这个切换是随机的。

(7)JVM启动时就已经启动了至少两个线程:

1主线程:

执行main方法的线程:

该线程的任务代码都定义在main方法里8;

2负责垃圾回收的线程。

(8)创建一个线程的目的是:

为了开启一条执行路径,能够达到执行此代码和其他代码的同时运行,而运行此代码就是这个执行路径的任务。

(9)JVM创建的主线程的任务都定义在了主方法中;而自定义的线程的任务在哪里呢?

为什么要重写run方法呢?

Thread用于描述线程,线程是需要任务的,所以Thread类也对任务进行描述;这个任务就是通过Thread类中的run方法来体现,也就是说,run方法就是封装自定义线程运行任务的方法。

run方法中定义的就是线程需要执行的任务代码。

(10)开启线程是为了执行指定的代码,所以只有继承Thread类,并重写run方法,将运行的代码定义在run方法中即可。

3.创建线程的两个方法:

(1)方法一:

继承Thread类。

∙创建步骤:

①定义一个类并继承Thread类,②重写Thread类中的run方法;③直接创建此类的一个对象,即创建线程成功,④调用此对象的start方法,启动此线程。

∙(调用run与调用start方法有什么区别?

∙run方法只是个普通的方法,调用run方法并不能启动此线程,只有调用start方法才会启动一个线程,启动后自动执行run方法中的任务。

∙classPrimeThreadextendsThread{

∙longminPrime;

∙PrimeThread(longminPrime){

∙this.minPrime=minPrime;

∙}

∙publicvoidrun(){

∙//computeprimeslargerthanminPrime

∙ . . .

∙}

∙}

∙启动:

PrimeThreadp=newPrimeThread(143);

∙p.start();

(2)方法二:

①接口Runnable:

里面只有一个抽象方法run()方法,实现此接口就是实现任务的封装,然后通过生成对象,实现任务的对象封装,作为参数传递给Thread,创建Thread对象(线程对象)。

Thread类也是实现了Runnable接口的。

②如果一个类继承了其他的父类,此时就不能继承Thread了,所以只能继承接口了,此时就可以继承Runnable接口。

3创建线程:

步骤:

i.定义类并实现接口Runnable;

ii.重写接口中的run方法,将线程的任务代码封装到run方法中去;

iii.对新创建的类new出一个对象,并作为Thread类构造方法的参数传递进去;目的是创建一个Thread类的线程对象;为什么?

因为线程的任务都封装在创建的类(此类继承接口Runnable)中的run方法中,所以要在创建线程对象(Thread对象)时,必须明确此线程要执行的任务(run方法),即需要把创建类的对象作为参数传进去。

iiii.调用线程对象(Thread类的对象)的start方法启动线程。

注意:

Thread构造方法中如果参数是个类的对象,则会调用传进去的对象的run方法,实现方式如下面代码:

classPrimeRunimplementsRunnable{

∙longminPrime;

∙PrimeRun(longminPrime){

∙this.minPrime=minPrime;

∙}

∙publicvoidrun(){

∙//computeprimeslargerthanminPrime

∙ . . .

∙}

∙}

∙启动线程:

PrimeRunp=newPrimeRun(143);

∙newThread(p).start();

解析:

Thread的有多个构造方法,其中一个是

Thread(Runnable target)

∙AllocatesanewThreadobject.

4.两个方法的区别:

(1)第一个是继承类Thread,重写Thread类中的run方法,第二个是实现接口Runnable,实现接口的同时可以继承其他的一个类;

(2)实现Runnable接口的方法有好处:

①将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想,就是把线程的任务封装成对象;②避免了Java单继承的局限性;

因此,利用实现Runnable接口的方法创建线程比较常用。

5.对于一个线程,不能多次启动start;特别是当线程结束后不能再次启动。

否则会抛出异常Throws:

IllegalThreadStateException-ifthethreadwasalreadystarted.

Itisneverlegaltostartathreadmorethanonce.Inparticular,athreadmaynotberestartedonceithascompletedexecution.

6.抛出异常要看四部分信息:

①异常所属的线程,②异常的名称,③异常的提示信息,④异常发生的位置。

7.一个线程开启多次产生异常,这个异常属于多次开启语句所在的线程,如线面这个是在main方法中产生异常IllegalThreadStateException:

publicstaticvoidmain(String[]args){

Tickett1=newTicket();

Tickett2=newTicket();

Tickett3=newTicket();

Tickett4=newTicket();

t1.start();

t2.start();

t3.start();

t3.start();//此处产生异常,使main线程停止,后面的语句不在进行,即线程t4便不会启动,前面三个进行仍然照常进行,只不过是main进程停止,并不影响t3进程。

t4.start();//main进程异常,此语句不会执行

}

8.买票问题:

实现多个窗口同时售票

(1)利用继承Thread类的子类创建多个线程对象,把创建的子类中的票数num定义为static,则多个对象就操作共享的这一个变量了,解决了买票问题;实例见代码。

publicclassSaleTickets{

publicstaticvoidmain(String[]args){

Tickett1=newTicket();

Tickett2=newTicket();

Tickett3=newTicket();

Tickett4=newTicket();

t1.start();

t2.start();

t3.start();

t4.start();

}

}

classTicketextendsThread{

privatestaticintnum=100;

publicvoidrun(){

while(num>0){

System.out.print(num--+""+Thread.currentThread().getName()+"");

}

}

}

(2)利用实现接口Runnable的方法:

利用一个类去实现接口Runnable,然后利用此类产生一个对象,然后把这个对象作为Thread类的构造方法的参数,由于参数是同一个对象,显然产生的多个线程就是操作同一个对象中的数据。

classTicket22implementsRunnable{

privateintnum=0;

publicvoidrun(){

for(;num<100;){

System.out.println(num+++""+Thread.currentThread().getName());

}

}

}

publicclassTickets{

publicstaticvoidmain(String[]args){

Ticket22t=newTicket22();

Threadt0=newThread(t);

Threadt1=newThread(t);

Threadt2=newThread(t);

Threadt3=newThread(t);

t0.start();

t1.start();

t2.start();

t3.start();

}

}

9.多线程的安全隐患:

原因:

当多个线程是在操作共享的数据,且操作共享数据的线程代码有多条时,当其中一个线程在处理共享数据的多条代码过程中,其他线程参与了运算,就会导致多线程安全问题的出现。

10.上述安全问题的解决方法:

将操作共享数据的多条代码封装起来,当有线程执行这块代码时,其他的线程不准执行这块代码,只有当此线程把这块代码都执行完毕后,其他线程才可以执行这块代码。

在java中利用同步代码块就可以实现上述需求。

同步代码块的定义格式:

Synchronized(对象){需要被同步的代码块}

对象称为同步锁;如同火车上的卫生间,“有人”,“无人”。

作用:

对同步进行监视。

同步的前提:

同步中必须有多个线程使用同一个锁(同步锁),此同步锁(对象)必须定义在run方法之外,否则就是一个线程一把锁了。

11.

(1)同步的好处:

解决了多线程的安全问题;

(2)同步的弊端:

降低了效率,因为同步外的线程都会判断同步锁,是一种没有必要的判断。

12.同步方法:

利用关键字synchronized修饰的方法,称为同步方法。

权限修饰符synchronized方法名(){方法体}

把需要同步的代码块封装成同步方法。

但是此时的同步锁呢?

答:

同步方法使用的同步锁是调用run方法的对象,即this。

13.同步方法与同步代码块的区别:

同步方法的同步锁是固定的this;而同步代码块的同步锁可以任意指定(任意对象),也可以利用this。

在实际开发中,建议使用同步代码块,因为可以任意指定对象作为同步锁,同步方法可以看做是同步代码块的简写形式。

14.静态同步方法的同步锁:

静态方法不能使用this,那静态同步方法的同步锁是什么对象呢?

学习过Object类中有个getClass()方法,可以获取一个Class类的对象(字节码类对象),静态同步方法的同步锁就是默认getClass()返回的Class类的字节码文件对象。

(注意Class是一个类名,首字母大写,class首字母小写表示关键字定义一个类。

)如果同步代码块同步锁定义为this.getClass(),就可以实现与静态同步方法拥有同一个同步锁对象,synchronized(this.getClass()){}相当于Classclazz=this.getClass();synchronized(clazz){}。

另外,对象名.getClass()等同于此类名.class;即Ticket是个类名,则Tickett=newTicket();有Classclazz=t.getClass();等价于Classclazz=Ticket.class;这是获取一个类的字节码文件对象的两个等价方法。

getClass()方法是非静态的。

15.多线程内存解析:

不同的线程有独自的内存区,每个线程有自己独自的路径,相互不影响。

只有当所有的线程都结束了,jvm才会结束。

任何一个线程发生异常与其他的线程没有影响。

 

16.线程的状态:

(1)当run方法执行完后,线程会自动消亡,当然也可以利用stop方法使线程强制消亡;

(2)sleep(time)方法必须指定时间,时间一到自动会进入运行状态;

(3)wait()方法既可以指定时间,也可以不指定时间,如果不指定时间,则必须由notify()方法唤醒线程,否则一直等待。

(4)CPU的执行资格:

在CPU待处理的队列中排队;

CPU的执行权:

正在被CPU处理。

(5)冻结状态:

释放CPU执行权的同时,同时释放CPU执行资格。

(6)临时阻塞状态:

具备CPU执行资格,但不具有CPU执行权,正在等待获取执行权。

(7)如果划分更细的话,可以把sleep和wait分别化为一种状态。

17.可以通过Thread类中getName()方法获取线程的名称:

Thread–编号(从0开始)。

18.在创建一个类对象时,就已经创建了一个线程,同时就已经给此线程编号了。

没有开启线程之前,便已经对线程编号了。

主线程的名字就是main。

19.Thread类中有个static方法,可以获取当前运行的线程,方法名currentThread();获取Thread.currentThread();。

获取当前运行线程的名字Thread.currentThread().getName();

20.单例模式的多线程问题:

(1)饿汉式:

(单例模式):

不需要同步:

classSigleEhan{

privatefinalstaticSigleEhans=newSigleEhan();

privateSigleEhan(){

}

publicstaticSigleEhangetInstance(){

returns;

}

}

(2)懒汉式:

(延迟加载的单例模式):

(面试的时候经常考到这种情况)

1第一种同步代码块形式

∙classSigleLanhan{//懒汉式(延迟加载单例模式)

∙privatestaticSigleLanhans=null;

∙privateSigleLanhan(){

∙}

∙publicstaticSigleLanhangetInstance(){

∙if(s==null){//这是为了在以后s赋予对象后,直接判断,不需要再次进入同步,从而提高了效率

∙synchronized(SigleLanhan.class){

∙//由于此方法是静态的,因此只能使用类名.class;因为静态方法中不可使用this,不可以使用this.getClass()

∙if(s==null)

∙s=newSigleLanhan();

∙}

∙}

∙returns;

∙}

∙}

②第二种静态同步方法:

classSigleLanhan2{

privatestaticSigleLanhan2s=null;

privateSigleLanhan2(){}

publicstaticsynchronizedSigleLanhan2getInstance(){

if(s==null){

s=newSigleLanhan2();

}

returns;

}

}

问题:

(1)②中的同步方法的同步锁是什么?

答:

Siglelanhan2.class。

类的字节码文件对象。

(2)①和②那个效率会更高些?

答:

①形式会更好,因为只要s赋予对象之后,就不再进行同步锁的判断,不会再进入同步代码块中;然而②同步方法,每次都要进行同步锁的判断,而且如果是同一个同步锁,就会进入同步方法中,使效率降低。

21.多线程死锁示例:

常见情景之一:

同步的嵌套

publicclassDeadlockDemo{

publicstaticvoidmain(String[]args){

Threadst1=newThreads(true);

Threadst2=newThreads(false);

Threadtt1=newThread(t1);

Threadtt2=newThread(t2);

tt1.start();//开启run方法中的true部分

tt2.start();//开启run方法中的false部分

}

}

classThreadsimplementsRunnable{

privatebooleanflag;

Threads(booleanflag){

this.flag=flag;

}

publicvoidrun(){

if(flag){

for(inti=10;i>0;i--)

synchronized(MyLock.locka){

System.out.println("if语句locka"

+Thread.currentThread().getName());

synchronized(MyLock.lockb){

System.out.println("if语句lockb"

+Thread.currentThread().getName());

}

}

}else{

for(intj=10;j>0;j--)

synchronized(MyLock.lockb){

System.out.println("else语句lockb"

+Thread.currentThread().getName());

synchronized(MyLock.locka){

System.out.println("else语句locka"

+Thread.currentThread().getName());

}

}

}

}

}

classMyLock{

publicstaticfinalObjectlocka=newObject();

publicstaticfinalObjectlockb=newObject();

}

解析:

开启了两个子线程(run方法中分别是两个同步的嵌套使用):

对于t1线程而言,首先是A锁的钥匙,然后是B锁的钥匙,;t2线程正好相反,首先是B的钥匙,然后是A的钥匙。

首先,线程t1利用A钥匙打开,紧接着t2利用B钥匙打开,这样T1

拥有A钥匙,需要B钥匙才可进入第二层同步,T2拥有B钥匙需要A钥匙才可以进入第二层同步,就这样,各自都不能得到自己的东西,又不释放已有的东西,产生死锁现象。

22.

1.finalize方法:

是根目录Object类中的方法。

2.System.gc();方法可以启动垃圾回收器。

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 自然科学 > 物理

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2