奇点临近

非淡泊无以明志, 非宁静无以致远

Java学习疑点(4)--线程的六个状态以及其安全性问题的个例解析

Thread States

Threads can be in one of six states:

  •  New
  •  Runnable
  •  Blocked
  •  Waiting
  •  Timed waiting
  •  Terminated

Each of these states is explained in the sections that follow.

To determine the current states of a threads, simply call the getState method.

 

New Threads

When you create a thread with the new operator--the thread is not yet running.This means that it is in thenewstate.When a thread is in the new state, the programe has not started executing code inside of it.A certain amount of bookkeeping needs to be done before a thread can run.

 

Runnable Threads

Once you invoke the start method, the thread is in the runnable state.A runnable thread may or may not actually be running.It is up to the operating system to give the thread time to run.(The Java specification doen not call this a separate state, though. A running thread is still in the runnable state.)

Once a thread is running,  it doesn't necessarily keep running.In fact, it is desirable if running threads occasionally pause so that other threads have a chance to run. The details of thread scheduling systems give each runnable thread a slice of time to perform its task.When that slice of time is  exhausted, the operating system preempts the thread and gives another thread an opportunity to work.

All modern desktop and server operating systems use preemptive scheduling. However, small devices such as cell phones may use cooperating scheduling. In such a device, a thread loses control only when it calls the yield method,  or it is blocked or waiting.

On a machine with multiple processors, each  processor can run a thread, and you can hava multiple threads run in parallel. Of course, if there are more threads than processors, the scheduler still has to do time-slicing.

Always keep in mind that a runnable thread may or may ont be running at any given time.(This is why the states is called "runnable" and not "running.")

 

Blocked and Waiting Threads

When a thread is blocked ot waiting, it is temporarily inactive. It doesn't execute any code and it consumes mininal resources. It is up to the thread scheduler to reactivate it.

The details depend on  how the inactive state was reached.

  • When the thread tries to acquire an intrinsic object lock(buy not a Lock in the java.util.concurrent library) that is currently held by another thread, it becomes blocked. The thread becomes unblocked when all other threads have relinquished the lock and the thread scheduler has allowed this thread to hold it.
  • When the thread waits for another thread to notify the scheduler of a condition, it enters the waiting states. This happens by calling the object.wait or Thread.join method, or by waiting for a Lock or Condition in the java.util.concurrent Library. In practice, the differece between the blocked and waiting state is not significant.
  • Several methods have a timeout parameter. Calling them causes the thread to enter the timed waiting state. This state persists either until the timeout expired or the appropriate notification has been received. Methods with timeout include Thread.sleep and the timed versions of Object.wait, Thread.join, Lock.tryLock, and Condition.await.

Terminated Threads

A thread is terminated for one of two reasons:

  • It dies a natural death because the run method exits normally.
  • It dies abruptly because an uncaught exception terminates the run method.

In particular, you can kill a thread by invoking its stop method. That method throws a ThreadDeath error object that kills the thread. However, the stop method is deprecated, and you should never call is in yout own code.

以上内容摘自<Java核心技术卷I: 基础知识>(第8版)

 

       一个线程在启动之后, 未必一直处于运行状态, 它什么时候处于运行状态取决于操作系统, 在某一时刻, 它可能处于非运行状态. 在程序逻辑中对此要特别小心, 防止因此产生错误. 例如, 如果你期望你的线程检测到某个具体时间点执行一个操作, 那么你的这个期望就有可能出现问题了, 因为也许在这个时间点到来的这个时刻, 刚好这个线程处于非运行状态, 此时你就无法捕捉这个时间点, 最终错过了执行操作的时机.

           另外, 不要把这点与线程中调用sleep相混淆, 在线程中调用sleep之后, 线程就进入另一种状态, 而不是runnable状态. 我们在此处所说的是线程处于runnable状态时有可能处于非运行状态. 提到sleep方法, 还有一点要注意, 线程执行sleep(睡眠)到期自动苏醒并返回runnable状态后, 未必能够立即得到运行的机会. 因此, sleep方法中指定的时间是线程不会运行的最短时间, sleep方法不能保证该线程睡眠到期后就立即开始执行.

 

下面引用一个简单典型的例子说明多线程可能存在的安全问题:

public void run() {
	while(true) {
		if(sum == 0) {
			sum++;
		}
                ......
	}
        ......
}
sum为多线程共享的变量


 

当上面这段代码由多个线程共同执行时, 当sum为0时, 线程1执行了if判断但是还未执行sum++时, 操作系统令其进入非运行状态而由线程2进入运行状态, 这时sum任然为0, 线程1和2就执行了sum++两次, 从而导致安全问题产生.

怎样避免这种问题呢?方法很多, 这里我们用同步代码块来做:

public void run() {
	while(true) {
		synchronized(this) {
			if(sum == 0) {
				sum++;
			}
		}
		......
	}
	......
}

我们将if代码块使用同步, 如此当线程1开始执行代码块中的内容但还未结束时, 其他线程就无法进入这片代码块中, 也就是保证了synchronized这块内容只允许一个线程执行, 当该线程没有执行完时, 其他线程是无法获得执行资格的. 从而保证了上述安全问题不会发生.

在使用多线程时, 一定要注意操作线程之间共享内容时可能会引发的安全问题.

 

阅读更多
个人分类: Java 疑惑
上一篇Java学习疑点(3)--解析二分法查找的原理以及其优缺点
下一篇Java学习疑点(5)--使用多线程的时候如何注意避免死锁发生?
想对作者说点什么? 我来说一句

java线程状态转换图

2011年07月04日 69KB 下载

没有更多推荐了,返回首页

关闭
关闭