初学java多线程

来源:互联网 发布:apple beta 版软件计划 编辑:程序博客网 时间:2024/06/07 02:50

原文地址:http://developer.51cto.com/art/200911/162925.htm

1线程是程序运行的基本执行单元。任何一个Java程序都必须有一个主线程。

一个cpu同一个时间只能执行一条指令,所以要靠线程调度来管理多个并发的线程。

2、进程与线程的区别:

一个进程中可以有一个或多个线程。进程和进程之间不共享内存,也就是说系统中的进程是在各自独立的内存空间中运行的。而一个进程中的线程可以共享系统分派给这个进程的内存空间,此外,线程还拥有一个属于自己的内存空间,这段内存空间也叫做线程栈。

3、两种建立线程的方法:

任何继承ThreadJava类都可以通过Threadstart方法来建立线程。如果想运行自己的线程执行函数,那就要覆盖Thread类的run方法。

还有一个标识某个Java类是否可作为线程类的接口Runnable,这个接口只有一个抽象方法run,也就是Java线程模型的线程执行函数。

由于Java不支持多继承,因此,这个线程类如果继承了Thread,就不能再继承其他的类了,因此,Java线程模型提供了通过实现Runnable接口的方法来建立线程,这样线程类可以在必要的时候继承和业务有关的类,而不是Thread类。

4Thread类的子类可以直接实例化,但在子类中必须要覆盖Thread类的run方法才能真正运行线程。

5、任何一个Java程序都必须有一个主线程。一般这个主线程的名子为main

6、可以使用Thread类的setName方法修改线程名。Thread类有一个重载构造方法可以设置线程名。

7、实现Runnable接口的类必须使用Thread类的实例才能创建线程。通过Runnable接口创建线程分为两步:

1. 将实现Runnable接口的类实例化。

2. 建立一个Thread对象,并将第一步实例化后的对象作为参数传入Thread类的构造方法。

3. 最后通过Thread类的start方法建立线程。

8、线程的生命周期:开始(等待)、运行、挂起和停止四种不同的状态。这四种状态都可以通过Thread类中的方法进行控制。

sleep方法并不等同于suspend。它们之间最大的一个区别是可以在一个线程中通过suspend方法来挂起另外一个线程,在使用sleep时要注意,不能在一个线程中来休眠另一个线程。

1.            // 开始线程  
2.            public void start( );  会自动调用run(),线程进入运行状态。
3.            public void run( );  
4.             
5.            // 挂起和唤醒线程  
6.            public void resume( );     // 不建议使用  
7.            public void suspend( );    // 不建议使用  
8.            public static void sleep(long millis);  休眠状态
9.            public static void sleep(long millis, int nanos); 
10.         sleep使线程休眠后,只能在设定的时间后使线程处于就绪状态(在线程休眠结束后,线程不一定会马上执行,只是进入了就绪状态,等待着系统进行调度)。
11.         
12.        // 终止线程  
13.        public void stop( );       // 不建议使用  
14.        public void interrupt( );  
15.         
16.        // 得到线程状态  
17.        public boolean isAlive( );  
18.        public boolean isInterrupted( );  
19.        public static boolean interrupted( );  
20.         
21.        // join方法  
22.        public void join( ) throws InterruptedException; 
23.        join方法的功能就是使异步执行的线程变成同步执行。
24.        如果不使用join方法,就不能保证当执行到start方法后面的某条语句时,这个线程一定已经执行完。而使用join方法后,直到这个线程退出,程序才会往下执行。

9volatile关键字

volatile关键字用于声明简单类型变量,如intfloatboolean等数据类型。如果这些简单数据类型声明为volatile,对它们的操作就会变成原子级别的。也就是说同一个时刻只能有一个线程可以对变量进行修改。

声明为volatile的简单变量如果当前值由该变量以前的值相关,那么volatile关键字不起作用,如n=n+1n++等。只有当变量的值和自身上一个值无关时对该变量的操作才是原子级别的,如n = m + 1,这个就是原级别的,volatile才有效。可以使用synchronized来代替volatile

10、向线程传递数据的三种方法

由于线程的运行和结束是不可预料的,因此,在传递和返回数据时就无法象函数一样通过函数参数和return语句来返回数据。

一、通过构造方法传递数据

这种方法是在创建线程对象的同时传递数据的,因此,在线程运行之前这些数据就就已经到位了。使用构造方法来传递数据虽然比较安全,但如果要传递的数据比较多时,就会造成很多不便。

二、在类中定义一系列的public的方法或变量(也可称之为字段)。然后在建立完对象后,通过对象实例逐个赋值。然后在运行线程。

三、通过回调函数传递数据

上两种方法是被动接收这些数据的。然而,在有些应用中需要在线程运行的过程中动态地获取数据,在run函数中调用事件函数(回调函数),得到需要的值。

11、从线程返回数据的两种方法

一、通过类变量和方法返回数据

需要注意的是:在线程运行后,需要调用join()方法,保证run方法中的语句都已经运行完,才能确保拿到数据;否则很有可能因为线程未结束,数据还未被赋值。

二、通过回调函数返回数据

12Synchronized关键字同步类方法

对于同一个Java类的对象实例,run方法同时只能被一个线程调用,并当前的run执行完后,才能被其他的线程调用。

对于静态方法来说,只要加上了synchronized关键字,这个方法就是同步的,并不存在非静态方法的多个实例的问题。

需要注意:

(1)     synchronized关键字只能用来同步方法,不能用来同步类变量;

(2)     synchronized关键字不能继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。或者调用父类的这个方法也可。

(3)     大量使用synchronized关键字会造成不必要的资源消耗以及性能损失。虽然从表面上看synchronized锁定的是一个方法,但实际上synchronized锁定的是一个类。也就是说,如果在非静态方法method1method2定义时都使用了synchronized,在method1未执行完之前,method2是不能执行的。静态方法和非静态方法的情况类似。但静态和非静态方法不会互相影响。也就是说,如果在类中使用synchronized关键字来定义非静态方法,那将影响这个中的所有使用synchronized关键字定义的非静态方法。如果定义的是静态方法,那么将影响类中所有使用synchronized关键字定义的静态方法。

13、使用Synchronized块同步方法

可以通过synchronized块来同步一个对象变量。也可以使用synchronized块来同步类中的静态方法和非静态方法。

1.            public void method()  
2.            {  
3.                … …  
4.                synchronized(表达式)  
5.                {  
6.                    … …  
7.                }  
8.            

一、 非静态类方法的同步

使用this做为synchronized块的参数传入synchronized块,this表示当前类实例。

1.            public void method1()  
2.                  {  
3.                      synchronized(this)  // 相当于对method1方法使用synchronized关键字  
4.                      {  
5.                          … …  
6.                      }  
7.                  }  

除了使用this做为synchronized块的参数外,还可以使用SyncBlock.this作为synchronized块的参数来达到同样的效果。在内类(InnerClass)的方法中使用synchronized块来时,this只表示内类,和外类(OuterClass)没有关系,用SyncBlock.this表示外部类,用它来表示内部类与外部类的非静态方法同步。

1.            public class SyncBlock  
2.            {  
3.                … …  
4.                class InnerClass  
5.                {  
6.                    public void method4()  
7.                    {  
8.                        synchronized(SyncBlock.this)  
9.                        {  
10.                        … …   
11.                    }  
12.                }  
13.            }  
14.            … …  
15.        

InnerClass类的method4方法和SyncBlock类的其他三个方法同步,因此,method1method2method3method4四个方法在同一时间只能有一个方法执行。

二、 静态类方法的同步

由于在调用静态方法时,对象实例不一定被创建。因此,就不能使用this来同步静态方法,而必须使用Class对象来同步静态方法。

1.            public class StaticSyncBlock  
2.              {  
3.                  public static void method1()  
4.                  {  
5.                      synchronized(StaticSyncBlock.class)    
6.                      {  
7.                          … …  
8.                      }  
9.                  }  
10.              public static synchronized void method2()    
11.              {  
12.                  … …  
13.              }  
14.          } 

除了使用class字段得到Class对象外,还可以使用实例的getClass方法来得到Class对象。

我们还可以通过Class对象使不同类的静态方法同步。

也可以在非静态方法中使用Class对象来同步静态方法。但在静态方法中不能使用this来同步非静态方法。

14 Synchronized块使用变量来同步

保证变量同时只能被一个方法访问;利用同步变量保证方法同步。

如果在synchronized块中将同步静态变量的值改变,就会破坏方法之间的同步。为了彻底避免这种情况发生,在定义同步静态变量时可以使用final关键字。

注意:在使用synchronized块时应注意,synchronized块只能使用对象作为它的参数。如果是简单类型的变量(intcharboolean),不能使用synchronized来同步。

原创粉丝点击