如何创建和理解线程
曾经在学习操作系统的时候,进程是一个重点内容,线程也学习过,但是没有什么深刻的印象。在Java多线程的学习中对线程有了一个全面而深刻的理解。一个进程可以包含一个或多个线程,一个线程就是一个程序内部的一条执行线索。
创建多线程有两种方法:继承Thread类和实现Runnable接口。通过调用继承thread对象的start()方法就可以调用该类中的run()方法。如果想要将一段代码在一个新的线程上运行时,该代码就应该位于一个类的run函数中,并且run()函数所在的类是Tread类的子类。启动一个新的线程不是直接调用子类的run方法,而是调用thread类的start方法,thread类的start方法将产生一个新的线程,并在新的线程上运行该thread类对象中的run方法。由于线程的代码段是在run方法中,那么该方法执行完以后,线程也就相应结束了。
当使用Tread(Runnable target)方法创建线程对象时,需要为该方法传递一个实现了Runnable接口的对象,这样创建的线程将调用了那个实现了Runnable接口的类对象中的run()方法作为其运行代码,而不再调用Thread类中的run方法了。
通过对这两种实现多线程的方法对比我们知道,实现Runnable接口相对于继承Thread类来说有以下几个优点:
(1) 适合多个相同程序代码的线程去处理同一个资源的情况,把虚拟的CPU同程序的代码,数据有效分离。
(2) 可以避免由于Java的单继承带来的局限性。
(3) 有利于程序的健壮性,代码能够被多个线程共享,代码与数据时独立的。多个线程可以操作想相同的数据,与他们的代码无关。
几乎所有的多线程都可以使用实现Runnable接口的方法实现。
关于后台线程的概念就不在累述,关于如何设置后台进程就需要使用setDaemon方法。按照前面产生方式产生的线程都是前台线程,在一个线程对象启动之前调用了setDaemon(true)方法,这个线程就变成后台线程了,只有后台线程运行时程序就会结束。通过调用Thread类的join方法就可以把该线程合并到调用join语句的线程中。
关于多线程的应用是十分广泛的。我们可以使用多线程来编写一个网络聊天程序,多线程编写的网络聊天程序才可以正确运行,而单线程的聊天程序,由于等待某件事件的发生常常会因互相等待而发生崩溃。关于www服务器就是一个多线程的很典型的例子,它可以为每一个访问者建立一个线程。
多线程的同步
说句实在话,如果不是张老师讲解关于线程安全的问题,我还真是不懂什么叫线程安全。我们可以使用同步代码块和同步函数来实现线程安全。同步代码块就相当于一个独木桥,任何时候只允许一个线程在上面运行,这样就可以避免线程安全问题的产生。关于同步代码块我们使用synchronized(object){代码段} 张老师在本段的讲解中使用了通俗的讲解方法和专业的术语两种方式来讲解,比如锁旗标,线程池啊。讲解得已经非常详细了,在此我就不再累述了。在看看如何使用同步函数来实现线程之间同步。只需要在需要同步的函数前加上synchronized关键字就可以了。运行的结果与使用同步代码块是完全一样的。代码块和同步函数之间也可以实现同步的。只是我们需要寻找一个监视器对象放在同步代码块中,究竟这个监视器对象怎么样设置才是对的呢。在前面的学习中我们学习了一个非常有用的关键字对象,就是this,它给我们的编程带来了很大的方便,这里同步代码块和同步函数的同步实现也是通过使用this关键字来实现的。
我记得在学习操作系统原理的时候,老师给我们重点讨论的一个问题就是死锁的问题。关于死锁的概念,死锁的产生条件,死锁的预防,死锁的检测,从原理上已经理解的非常清楚了。在这里我们通过编写程序,来进一步理解死锁的产生机制,以及如何避免死锁。因此在以后编写多线程的程序的时候,应该考虑到这一点,要预防死锁的产生。
线程间的通信(线程间交互)
本节通过消费者生产者模型的问题引出了关于进程之间的通信。Java是通过object类的wait,notify,notifyAll这几个方法来实现线程之间的通信的。由于所有的类都是从object继承来的,因此在任何类中都可以直接使用这些方法。三个方法的简单说明如下:
Wait:告诉当前进程放弃监视器并进入睡眠状态,直到其他进程进入同一监视器并调用notify为止。
Notify:唤醒同一对象监视器中调用wait的第一个线程。
notifyAll:唤醒同一对象监视器中调用wait的所有线程,具有最高优先级的线程首先被唤醒并执行。
Wait,notify,notifyAll这三个方法只能在synchronized方法中调用,即无论线程调用一个对象的wait还是notify方法,该线程必须得到该对象的锁旗标。这样,notify只能唤醒同一对象监视器中调用wait的线程,使用多个对象监视器,我们就可以分组有多个wait,notify的情况,同组里的wait只能被同组的notify唤醒
线程生命的控制
控制线程生命周期的方法有很多种,如:suspend方法,resume方法和stop方法。但是我们并不推荐使用这三个方法,主要是因为以下几点:
(1)会导致死锁的发生。
(2)它允许一个线程通过直接控制另外一个线程的代码来直接控制那个线程。
虽然stop方法可以避免死锁,但会带来另外的不足,如果一个线程正在操作共享数据段,操作过程没有完成就stop的话,将会导致数据的不完整性。因此stop方法我们也不提倡使用。在实际的编程操作中我们推荐使用控制run方法中的循环条件的方式来结束一个线程,这也是实际情况中使用最多的。
至此Java的多线程也就学习完毕,下面是关于多线程的一点总结信息。
要认识多线程就要从操作系统的原理说起。多线程的目的是为了最大限度的利用CPU资源。Java编写程序都运行在在Java虚拟机(JVM)中,在JVM的内部,程序的多任务是通过线程来实现的。每用java命令启动一个java应用程序,就会启动一个JVM进程。在同一个JVM进程中,有且只有一个进程,就是它自己。
Java 虚拟机允许应用程序并发地运行多个执行线程。Java语言提供了多线程编程的扩展点,并给出了功能强大的线程控制API。
常见线程名词解释
主线程:JVM调用程序mian()所产生的线程。
当前线程:这个是容易混淆的概念。一般指通过Thread.currentThread()来获取的进程。
后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。
前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。