Java笔记(七)多线程

来源:互联网 发布:淘宝亲密付退款退到哪 编辑:程序博客网 时间:2024/04/30 06:15

一个独立程序的每一次运行称为一个进程。每一个进程中又可以包含多个同时执行的子任务,对应于多个线程。
1.多线程编程基础
(1)线程的概念
进程:创建一个进程代价大,设置一个进程要占用想当一部分处理器时间和内存资源,而且进程间的通信也很不方便。
如果同一个应用程序需要并行处理多件任务,就不必建立多个进程,而是在一个进程中建立多个线程。
在Java中创建多线程的方法:(继承Thread类和实现Runnable接口)
(2)Thread类
当JVM执行单线程的Java程序时,只有一个非后台线程,例如执行中的main方法。
Java的Thread类封装了Java程序中的一个线程(对象)需要拥有的属性和方法。
从Thread类派生一个子类,并创建这个子类的对象,就可以产生一个新的线程。这个子类应该重写Thread类的run方法,在run方法中写入需要在新线程中执行的语句段。这个子类的对象需要调用start方法来启动,新线程将自动进入run方法。原线程将同时继续往下执行。
Thread类直接继承了Object类,并实现了Runnable接口。Thread类位于java.lang包中。
(3)Runnable接口
Runnable接口也是Java多线程机制的一个重要部分,实际上他只有一个run()方法。实现Runnable接口的类对象可以用来创建线程,这时start方法启动此线程就会在此线程上运行run()方法。
在编写复杂程序时相关的类可能已经继承了某个基类,而Java不支持多继承,在这种情况下,便需要通过实现Runnable接口来生成多线程。其实使用Runnable接口的好处不仅在于解决了多继承的问题,与Thread类相比,Runnable接口更适合多线程处理同一资源。
(4)线程间的数据共享
当多个线程的执行代码来自同一个类的run方法时。即称他们共享相同的代码;当共享相同的对象时,即他们共享相同的数据。使用Runnable接口轻松实现多个线程共享相同的数据,只要用同一个实现了Runnable接口的实例作为参数创建多个线程就可以了。
(5)多线程的同步控制
被多个线程共享的数据在同一时刻只允许一个线程处于操作之中,这就是同步控制中的一个问题:线程的互斥。
在Java中,利用对象的“锁旗标”可以实现线程间的互斥操作。每个对象都有一个“锁旗标”与之相连。一个对象的锁旗标只有一个,所以利用一个对象锁旗标的争夺,可以实现不同线程的互斥效果,当一个线程获得锁旗标后,需要该锁旗标的其他线程只能处于等待状态。
关键字:synchronized可以实现与一个锁旗标的交互。例如: synchronized(对象){代码块}
Synchronized功能:首先判断对象的锁旗标是否在,如果在就获得锁旗标,然后就可以执行紧随其后的代码段;如果对象的锁旗标不再(已被其他线程拿走),就进入等待状态,直到获得锁旗标。当被synchronized限定的代码段执行完后,就释放锁旗标。
除了可以对指定的代码段进行同步控制之外,还可以定义整个方法在同步控制下执行,只要在方法定义前加上synchronized关键字即可。
数据共享和线程互斥操作经常是密不可分的。
(6)线程之间的通信(多线程的执行往往需要互相之间的配合)
java.lang.Object类的wait、notify等方法为线程间的通信提供了有效手段。
public final void wait();//调用该方法,该线程暂停进入等待池,直到调用notify或notifyAll方法
public void notify();//唤醒等待该对象锁旗标的第一个线程
public void notifyAll();//唤醒等待该对象锁旗标的所有线程,具有最高优先级的首先被唤醒并执行
(7)后台线程
后台线程(也叫守护线程)通常是为了辅助其他线程运行的线程,他不妨碍程序的终止,只要还有前台线程在运行,这个进程就不会结束,如果没有了前台线程运行,则不管有没有后台线程运行,进程都该结束。
如果对某个线程对象在启动(调用start方法)之前调用了setDaemon(true)方法,这个线程就变成了后台线程。
2.线程的生命周期
(1)线程的几种基本状态
这里写图片描述
这里写图片描述
一个线程在任何一个时刻都处于某种线程状态。
(1.线程启动并进入就绪状态。2.运行状态(各线程分时占用CPU)。3.死亡状态。4.堵塞状态(调用sleep方法后;执行synchronized同步代码块;调用wait方法后)。)
Thread类API函数isAlive(),判断线程状态。
(2)死锁问题
陷入一个彼此等待的轮回中,任何一个线程都动弹不得,就陷入死锁。
(3)控制线程的生命
用stop方法可以结束线程生命,但如果一个线程正在操作共享数据段,操作过程没有完成就用stop结束的话,将会导致数据的不完整,因此并不提倡使用此方法。通常通过控制run方法中循环条件的方式来结束一个线程。
3.线程的优先级
控制多个线程在同一个CPU上以某种顺序运行称为线程调度,JVM支持一种非常简单的、确定的算法,叫做固定优先级算法,这个算法基于线程的优先级对其进行调度。
每个Java线程都有一个优先级,其范围都在Thread.MIN_PRIORITY(常数1)和Thread.MAX_PRIORITY(常数10)之间,默认情况下,每个线程的优先级都设置为Thread.NORM_PRIORITY(常数5)。具有较高优先级的线程比优先级较低的线程优先执行。在某个线程A运行过程中创建的新的线程对象B初始状态具有和线程A相同的优先级。并且如果A是一个后台程序,则B也是一个后台程序,也可以在创建之后的任何时候,通过setPriority(int priority)方法改变其原来的优先级。
对于相同优先级的线程,Java的处理是随机的,如何处理这些线程取决于底层的操作系统的策略。Java支持10个优先级,但是底层系统支持的优先级更少,这样会照成一些混乱,因此只能将优先级作为一种很粗略的工具使用,最后的控制可以通过明智的使用yield()函数来完成。假设某个线程正在运行,则只有出现一下情况才会暂停运行:(一个具有更高优先级的线程变为就绪状态;由于输入输出(或其他原因),调用sleep、wait、yield方法使其发生堵塞;对于支持时间分片的系统,时间片的时间期满)。
在某些情况下,优先级相同的线程分时运行;在另一些情况下,线程将一直运行到结束。通常情况下,请不要依靠线程的优先级来控制线程的状态。
JVM不是时间分片的,通常,在一个线程内部插入yield()语句,这个方法会使正在运行的线程暂时放弃执行,这时具有相同优先级的线程就有机会获得调度开始执行,但较低优先级的线程仍将被忽略不参加调度。
对于分时系统,如果所有处于可运行状态的线程都具有相同的优先级,他们将以循环的方式被调度,因此能够在时间片内被执行,所以没必要使用yield方法;但在非分时系统中,互相协作的相同优先级线程应定期调用yield方法,以便能够平滑的处理其他相同优先级线程。

0 0
原创粉丝点击