黑马程序员_Java多线程学习

来源:互联网 发布:java 数组最大长度 编辑:程序博客网 时间:2024/05/22 00:37

------- android培训、java培训、期待与您交流! ----------

一个正在运行完整的Java应用程序实例就是一个进程,在这个应用程序中会保护许多小的执行单元,那么每一个执行单元或控制单元就是一个线程。一个应用程序必须至少有一个线程(主线程)。

一、线程的创建

Java中线程的创建方式有两种:1、继承Thread类实现run方法。通过例子说明创建及启动线程的步骤:

class MyThread extends Thread //1、继承Thread类{public MyThread(){}public void run()//2、实现run方法{DoWork();//将要执行的功能}public void DoWork(){}}class MyThreadDemo{public static void main(String[] args){MyThread thread=new MyThread();//3、创建线程子类的对象thread.start();//4、利用start()函数启动线程,并调用run方法}}


注意:该创建方式的一个弊端:因为要创建某线程必须要继承Thread类,而很多情况下一个功能类很可能会继承其他类,Java只支持单继承,那么这个时候就无法再继承Thread类。为了解决这一问题,Java中提供了另外一种创建线程方式,通过实现接口来扩展功能。

2、由于第一中创建线程方式的局限性,第二种方式应运而生——通过实现Runnable接口复写run方法来实现线程的创建。其具体步骤:

class MyFun implements Runnable //1、实现Runnable接口,扩展功能{public MyFun(){}public void run()//2、实现run方法{DoWork();//将要执行的功能}public void DoWork(){}}class MyThreadDemo{public static void main(String[] args){MyFun fun=new MyFun();//3、创建MyFun类的对象(注意它不是线程对象)Thread thread=new Thread(fun);//创建线程对象,并将fun引用作为参数传递给线程的构造函数,与线程建立联系thread.start();//4、利用start()函数启动线程,并调用run方法}}


第二种方式只是对该类进行额外的功能扩展,java就提供了一个接口Runnable并定义了run方法,其实run方法的定义就是为了存储多线程要运行的代码。由于实现Runnable接口可以避免单继承的局限性,所以,通常创建线程都用第二种方式。

二、线程的几种状态。

线程主要有:被创建、运行、冻结(睡眠和等待)、消亡及阻塞等五种状态。

1、线程被创建后通过调用start方法使该线程具有资源的访问资格,同时具备执行权后线程进入运行状态。

2、如果start后该线程拥有了执行资格但是还不具备访问资源的执行权,此时该线程处于临时阻塞状态,当其他线程交出执行权且自己获得执行权后才进入运行状态。

3、对于运行中的线程,可以通过sleep和wait方式将线程进行冻结,对于sleep方式当睡眠时间到,线程自动进入运行状态。而wait方式只能等待别的线程调用notify或notifyAll才能唤醒进入运行状态。各状态之间转换的过程如下图所示:

三、多线程的问题,通常为满足一定的需求,会需要多个线程对同一个资源进行处理,比如一个数据库的写入操作、运算、读取操作等,可能会需要两个以上的线程对这样一个数据库进行处理,那么在线程运行期间会出现数据访问或写入错乱的问题,这就是多线程的安全问题。故未来解决线程安全问题,Java就提供了线程同步机制。

class MulThreaDemo extends Thread{private int count=0;public MulThreaDemo(){}public void run(){for(int i=0;i<10;i++){count += 10;System.out.println(Thread.currentThread().getName()+"----"+this.count);Thread.sleep(20);}} }class Test{MulThreaDemo m1=new MulThreaDemo();MulThreaDemo m2=new MulThreaDemo();MulThreaDemo m3=new MulThreaDemo();m1.start();m2.start();m3.start();}


运行程序我们将会看到m1、m2、m3线程的打印结果是错乱排列的,我本意是要先打印m1结果,然后是m2、m3,这就是由于多个线程同事共享同一个资源造成的,那么,为了解决这个现象,可以利用关键字synchronized来标记同步代码块或同步函数。这个关键字就告诉jvm当前线程拥有访问权,在该线程结束前其他线程不能访问该段代码或函数,此刻其他线程处于等待状态位于线程池中,当线程1执行完后,放弃执行权,那么线程池中等待的线程顺序获得执行权,执行代码。

synchronized的使用方式有两种:同步代码块和同步函数。修饰一个代码块时,synchronized需要指定同步对象,而且保证多线程的同步对象为同一个对象。特殊情况:对于在静态方法内声明同步代码块的地方,在指定同步对象的时候要注意,因为静态方法是在加载类的时候就存在,先于对象引用存在,此时不能再指定所属对象为同步对象,而是要指定为该类的字节码文件对象。

第二种就是同步函数,直接在返回值前添加关键字synchronized(不能放置于返回值的后面)即可。注意同步函数使用的锁对象是this,而同步代码块可以使用任意对象为锁对象。特殊情况是静态方法为同步函数的情况,锁对象不再是this而是该类的字节码文件对象。

eg:

class SynTest{public SynTest(){}//普通方法中指定同步代码块public void method1(){synchronized(this){//要同步的代码}}//注意是静态方法中同步代码块public static void method2(){synchronized(SynTest.class){//需要同步的代码块}}//普通同步函数public synchronized void method3(){}//静态的同步函数public static synchronized void method4(){}}

注意:判断线程同步的两个条件:1、必须有两个或两个以上的线程运行。2、多线程必须保证使用的是同一个同步锁(同步对象)。

0 0