黑马程序员_多线程

来源:互联网 发布:iphone6软件无法联网 编辑:程序博客网 时间:2024/05/27 00:45

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

 

进程:是一个正在执行的程序。每个进程执行都有一个执行顺序,该顺序是一个执行路径或叫一个控制单元。

 

线程:是进程中的一个独立的控制单元。线程控制着进程的执行,一个进程可以拥有多个线程,一个线程必须有一个主进程。线程是共享主进程中的共享变量及部分环境,相互之间协同来完成进程所要完成的任务。

注意:在单核CUP中,任一时刻,处在运行中的线程仅有一个,CPU在线程间快速切换,导致产生多个线程同时运行的错觉。

 

线程的创建和启动:

      java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每条线程的作用是完成一定的任务,实际上是执行一段顺序执行代码,java使用run方法来封装一段顺序执行代码。

创建线程的方法有两种:一种是继承Thread类创建线程类,重写run方法,该run方法的方法体就是代表了线程需要完成的任务,因而常把run方法称为线程执行体,启动线程时要创建自定义线程对象,调用start方法(若调用run方法并没有开启新线程,仅仅是主线程执行run方法);另一种是实现Runnable接口创建线程类,重写run方法,启动线程时创建Thread类对象,将自定义线程类作为参数传入Thread类的构造函数中,调用Thread对象的start方法。

        

两种创建线程方式的区别:1、Java仅支持单继承,若自定义线程类是其他类的子类,则只能使用实现接口方式创建线程;2、继承方式线程执行代码存放在Thread类子类的run方法中,实现方式代码存放在Runnable接口子类的run方法中。

 

线程的状态:

线程通常都有五种状态,创建、就绪、运行、阻塞和死亡。

第一是创建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态;

第二是就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态

第三是运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。

第四是阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,wait等方法都可以导致线程阻塞。

第五是死亡状态。如果一个线程的run方法执行结束,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪状态。

 

控制线程的常用方法:

    加入线程:java 提供了让线程等待另一个线程完成的方法join()方法。当A线程执行过程中调用线程B的join方法时,线程A就会等待,当线程B执行完毕后,A才会继续执行。

    后台线程:有一种线程,它是在后台运行的,它的任务就是为其他线程提供服务,这种线程称为后台线程,又称为守护线程。后台线程有一个特征就是如果所有的前台线程都死亡,后台线程会自动死亡。调用Thread对象的setDaemon(ture)方法可以将指定线程设置成后台线程,注意该方法必须在启动线程前调用。

    线程睡眠:Thread.sleep方法,让当前线程让出CUP执行权;如果当前线程握有同步锁,该锁并不会被释放;在睡眠指定时间后恢复CUP执行权。

         线程等待:wait方法,在对象的监视器上等待,让出CUP执行权;如果当前线程握有同步锁同时释放锁,必须由其他线程在执行过程中调用此对象的notify()方法或 notifyAll() 方法才能唤醒。

         线程唤醒:notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择(这种选择具有随意性)唤醒其中一个线程。notifyAll()方法唤醒在此对象监视器上等待的所有线程。

暂停线程:Thread.yield()方法暂停当前正在执行的线程对象,并执行其他线程。

更改线程优先级:setPriority方法,优先级0~10,默认优先级为5。

中断线程:interrupt()当没有指定的方式让冻结的线程恢复到运行状态时,强制让线程恢复到运行状态中来,操作标记让线程结束。

 

    如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,则可以通过调用Thread类的静态方法sleep方法。yield()方法是一个和sleep方法有着相似的方法,它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将线程转入就绪状态。每个程序执行都具有一定的优先级,Thread提供了setPriority(int newPriority)和getPriority()方法来设置和返回指定线程的优先级。

 

多线程的安全问题:

因为多线程在执行时,CPU的切换具有随机性,因而在多个线程操作同一共享数据并有线程对共享数据进行修改时,容易导致共享数据的错误。因而对于共享数据进行排他处理,即只能让一个线程对共享数据进行访问修改,其他线程在该线程执行完毕之前不可访问共享数据。

找出多线程的安全问题:1)明确哪些代码是多线程运行的代码;2)明确共享数据;3)明确多线程运行代码中哪些语句是操作共享数据的。

 

多线程的安全问题解决方案—同步

         同步的前提:1)必须有两个或两个以上的线程;2)必须是多个线程使用同一个锁;

    同步代码块:synchronized(obj){.....//此处的代码就是同步代码块  }  

该语法格式中的synchronized后括号里的obj就是提供监视器的对象,一次只能有一个线程拥有对象的监视器。该代码的含义是:线程开始执行同步代码块之前,必须先获得对指定对象的监视器。任何时刻只能有一个线程拥有该对象的监视器,当同步代码块执行结束之后,该线程自动释放了该对象的监视器的拥有权。

同步函数与同步代码块是对应的,同步方法就是使用synchronized关键字来修饰某个方法,则该方法称为同步方法。对于同步方法无需指定同步监视器,同步方法的同步监视器就是this,也就是该对象本身。当线程的同步方法执行完就会释放同步监视器,在方法中遇到break、return终止代码,出现未处理的Error,Exception,执行了监视器对象的wait()方法则会释放当前对象的监视器的拥有权。注意同步函数被静态修饰后,使用的锁对象是该方法所在类的字节码对象,即类名.class。

    同步锁Lock,这是从java 1.5之后提供的另一种线程同步机制:它通过显式定义同步锁Lock对象来实现同步,在这种机制下,同步锁应该使用Lock对象充当。通常认为:Lock提供了比synchronized方法和代码块更广泛的锁定操作,Lock实现允许更灵活的结构,可以具有差别很大的属性,并且可以支持多个相关的Condition对象。

Lock对象的创建:Lock lock = new ReentrantLock();

Lock上锁:lock.lock()

Lock解锁:lock.unlock();

用Condition类对象中的await、signal、signal等方法替代Object类中的wait、notify、notifyAll方法。Condition的对象基于Lock类对象创建,Condition对象调用await方法时会抛出InterruptedException,故调用Condition对象方法时可对异常进行声明。

 

死锁

    当两个线程相互等待对方释放同步对象的监视器时就会发生死锁,java虚拟机没有监测,也没有采用措施来处理死锁情况,所以多线程编程时应该采取措施避免死锁的出现。一旦出现死锁,整个程序既不会发生任何异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。产生原因:同步嵌套同步,使用的锁不同。

0 0