java线程的总结.docx

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

java线程的总结.docx

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

java线程的总结.docx

java线程的总结

线程

Java中的进程、线程和多线程

一、进程:

进程就是一个正在执行的程序。

二、线程:

线程是进程执行的一条线索或路径。

进程中至少有一个线程存在。

三、多线程:

顾名思义,一个进程中的多个线程。

线程的开始:

当要运行一个程序时,JVM首先会找到main函数,然后从main函数开始执行(也就是说,程序是从main函数开始运行的),

此时,程序就成为一个进程,既然是进程肯定有线程的存在。

此时的线程就是主线程,主线程会向下顺序执行代码。

如果程序中存在一个庞大的循环语句,主程序就会一直在这里运行,直到循环语句结束,下面的代码才能被执行到。

这可能要花费较长的时间,影响了程序运行的效率。

所以,为了提高程序的效率,就引入了多线程。

由主线程开辟另一个或多个线程,

让这些线程都去执行代码。

线程线程是一个程序内部的控制流

java的线程是通过java.long.Thread类实现的,VM启动时会有一个主方法publicstaticvoidmain(){}的线程

1可以通过Thread的实例来创建一个新的线程,必须通过start来开始一个线程,每个线程都是通过特定的Thread对象来调用run方法来完成其操作的,方法run()称为线程体。

类直接继承Thread也可以形成线程,但是这样该类就只能继承一个类

而实现接口的话,可以实现多个接口,从而更加灵活。

2实现Runnable接口,复写run方法。

用Thread来new一个线程对象,并将Runnable的子类对象传入Thread的构造方法中。

我们用代码来模拟铁路售票系统,实现通过四个售票点发售某日某次列车的100张车票,一个售票点用一个线程表示。

  我们首先这样编写这个程序:

1publicclassThreadDome1{

2  publicstaticvoidmain(String[]args){

3  ThreadTestt=newThreadTest();

4  t.start();

5  t.start();

6  t.start();

7  t.start();

8  }

9  }

10  classThreadTestextendsThread{

11  privateintticket=100;

12  publicvoidrun(){

13  while(true){

14  if(ticket>0){

15  System.out.println(Thread.currentThread().getName()+

16  "issalingticket"+ticket--);

17  }else{

18  break;

19  }

20  }

21  }

22  }

上面的代码中,我们用ThreadTest类模拟售票处的售票过程,run方法中的每一次循环都将总票数减1,模拟卖出一张车票,同时该车票号打印出来,直接剩余的票数到零为止。

在ThreadDemo1类的main方法中,我们创建了一个线程对象,并重复启动四次,希望通过这种方式产生四个线程。

从运行的结果来看我们发现其实只有一个线程在运行,这个结果告诉我们:

一个线程对象只能启动一个线程,无论你调用多少遍start()方法,结果只有一个线程。

我们接着修改ThreadDemo1,在main方法中创建四个Thread对象:

1publicclassThreadDemo1{

2  publicstaticvoidmain(String[]args){

3  newThreadTest().start();

4  newThreadTest().start();

5  newThreadTest().start();

6  newThreadTest().start();

7  }

8  }

9  classThreadTestextendsThread{

10  privateintticket=100;

11  publicvoidrun(){

12  while(true){

13  if(ticket>0){

14  System.out.println(Thread.currentThread().getName()+

15  "issalingticket"+ticket--);

16  }else{

17  break;

18  }

19  }

20  }

21  }

这下达到目的了吗?

  从结果上看每个票号都被打印了四次,即四个线程各自卖各自的100张票,而不去卖共同的100张票。

这种情况是怎么造成的呢?

我们需要的是,多个线程去处理同一个资源,一个资源只能对应一个对象,在上面的程序中,我们创建了四个ThreadTest对象,就等于创建了四个资源,每个资源都有100张票,每个线程都在独自处理各自的资源。

  经过这些实验和分析,可以总结出,要实现这个铁路售票程序,我们只能创建一个资源对象,但要创建多个线程去处理同一个资源对象,并且每个线程上所运行的是相同的程序代码。

在回顾一下使用接口编写多线程的过程。

1publicclassThreadDemo1{

2 publicstaticvoidmain(String[]args){

3 ThreadTestt=newThreadTest();

4 newThread(t).start();

5 newThread(t).start();

6 newThread(t).start();

7 newThread(t).start();

8 }

9 }

10 classThreadTestimplementsRunnable{

11 privateinttickets=100;

12 publicvoidrun(){

13 while(true){

14 if(tickets>0){

15 System.out.println(Thread.currentThread().getName()+

16 "issalingticket"+tickets--);

17 }

18 }

19 }

20 }

上面的程序中,创建了四个线程,每个线程调用的是同一个ThreadTest对象中的run()方法,访问的是同一个对象中的变量(tickets)的实例,这个程序满足了我们的需求。

在Windows上可以启动多个记事本程序一样,也就是多个进程使用同一个记事本程序代码。

实现Runnable接口相对于继承Thread类来说,有如下显著的好处:

  

(1)适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码,数据有效的分离,较好地体现了面向对象的设计思想。

  

(2)可以避免由于Java的单继承特性带来的局限。

我们经常碰到这样一种情况,即当我们要将已经继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable接口的方式了。

  (3)有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。

当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。

多个线程操作相同的数据,与它们的代码无关。

当共享访问相同的对象时,即它们共享相同的数据。

当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。

线程的状态与生命周期

1.新建状态当创建一个Thread类或子类的对象时,该线程称为新建的线程

2.就绪:

线程创建后,处于就绪状态,等待start()方法被调用

3.运行:

线程开始执行进入运行状态

4.睡眠:

线程的执行可以通过调用sleep()方法来暂时中止

5.等待:

如果调用了wait()方法,线程将处于等待状态。

用于两个或多个线程并发运行时

6.挂起(Suspended):

在临时停止或中断线程的执行时,线程就处于挂起状态

7.阻塞(Blocked):

在线程等待一个事件时,例如输入输出操作,就称其处于阻塞状态

8.死亡:

在run()方法已完成执行或stop()方法被调用后,线程处于死亡状态

优先级

设置线程的优先级,用setPriority()方法,该方法也是thread类成员。

它的通常形式为:

finalvoidsetPrority(intlevel)

这里level指定了对所调用的线程的新的优先权的设置。

level的值必须在MIN_PRIORITY到MAX_PRIORITY范围内。

通常,它们的值分别是1和10.要返回一个线程为默认的优先级,指定NORM_PRIORITY,通常值为5.这些优先级在Thread中都被定义为final型变量。

通过调用thread类的getPriority()方法可以获得当前的优先级设置。

方法如下:

finalintgetPriority()

如果t1的优先级比t2高,并不是说t2不执行,而是t1执行时间多点,t2执行时间少

正常停止一个线程的方法,不是调用interrupt,stop,而是定义一个变量,bolleanf=true;

