首先,要辨析进程与线程的概念:
进程是程序执行的过程,它持有资源和线程,相对于程序本身而言具有动态性。
线程是系统中最小的执行单元,同一个进程中可能有多个线程,它们共享该进程持有的资源。线程的通信也称为线程的交互,方式主要有互斥和同步。同步是指线程之间通过共同协作完成某项工作,线程间具有次序性;互斥是指线程间对某一资源的竞争,一次只能有一个线程访问该资源。
介绍完了这些基本概念,下面简单介绍一下Java对多线程的支持.
java中通过类Thread和接口Runnable来实现多线程的操作。它们都有一个run方法来指定线程工作时执行的代码。
Thread类的常用方法
类别 | 名称 | 简介 |
线程的创建 | Thread() | |
Thread(String name) | ||
Thread(Runnable target) | ||
Thread(Runnable target, String name) | ||
线程的方法 | void start() | 启动线程 |
static void sleep(long millis) | 线程休眠 | |
static void sleep(long millis, int nanos) | ||
void join() | 某线程调用join方法后,使其他线程等待该线程终止 | |
void join(long millis) | ||
void join(long millis, int nanos) | ||
static void yield() | 当前正在运行的线程立刻释放处理器,重新加入竞争处理器的队列 | |
获取线程引用 | static Thread currentThread() | 返回当前正在运行的线程的引用 |
使用多线程有两种方式,一种是直接继承Thread类,一种是实现Runnable接口
另外,通过volatile关键字声明的成员变量可以保证当其他线程修改该成员变量后,本线程可以正确读取到此成员变量的值。
线程的状态请结合OS中进程的5态来思考。以下线程状态和方法的介绍来自DreamSea(张小哲)
Ø线程的方法(Method)、属性(PRoperty)
1)优先级(priority)
每个类都有自己的优先级,一般property用1-10的整数表示,默认优先级是5,优先级最高是10;优先级高的线程并不一定比优先级低的线程执行的机会高,只是执行的机率高;默认一个线程的优先级和创建他的线程优先级相同;
2)Thread.sleep()/sleep(long millis)
当前线程睡眠/millis的时间(millis指定睡眠时间是其最小的不执行时间,因为sleep(millis)休眠到达后,无法保证会被JVM立即调度);sleep()是一个静态方法(static method) ,所以他不会停止其他的线程也处于休眠状态;线程sleep()时不会失去拥有的对象锁。 作用:保持对象锁,让出CPU,调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留一定的时间给其他线程执行的机会;
3)Thread.yield()
让出CPU的使用权,给其他线程执行机会、让同等优先权的线程运行(但并不保证当前线程会被JVM再次调度、使该线程重新进入Running状态),如果没有同等优先权的线程,那么yield()方法将不会起作用。
4)thread.join()
使用该方法的线程会在此之间执行完毕后再往下继续执行。
5)object.wait() 当一个线程执行到wait()方法时,他就进入到一个和该对象相关的等待池(Waiting Pool)中,同时失去了对象的机锁—暂时的,wait后还要返还对象锁。当前线程必须拥有当前对象的锁,如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常,所以wait()必须在synchronized block中调用。sleep()和wait()方法的最大区别是:sleep()睡眠时,保持对象锁,仍然占有该锁;而wait()睡眠时,释放对象锁。但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException(但不建议使用该方法)。
6)object.notify()/notifyAll()
唤醒在当前对象等待池中等待的第一个线程/所有线程。notify()/notifyAll()也必须拥有相同对象锁,否则也会抛出IllegalMonitorStateException异常。
7)Synchronizing Block
Synchronized Block/方法控制对类成员变量的访问;Java中的每一个对象都有唯一的一个内置的锁,每个Synchronized Block/方法只有持有调用该方法被锁定对象的锁才可以访问,否则所属线程阻塞;机锁具有独占性、一旦被一个Thread持有,其他的Thread就不能再拥有(不能访问其他同步方法),方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。正确停止Java线程非正确停止的方法:stop()
要正确停止线程,应该使用正确的退出标记。正常情况下run方法执行完毕线程就停止了,但是有些情况下run方法中需要while循环保持轮询,所以应该为while循环设置合适的退出条件,即退出标记,保证线程正确停止。
或者使用interrupt()方法的初衷并不是要停止线程,正常情况下,调用该方法可以将中断标记设置为TRUE。但是,当某个线程因为调用了wait()或者sleep()等方法而被阻塞时,此时调用interrupt()方法并不能正确的将interrupted标记设置为TRUE,并且同时会抛出中断异常。
争用条件:当多个线程同时共享访问同一个内存数据时,每个线程都尝试操作该数据,从而导致数据被破坏。
线程的互斥与同步互斥是指在同一时刻只有一个线程可以对临界区进行操作。在JAVA中,可以通过synchronized块或者方法来实现。
在synchronized块中,需要对某个对象进行加锁,并将需要互斥的代码放入synchronized块中,从而来实现互斥行为。获得该锁的进程可以进入该synchronized块中.
而同步则是线程之间的一种通信机制,通过同步可以规定线程执行的顺序。例如,当线程不满足访问某资源时,可以通过调用锁对象的lock.wait()方法,使该线程让出CPU,让出锁,并在该锁对象的waitset中等待。满足条件时,可以通过lock.notify()方法随机唤醒一条线程,lock.notifyAll()则唤醒该waitset中所有的线程,使它们重新竞争该锁。被唤醒的线程从wait()方法处继续执行。
新闻热点
疑难解答