您的位置:首页 > 理论基础 > 计算机网络

读书笔记-java网络编程-3线程-线程调度

2016-07-03 20:06 381 查看

4. 线程调度

4.1线程调度

java中10是最高优先级,0是最低优先级,默认的优先级5。

这与Unix优先级相反。

Windows只有7个优先级。

尽量不要设置过高的优先级否则会导致低优先级饥饿。

public final void setPriority(int newPriority)


使用这条语句来指定设置优先级。

4.2 抢占

主要有两种线程调度机制:

抢占式(preemptive):调度器确定正常地轮到某cpu时间时,会暂停这个线程,将cpu控制权交给另外的线程。

协作式(coorperative):等待线程自己暂停。

所有java虚拟机都保证在不同优先级之间使用抢占式的线程调度,在同样优先级之间偶尔会暂停其中一个线程,让下一个线程得到一些cpu时间。

在jvm虚拟机的机制下很难发现的,此外需要注意的是如果客户端的程序可能是协作式的,因此也有可能陷入饥饿状态。

通常来说一个线程有10中方式可以暂停或者指示它准备暂停:

1. 可以对IO阻塞

2. 可以对同步对象阻塞

3. 可以放弃

4. 可以休眠

5. 可以连接另一个对象

6. 可以等待另一个对象

7. 可以结束

8. 可以被更高优先级线程抢占

9. 可以被挂起

10. 可以被停止

最后两种方法以及不使用了,因为它会使得对象处于不一致的状况。

4.3 线程切换方式种类

4.3.1阻塞

当网络程序中的线程自动放弃CPU控制权,最常见的方式是对IO阻塞。同样进入一个同步方法

对于IO阻塞一般不会造成严重后果,要不获得所继续进行,要么抛出IOException异常。

或者代码块的时候也会阻塞。如果没有这个同步对象的锁,则会陷入阻塞。

有可能造成死锁的严重后果。

4.3.2 放弃

线程可以通过调用Thread.yield()静态方法来做到。将通知虚拟机,如果有另一个线程准备运行,可以运行该线程。但是有些虚拟机会忽视这个提示。

在放弃之前应该确保它与它关联的Runnable对象处于一致状态,因此在理想情况下,在线程放弃时不做任何同步。

public void run(){
while(true){
//
Thread.yield();
}
}


这使得其他有相同优先级的线程有机会运行

4.3.3 休眠

休眠与放弃类似,区别是不管有没有其他准备运行的线程,休眠的线程都会暂停。这样低优先级的线程会获得运行的机会。

缺点和线程一样,要避免在同步方法或块内让线程同步。

public static void sleep(long milliseconds) throws InterruptedException
public static void sleep(long milliseconds, int nanoseconds) throws InterruptedException


需要说明的是,这里的的秒速是不保证时间准确的,在实际编程的时候需要注意精度。

public void run(){
while(true){
if(!getPage("http://www.iboiblio.org")){
mailError("webmaster@ibiblio.org");
}
try{
Tread.sleep(300000);
}catch(InterruptedException ex){
break;
}
}
}


有时候在时间到后没有办法立刻唤醒,因为JVM正在忙于其他的事情。同样也可能时间还没有到就中断了。通过

public void interrupt()


来完成。这就是线程和Thread对象之间的区别。线程在休眠,并不意外着其他醒着的线程不能处理这个线程相应的Thread对象。具体的说,另一个线程可以调用休眠Thread对象的interrupt()方法,会让休眠中的线程得到一个InterruptedExcetpion异常。并处理这个异常。

如果一个线程对IO操作阻塞,中断这个线程的效果很大程度上依赖于具体的平台。通常这将是一个什么都不做的操作。也就是说线程继续阻塞。如果你需要可中断的IO,应该认真考虑非阻塞的IO,而非流。

4.3.4 链接线程

java提供了join()方法,允许一个线程在运行之前等待其他线程结束。

public final void join() throws InterruptedException
public final void join(long milliseconds) throws InterruptedException
public final void join(long milliseconds, int nanoseconds) throws InterruptedException


这里把书上的例子 稍加改动变成了可以执行的版本如下:

package thread;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class ReturnDigest extends Thread {

private String filename;
private JoinDigestUserInterface callback;

public ReturnDigest(String filename,JoinDigestUserInterface callback) {
this.filename = filename;
this.callback = callback;
}

public void run() {
FileInputStream in;
try {
in = new FileInputStream(filename);
MessageDigest sha = MessageDigest.getInstance("SHA-256");
DigestInputStream din = new DigestInputStream(in, sha);
while (din.read()!=-1) ;
din.close();
callback.setDigest(sha.digest());
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


package thread;

import javax.xml.bind.DatatypeConverter;
import org.junit.Test;

public class JoinDigestUserInterface {

private byte[] digest;

public void setDigest(byte[] digest) {
this.digest = digest;
}

@Test
public void test(){
String[] list = new String[3];
list[0] = "E:\\tomcat\\webapps\\network\\bin\\thread\\test1.txt";
list[1] = "E:\\tomcat\\webapps\\network\\bin\\thread\\test2.txt";
list[2] = "E:\\tomcat\\webapps\\network\\bin\\thread\\test3.txt";
ReturnDigest[] digestThreads = new ReturnDigest[list.length];
for(int i = 0 ; i < 3 ; i++){
digestThreads[i] = new ReturnDigest(list[i],this);
digestThreads[i].start();
}
for(int i = 0 ; i < 3 ; i++){
try{
digestThreads[i].join();
//
StringBuffer result = new StringBuffer(list[i]);
result.append(":");
result.append(DatatypeConverter.printHexBinary(digest));
System.out.println(result);
}catch(InterruptedException ex){
System.out.println("Thread Interrupt before completion");
}
}
}

}


4.3.5 等待一个对象

线程可以等待一个它锁定的对象,在等待时,它会释放这个对象的锁并暂停,知道它得到其他线程的通知。

另一个线程以某种方式修改这个对象,通知等待对象的线程然后继续执行。

与连接不同的是,这里等待的是资源达到某种状态而不是等待线程结束。

public final void wait() throws InterruptedException
public final void wait(long milliseconds) throws InterruptedException
public final void wait(long milliseconds, int nanoseconds) throws InterruptedException


需要注意的是以上的方法是属于object类的。因此任何对象都可以调用上述的方法

调用时,它所属的线程会释放所等待对象的锁,但不会释放它所拥有的其他资源的锁。并进入休眠,线程也会休眠。直到发生下列3中情况之一:

- 时间到期

- 线程被中断

- 对象得到通知

超时时间(timeout)与sleep()和join()方法中的超时时间相同,即线程经过指定的一段时间后会唤醒。当时间到期,线程会从wait后开始执行,不过如果线程不能立即获得所等待对象的锁,它可能会继续阻塞

中断(Interruption)其他线程调用这个线程的interrupt方法。导致一个InterruptedException异常,并在捕获这个异常的catch内继续运行。不过在抛出异常钱线程要重新获得所等待对象的锁,因此调用interrupt方法后该线程可能仍然要阻塞一段时间。

通知(notification),在其他线程在这个线程所等待的对象上调用notify或者notifyall方法就会发生通知。这两个方法都在object中

public final void notify()
public final void notifyall()


再通知一个对象之前,线程必须首先使用同步方法或块获得了对象的锁。notify()基本上随机地从等待这个对象的线程列表上选择一个线程,并将其唤醒。notifyall将唤醒所有线程

一旦等待线程得到通知,它就试图重新获得所等待对象的锁,如果成功就继续从wait后执行,如果失败就阻塞这个对象,直到获得锁。

这里少段程序

当多个线程希望等待同一个对象时,等待和通知会更为常见。

如果有多个线程在等待这个对象,首选notifyall,因为没有办法选择要通知哪个线程。

一般要将wait调用放在检测当前对象状态的循环中,不要假定因为线程得到了通知,对象现在就处于正确的状态。

这里少段程序

4.3.5 结束

当run方法返回时,线程将撤销,其他线程可以接管CPU。

在网络应用程序中,包装一个阻塞操作的线程往往会这样做,例如从服务器下载一个文件,这样应用程序的其他部分就不会被阻塞了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息