classMyThreadextendsThread{

publicvoidrun(){

bolleanf=true;

while(true){

System.out.println("1");

try{

sleep(1000);

}catch(InterruptedExceptione){

return;

}

多线程的安全问题

什么时候会出现线程安全问题?

满足3个条件时,会出现:

1.存在多个线程

2.多个线程共享数据(共享数据一般是成员变量,局部变量不是)。

3.操作共享数据的语句至少要有两条。

出现安全问题的原因:

多个线程执行共享数据的代码块时,其中的一个线程还没有执行完代码块,另一个线程就开始执行代码块,这会造成共享数据的错误。

从而出现安全问题。

多线程安全问题的解决:

同步。

因为同步可以保证多线程代码只能被持有锁的线程运行,其它线程不能运行。

任意时刻,一个锁只能被一个线程拥有。

(持有锁的线程会在执行完多线程代码时释放锁,这样锁就能被其它线程拥有。

1.同步代码块;将要同步的代码放synchronized中,并加锁。

synchronized(对象)//只要是个对象就行,这个对象就是锁

{要被同步的代码}

2.同步函数:

synchronized能将代码封装并同步,函数只能将代码封装,所以用synchronized修饰函数,让函数既能封装又能同步。

同步函数也有锁。

非静态同步函数的锁是this,同步函数所属对象的引用;静态同步函数的锁是:

类名.class,函数所属类的字节码文件对象。

需注意的是:

1.不需要同步的代码不要放入同步函数中。

2.synchronized放在函数返回值类型前

publicsynchronizedvoidshow()

{要被同步}

找到要被同步的代码的方法:

1.先找到多线程所有要运行的代码(可能是一个run方法被多个线程使用同,也可能是一个run方法只被其中的一个线程使用);

2.多线程的共享数据(一般成员变量都是);

3.多线程要运行的代码中操作共享数据的代码就是要被同步的代码。

注意:

有时使用了同步仍然不成功,出现这种现象的原因可能是:

需要被同步的代码可能放在程序的不同位置,有的代码要用到同步代码块,有的要用同步函数

那么怎么才能使同步成功?

1.首先判断要被同步的代码是否正确,确定所有要被同步的代码都被同步了。

2.同步代码块和同步函数中的锁是同一个锁,即必须是多个线程使用同一个锁

线程同步的问题

1.方法声明时使用,放在范围操作符(public等)之后,返回类型声明(void等)之前。

即一次只能有一个线程进入该方法,其他线程要想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入。

锁定后别的线程不能调用该方法B,但是其他线程可以调用别的未锁定的方法A,访问方法A就有可以改变方法B中的变量。

解决方法时,将两个方法全部加锁.

针对某变量,想要同步,涉及改该变量的方法都要加锁,而读该变量的方法不需要加锁

publicclassTTimplementsrunnable{

intm=100;

publicsynchronizedvoidm1()throwsException{

m=1000;

Tread.sleep(5000);

System.out.println(m);

}

publicvoidm2{

m=2000;}

publicvoidrun(){

try{m1();}catch(){}

}

}

publicstaticvoidmian(String[]args)throwsException{

TTtt=newTT();

Threadt=newThread(tt);

t.start();

tt.m2();

System.out.println(m);

}

进程t先锁定m1方法,改变m为1000,在睡眠,主线程继续执行,通过m2方法改变m为2000,最后m1,m2方法的打印m均为2000.

2.对某一代码块使用,synchronized后跟括号,括号里是变量,这样,一次只有一个线程进入该代码块。

publicintsynMethod(inta1){

synchronized(a1){//一次只能有一个线程进入}

}

3.synchronized后面括号里是一对象,此时,线程获得的是对象锁。

例如:

publicclassMyThreadimplementsRunnable{

publicstaticvoidmain(Stringargs[]){

MyThreadmt=newMyThread();

Threadt1=newThread(mt,"t1");

Threadt2=newThread(mt,"t2");

Threadt3=newThread(mt,"t3");

t1.start();

t2.start();

t3.start();

}

publicvoidrun(){

synchronized(this){

System.out.println(Thread.currentThread().getName());

}

}

}

对于3,如果线程进入,则得到对象锁,那么别的线程在该类所有对象上的任何操作都不能进行。

在对象级使用锁通常是一种比较粗糙的方法。

为什么要将整个对象都上锁,而不允许其他线程短暂地使用对象中其他同步方法来访问共享资源?

如果一个对象拥有多个资源,就不需要只为了让一个线程使用其中一部分资源,就将所有线程都锁在外面。

由于每个对象都有锁,可以如下所示使用虚拟对象来上锁:

classFineGrainLock{

MyMemberClassx,y;

Objectxlock=newObject(),ylock=newObject();

publicvoidfoo(){

synchronized(xlock){//accessxhere

}

synchronized(ylock){//accessyhere

}

}

publicvoidbar(){

synchronized(this){

//accessbothxandyhere

}

4.synchronized后面括号里是类。

例如:

classArrayWithLockOrder{

privatestaticlongnum_locks=0;

privatelonglock_order;

privateint[]arr;

publicArrayWithLockOrder(int[]a)

{arr=a;

synchronized(ArrayWithLockOrder.class){//--------这里类

num_locks++;//锁数加1

lock_order=num_locks;}//为此对象实例设置唯一的lock_order。

}

publiclonglockOrder()

{returnlock_order;}

publicint[]array()

{returnarr;}

}

classSomeClassimplementsRunnable

{

publicintsumArrays(ArrayWithLockOrdera1,

ArrayWithLockOrdera2)

{

intvalue=0;

ArrayWithLockOrderfirst=a1;//保留数组引用的一个

ArrayWithLockOrderlast=a2;//本地副本。

intsize=a1.array().length;

if(size==a2.array().length)

{

if(a1.lockOrder()>a2.lockOrder())//确定并设置对象的锁定

{//顺序。

first=a2;

last=a1;

}

synchronized(first){//按正确的顺序锁定对象。

synchronized(last){

int[]arr1=a1.array();

int[]arr2=a2.array();

for(inti=0;ivalue+=arr1[i]+arr2[i];

}

}

}

returnvalue;

}

publicvoidrun(){

//...

}

}

对于4,如果线程进入,则线程在该类中所有操作不能进行,包括静态变量和静态方法,实际上,对于含有静态方法和静态变量的代码块的同步,我们通常用4来加锁。

以上4种之间的关系:

锁是和对象相关联的,每个对象有一把锁,为了执行synchronized语句,线程必须能够获得synchronized语句中表达式指定的对象的锁,一个对象只有一把锁,被一个线程获得之后它就不再拥有这把锁,线程在执行完synchronized语句后,将获得锁交还给对象。

在方法前面加上synchronized修饰符即可以将一个方法声明为同步化方法。

同步化方法在执行之前获得一个锁。

如果这是一个类方法,那么获得的锁是和声明方法的类相关的Class类对象的锁。

如果这是一个实例方法,那么此锁是this对象的锁。

synchronzied块后面跟类的具体详细例子、publicclassDB2_JDBCFactory{privatestaticDB2_JDBCFactoryinstance=null;publicstaticfinalThreadLocalthreadLocal=newThreadLocal();privateDB2_JDBCFactory(){

}publicstaticDB2_JDBCFactorygetInstance(){

if(instance==null){

synchronized(DB2_JDBCFactory.class){//synchronized后面跟一个类

instance=newDB2_JDBCFactory();

}

}

returninstance;

}publicConnectiongetConnection_JNDI_localhost(){

Connectionc=(Connection)threadLocal.get();

try{

if(c==null||c.isClosed()){

InitialContextctx=newInitialContext();

DataSourceds=(DataSource)ctx.lookup("java:

comp/env/jdbc/localhost");

c=ds.getConnection();

threadLocal.set(c);

}

}catch(Exceptionex){

System.err.println("getConnection_JNDIInitialfailed."+ex);

returnnull;

}

returnc;

}}外面的对象访问这个类的需要通过调用它的getInstance()

死锁的例子

publicclassTestDeadLockimplementsRunnable{

publicintflag=1;

staticObjecto1=newObject();

staticObjecto2=newObject();

publicvoidrun(){

System.out.println("flag="+flag);

/*

if(flag==1){

synchronized(o1)

{

try{Thread.sleep(5000);}

catch(Exceptione){e.printStackTrace();}

}

synchronized(o2)

{

System.out.println("1");

}

}//这种情况是if语句中的两个锁并列,即便没有o2的锁,o1的语句也一样能执行完毕,锁在执行完毕后立即返还,可以供别的对象使用。

if(flag==1){

synchronized(o1)

{

try{Thread.sleep(5000);}

catch(Exceptione){e.printStackTrace();}

synchronized(o2){System.out.println("1");}

}

}//此时有了O1的锁还必须要O2的锁,才能执行完O1语句,从而返还锁O1.

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

当前位置:首页 > 医药卫生 > 基础医学

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

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