线程
线程是进程中的一个单一的连续控制流程,一个进程可以拥有多个线程。线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其他线程共享一个存储空间,这使得线程间的通信远比进程简单。线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小的多。线程也是动态的,具有一定的生命周期,分别经历从创建、就绪、执行、阻塞直到消亡的过程。下面是一个线程状态转换图:
创建线程(new)
当利用new关键字创建线程对象实例后,它仅仅作为一个对象实例存在,JVM没有为其分配CPU时间片等待线程运行资源。要想真正运行这个线程,就得调用start()方法,这时线程处于就绪状态。
就绪状态(runnable)
在处于新建状态的线程中调用start()方法将线程的状态转换为就绪状态。这个时候,线程已经得到了除CUP时间之外的其他系统资源,只等JVM的线程调度器按照线程的优先级对该线程进行调度,从而使该线程拥有能够获得CPU时间片的机会,一旦该线程获得CPU时间片,就进入运行状态。
运行状态(running)
线程获得CPU时间片之后就进入运行状态,这个时间片过后,CPU又要调度其他线程来运行,这个时候该线程又进入了就绪状态。运行的线程也可以调用yield()自动放弃CPU,从而回到就绪状态,以便其他线程能够运行。当运行着的线程调用sleep()、wait()或者进入synchronized代码保护区又没获得锁时,就进入了阻塞状态。
阻塞状态(blocked)
阻塞指的是暂停一个线程的执行以等待某个条件发生,若线程处于阻塞状态,调度机制不给它分配任何CPU时间,直接跳过它。比如要读入磁盘文件数据,读磁盘是非常慢的操作,CPU就可以把等待文件数据到来的时间去运行其他的线程,当前线程就进入了阻塞状态,磁盘文件数据到来时该线程就进入了就绪状态等待CPU调度。
死亡状态(dead)
当线程体运行结束或者调用线程对象的stop方法后线程将终止运行,由JVM收回线程占用的资源。
对象的互斥锁
在并发程序中,对多线程共享的资源或数据称为临界资源,而把每个线程中访问临界资源的那一段代码段称为临界代码段。通过为临界代码段的设置,就可以保证资源的完整性,从而安全地访问共享资源。为了实现这种机制,Java语言提供了以下两方面的支持:
(1)为每个对象设置了一个“互斥锁”标记。该标记保证在每一个时刻,只能有一个线程拥有该互斥锁,其他线程如果需要获得该互斥锁,必须等待当前拥有该锁的线程将其释放,该对象就成了一个互斥对象。
(2)为了配合使用对象的互斥锁,Java语言提供了保留字synchronized。如下:
synchronized(互斥对象){
临界代码段
}
当一个线程执行到该代码段时,首先检测该互斥对象的互斥锁。如果该互斥锁没有被别的线程所拥有,则该线程获得该互斥锁,并执行临界代码段,直到执行完毕并释放互斥锁;如果该互斥锁已被其他线程占用,则该线程自动进入该互斥对象的等候队列,等待其他线程释放该互斥锁。
class
ShareData
... {
int sharedata;
public ShareData(int sharedata)
...{
this.sharedata=sharedata;
}
// public synchronized void method(int sharedata)
public void method(int sharedata)
...{
synchronized(this)
...{
if(this.sharedata>=sharedata)
...{
this.sharedata=this.sharedata-sharedata;
System.out.println("success!");
}
else
... {
int sharedata;
public ShareData(int sharedata)
...{
this.sharedata=sharedata;
}
// public synchronized void method(int sharedata)
public void method(int sharedata)
...{
synchronized(this)
...{
if(this.sharedata>=sharedata)
...{
this.sharedata=this.sharedata-sharedata;
System.out.println("success!");
}
else