Java中的线程

来源:互联网 发布:蛇口招商网络怎么样 编辑:程序博客网 时间:2024/06/06 01:10
1,进程和线程进程:是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以有多个线程。比如在Windows系统中,一个运行的xx.exe就是一个进程。Java程序的进程中有几个线程:主线程、垃圾回收线程线程:是指进程中的一个执行任务,一个进程中可以运行多个线程,多个线程可共享数据进程与线程的区别:1)进程有独立的进程空间,进程中的数据存放空间(堆空间和栈空间)是独立的。2)线程的堆空间是共享的,栈空间是独立的,线程消耗的资源也比进程小,相互之间可以影响的。2,线程【1】创建线程三种方式1)继承Thread类1,子类重写父类中的run方法。2,建立子类对象的同时线程也被创建。3,通过调用start方法开启线程:new MyThread().start();特点:不能再继承其他类了,同份资源不共享2)实现Runnable接口1,子类覆盖接口中的run方法。2,通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。3,Thread类对象调用start方法开启线程:new Thread(new Runnable(){public void run() {...}}).start();特点:多个线程共享一个目标资源,适合多线程处理同一份资源该类还可以继承其他类,也可以实现其他接口。3)实现Callable接口(jdk1.5)java.util.concurrent包下的Callable接口:public interface Callable<V> {V call() throws Exception;}Callable和Runnable的区别:1)Callable定义的方法是call,而Runnable定义的方法是run。2)Callable的call方法可以有返回值,而Runnable的run方法不能有返回值。3)Callable的call方法可抛出异常,而Runnable的run方法不能抛出异常。【2】线程类(Thread)中的方法1)currentThread():说明:返回对当前正在执行的线程对象的引用public static Thread currentThread()2)join:public final void join() throws InterruptedException说明:强制运行调用join方法的线程,该线程运行期间,其他线程无法运行,必须等到该线程结束后其他线程才可以运行。public final void join(long millis) throws InterruptedException说明:强制运行调用join方法的线程,在millis毫秒内,其他线程无法运行。如果millis的值为0,则代表一直等待下去。3)setDaemon:说明:将该线程标记为后台线程,该方法必须在启动线程(start()方法)前调用,否则报IllegalThreadStateException异常。 public final void setDaemon(boolean on)后台线程:处于后台运行,任务是为其他线程提供服务。也称为“守护线程”或“精灵线程”。JVM的垃圾回收就是典型的后台线程。判断是否为后台线程:使用Thread对象的isDaemon()方法;特点:1)若所有的前台线程都死亡,后台线程自动死亡。2)前台线程创建的线程默认是前台线程;4)sleep:说明:让当前正在执行的线程休眠(暂停)指定的毫秒数,进入阻塞状态public static void sleep(long millis) throws InterruptedException5)yield:说明:暂停当前正在执行的线程对象,并执行其他线程,即线程礼让。public static void yield()注意:暂停当前线程,但不会阻塞该线程,而是让其进入就绪状态。故完全有可能:某个线程调用了yield()之后,线程调度器又把他调度出来重新执行。6)getName()/setName():说明:获取/设置线程的名称注:第一个子线程默认的名字:Thread-07)stop:说明:终止线程,马上让线程停止运行,并释放该线程所持有的锁!该操作无法保证对象的内部状态正确;@Deprecatedpublic final void stop()8)suspend:说明:挂起线程,使线程进入“阻塞”状态,该状态下CPU不会分给线程时间片,进入这个状态可以用来暂停一个线程的运行,在被resume方法调用前一直不可用。该方法容易发生死锁:调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁。如果其它线程想通过resume()方法来恢复目标线程,同时必须使用目标线程锁定的资源,就会造成死锁。@Deprecatedpublic final void suspend()9)resume:说明:恢复线程,恢复被suspend方法挂起的线程@Deprecatedpublic final void resume()注意:Java2开始已经废弃了suspend()和resume()方法,因为使用这两个方法可能会产生死锁,所以应该使用同步监听器对象调用wait()和notify()的机制来代替suspend()和resume()来控制线程。Object类中的方法:1)wait:public final void wait() throws InterruptedException说明:导致线程进入等待状态,直到它被其他线程通过notify()或者notifyAll唤醒。该方法只能在同步方法中调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常public final void wait(long timeout) throws InterruptedException说明:在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。 该方法只能在同步方法中调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常2)notify:说明:随机选择一个在该对象上调用wait方法的线程,解除其阻塞状态。该方法只能在同步方法或同步块内部调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。public final void notify()3)notifyAll:说明:唤醒在此对象监视器上等待的所有线程。该方法只能在同步方法或同步块内部调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常public final void notifyAll()注:wait()、notify()、notifyAll(),这三个方法必须由同步监听器对象来调用,两种情况:1)synchronized修饰的方法:因为this就是同步监视器,所以可以在同步方法中调用这三个方法2)synchronized修饰的同步代码块:必须使用同步监听器对象调用这三个方法原理:Thread类中有一个标志,表明线程是应该活动还是应该挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。wait、notify、notifyAll这三个方法为何不在thread类里面,而是在Object里面?答:因为锁属于对象,由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中。【3】线程的生命周期新建(New):当程序使用new创建一个线程后,该线程处于新建状态就绪(Ready/Runnable):当线程对象调用start()方法后,该线程处于就绪状态,线程计入线程队列排队,此时该状态线程并未开始执行,它仅表示可以运行了运行(Running):若处于就绪状态的线程获得了CPU,开始执行run()线程执行体,该线程处于执行状态阻塞(Blocked):线程运行过程中需要被中断注意:阻塞状态不能直接转成运行状态,阻塞状态只能重新进入就绪状态阻塞的原因:1)线程执行了Thread.sleep(n)方法,线程放弃CPU,睡眠n毫秒。2)线程执行了某个对象的wait()方法,进入阻塞状态,只有等到其它线程执行了该对象的notify()方法或notifyAll()方法,才能解除该线程的阻塞状态。3)线程执行I/O操作、线程进行远程调用时,因为等待相关资源的进入而变成阻塞状态。4)线程要执行一段同步代码时,由于无法获得相关的同步锁而进入阻塞状态。死亡(Terminated):1)run()执行完成,线程正常结束2)线程抛出未捕获的Exception或Error3)调用线程的stop()。(易导致死锁,不推荐)注意:1)主线程结束后,其他线程不受其影响,不会随之结束;一旦子线程启动起来后,就拥有和主线程相等地位,不受主线程影响2)测试线程是否活着,可用线程对象的isAlive()方法。当线程处于就绪,运行,阻塞状态返回true;当线程处于新建和死亡状态,返回false3)已死亡的线程是不可能通过start()方法唤醒线程的(线程死不能复生),否则引发IllegalThreadStateException异常重要:我们一般使用布尔变量来控制线程,默认为true的时候,线程就可以执行,想要结束线程时,把变量设为false即可。boolean flag = true;//用变量来控制线程public void run() {int i = 0;while (flag) {if (i == 25) {flag = false;}System.out.println("==" + i);i++;}}【4】线程的优先级每个线程都有优先级,优先级的高低只和线程获得执行机会的次数多少有关,并非线程优先级越高的就一定先执行,哪个线程的先运行取决于CPU的调度;默认情况下main线程具有普通的优先级,而它创建的线程也具有普通优先级。Thread对象的setPriority(int x)和getPriority()来设置和获得优先级。MAX_PRIORITY:值是10MIN_PRIORITY:值是1NORM_PRIORITY:值是5(主方法默认优先级)注:不能在程序中通过Java线程的优先级来判断都处于就绪状态的线程的执行顺序。Java的线程是通过映射到操作系统的原生线程上来实现的,故最终的调度还是取决于操作系统。虽然操作系统也提供线程优先级的功能,但是并不能和Java线程的优先级一一对应,Java线程的优先级可能被操作系统改变。【5】同步synchronized线程安全:在多线程环境下,一个类在执行某个方法时,对类的内部实例变量的访问是安全的字节码指令--同步指令Java虚拟机支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用监听器(Monitor)来实现的。1)方法级的同步是隐式的,即无需通过字节码指令来控制,它的实现是包含在方法调用和返回操作之中。1)虚拟机可以从方法常量池的方法表结构中的ACC_SYNCHRONIZED访问标志得知一个方法是否声明为同步方法。2)当方法调用时,调用指令将会检查方法ACC_SYNCHRONIZED访问标志是否被设置,如果设置了,执行线程就要求先成功持有监听器,然后才能执行方法,最后当方法完成(无论是否是正常完成的)时释放监听器。3)在方法执行期间,执行线程持有了监听器,其它任何线程都无法再获取到同一个监听器。4)如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那么这个同步方法所持有的监听器将在异常抛到同步方法之外时自动释放。2)同步一段指令集序列通常是由Java语言中的synchronized语句块来表示的,Java虚拟机的指令集中有monitorentry和monitorexit两条指令来支持synchronized关键字的语义。同步代码块:synchronized(obj){}注意:需要一个同步监听器(即:锁)obj若线程实现了Runnable接口,则:同步监听器(即:锁)可以选 this、所在类的Class对象、或者任何一个不变的对象若线程继承了Thread(没有实现Runnable接口),则:同步监听器(即:锁)可以选 所在类的Class对象、或者一个不变的对象,但是不能用this同步方法:只需要使用synchronized修饰要同步的方法即可:synchronized 返回值类型 方法名(参数列表){} 注:非静态方法的监听器默认是this,静态方法的默认监听器就是当前方法所在类的Class对象。若线程实现了Runnable接口,则可以使用synchronized直接修饰run方法,若没有实现Runnable接口,则不能使用synchronized直接修饰run方法【6】同步锁:jkd1.5后的另一种同步机制:1)通过显示地定义同步锁对象来实现同步,这种机制,同步锁应该使用java.util.concurrent.locks包下的Lock充当。2)在实现线程安全的时候,通常使用可重入锁:java.util.concurrent.locks.ReentrantLock3)使用ReentrantLock可以显示地加锁和解锁,和使用synchronized锁的作用相似,但功能更强大。同步锁格式:public class X {private final ReentrantLock lock = new ReentrantLock();//定义需要保证线程安全的方法public void foo() {//加锁lock.lock();try{//... method body}finally{//在finally释放锁lock.unlock();}}}注:Lock 替代了synchronized方法和语句的使用,Condition替代了Object监视器方法的使用Condition实例实质上被绑定到一个Lock上。使用Lock的newCondition()方法可以获得绑定的Condition实例Condition中的方法:await():  等价于同步监听器的wait()方法;signal(): 等价于同步监听器的notify()方法;signalAll(): 等价于同步监听器的notifyAll()方法;


0 0