`

黑马程序员Java培训和Android培训Java 四Thread 线程

 
阅读更多
黑马程序员
进程和线程
在多任务系统中,每个独立执行的程序称为进程,也即是"正在进行的程序"。我们现在使用的操作系统一般都是多任务的,即能够同时执行多个应用程序,实际情况是,操作系统负责对CPU等设备的资源进行分配和管理,虽然这些设备某一时刻只能做一件事,但是以非常小的时间间隔交替执行多个程序,就可以给人以同时执行多个程序的感觉。

一个进程中又可以包含一个或多个线程,一个线程就是一个程序内部的一条执行线索,如果要一程序中实现多段代码同时交替运行,就需要产生多个线程,并指定每个线程上所要运行的程序代码段,这就是多线程。
只有双CPU才能实现真正的多进程。

单线程和多线程的对比
ThreadDemo1.Main()----->TestThread.run()------->单线程执行。
ThreadDemo1.Main()|----->TestThread.run()
                  |            |
                  |多线程向下执行|多线程向下执行



用Thread类创建线程
要将一段代码在一个新的线程上运行,该代码应该在一个类的run函数中,并且run函数所在的类是Thread类的子类。倒过来看,我们要实现多线程,必须编写一个继承了Thread类的子类,子类要覆盖Thread类中的run函数,在子类的run函数中调用想在新线程上运行的程序代码。

启动一个新的线程,我们不是直接Thread的子类对象的run方法,而是调用Thread类对象的start (从Thread类的继承的)方法;Thread类对象的start方法将产生一个新的线程,并在该线程上运行该Thread类对象中的run方法,根据面向对象的运行时的多态性,在该线程上实际运行的是Thread子类(也就是我们写的那个类)对象中的run方法。

由于线程的代码段在run方法中,那么该方法执行完成以后线程也就相应的结束了,因而我们可以通过控制run方法中循环条件来控制线程的结束。




后台线程与联合线程
如果我们对某个线程对象在启动(调用start方法)之前调用了setDaemon(true)方法,这个线程就变成了后台线程。

对java程序来说,只要还有一个前台线程在运行,这个进程就不会结束,如果一个进程中只有后台线程运行,这个进程就会结束。

pp.join()的作用是把pp所对应的线程合并到调用pp.join();语句的线程中。join(千秒数)与主线程合并的时间,时间过了分成两个进程。

实现接口的进程与创建进程的子类的区别?
共同点:都可以创建多线程。
不同点:对临界资源的同步与否实现接口的线程方便定义线程引用。
实现Runnable接口的线程可以模拟铁路订票系统。用实现Runnable接口的类比直接继承父类Thread的方式要方便的多。




使用Runnable接口创建多线程
适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码,数据有效分离,较好地体现了面向对象的设计思想

可以避免由于Java的单继承特性带来的局限。我们经常碰到这样一种情况,即当我们要将已经继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable接口。

当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。

事实上,几乎所有多线程应用都可以用Rnunable接口方式。

多线程在实际中的应用
网络聊天程序的收发
1)如果一方从键盘读取了数据并发送给了对方,程序运行到"读取对方回送的数据",并一直等待对方回应,程序不能再做其他事情,这时程序处于阻塞状态,即使用户想正常终止程序运行都不可能,更不能实现 "再给对方发送一条信息催促对方赶快应答"这样的事情了。
2)如果程序没有事先从键盘上读取数据并向外发送,程序将一直在"从键盘上读取数据'处堵塞',即使有数据从网上发送过来,程序无法到达"读取对方回送的数据"处"程序将不能收到别处先主动发送过来的数据。
表记录的复制的中途取消

www服务器为没有个来访者都建立专线服务









多线程的同步
线程安全:即线程同步;即在代码块中每次都只能有一个线程在使用,其他线程必须 等待。即产生多个线程对某一资源的"争抢"现象。

同步代码块:使用synchronized()关键字来声明的。(注意块作用域。)其后面必须跟一个对象,可以是任意一个对象,即可以声明一个空字符串。不能将定义的空字符串写在run()方法中。使用同步语句是以牺牲系统性能为代价的,如果在以后编程的过程中,没有同步问题,尽量不要使用同步语句。

同步函数:重新定义一个public void sale()函数,在void之前加上关键字synchronized,将run()方法中未实现同步的代码段写入sale()函数,并且在run()方法中利用无限循环调用sale()方法。同样可以实现线程的同步。

代码块与函数间的同步:寻找线程执行的线索的一种方法"标记法"即用打印函数在某个函数中书写,验证猜测。线程内部的调用机制:  线程名.start()只是表示线程处于启用状态,并不是马上那个执行线程内部的函数。若想要代码块与同步函数同步,则只需要将同步块中的synchronized方法中的this。


注意死锁问题:


线程间的通信
wait:告诉当前线程放弃监视器并进入睡眠状态直到其他线程进入同一监视器并调用notify为止。
notify:唤醒同一对象监视器中调用wait的第一个线程。用于类似饭馆有一个空位后通知所有等候就餐的顾客中的第一位可以入座的情况。
notify All:唤醒同一对象监视器中调用wait的所有线程,具有最高优先级的线程首先被唤醒并执行。用于类似某个不定期的培训班终于招生满额后,通知所有学员都来上课的情况。

在不同的类中使用同一个对象来实现同步也是可以实现的。

线程的等待和唤醒过程
Thread T                   
   |
   |
synchronized(o)      <---线程t得到对象o的lock旗标
   |
   |
  o.wait()           <---此线程t被放置在对象o的等待线程池中,t自动释放o的
   |                  锁旗标
   |
  o.notify()         <---当另外的线程执行了对象o的notify()方法后
                      线程t可能会被从o的等待线程池中释放出来,并且移动到等待线程对象o的锁旗标的线程池中,当t的到锁旗标时就会执行下去。
注意:使用wait() notify() notify all语句可以使线程调用更加理性。

解决同步问题也可以将同步代码写在函数中,利用同步函数解决比较方便


如何控制线程的生命?
suspend resume stop
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics