`
sunmiracle
  • 浏览: 14823 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

SCJP笔记_章九_线程

    博客分类:
  • SCJP
阅读更多
第九章 线程

9.1.1 定义线程

扩展java.lang.Thread类,重写run()方法。
Java代码
class MyThread extends Thread{ 
public void run(){ 
System.out.println("Important job running in MyThread"); 
}  

 
实现java.lang.Runnable,实现run()方法。
Java代码
class MyRunnable implements Runnable{ 
public void run(){ 
System.out.println("Important job running in MyRunnable"); 
}  

Thread类希望run()方法不带任何变元,并且它将在单独的调用栈内为你执行这个方法。如果有run(String s)方法,Thread类不会为你调用该方法,而且即使你自己直接调用了他,执行也不会用一个不同的调用栈在一个新的执行线程中发生。他只会发生在与你发出调用的代码相同的调用栈中,就像任何其他不同的方法调用一样。

9.1.2 实例化线程
每个执行线程都是作为Thread类的一个实例开始的。
对于扩展Thread类而定义的线程实例化方式如下:
MyThread t = new MyThread();
对于实现Runnable接口而定义的线程实例化方式如下:
Java代码
MyRunnable r = new MyRunnable();  
Thread t = new Thread(r); 

Thread类本身也实现了Runnable(他又一个我们正在重写的run()方法)。因此能够将一个Thread传递给另一个Thread的构造函数
Thread t=new Thread(new MyThread());
 

Thread类的构造函数:
Thread()
Thread(Runnable target)
Thread(Runnable target,String name)
Thread(String name)
当线程已经被实例化但还没有启动时,该线程处于new state。此时该线程不能认为是alive的,一旦调用了start()方法,线程就被认为是alive的(即使run()方法可能还没有实际开始执行)在run()方法结束时,线程被认为是死的。isAlive()方法是判断县城是否已经启动但还没有完成run()方法的最佳方式。

9.1.3 启动线程
t.start();
在调用start()方法后,发生:
启动新的执行线程(具有新的调用栈)。
线程从新状态转到可运行状态(runnable state)。
当线程获得执行机会时,会运行它的目标run()方法。


如果想如下所示:
Thread t=new Thread();
t.run();//合法,但不会启动新的执行线程。而只是表示从线程当前正在执行的类调用run()方法。

Thread.currentThread()用来获取当前线程引用。
Thread.getName()用来获得线程的名字。

启动并运行多个线程:
每个线程都会启动,而且每个线程都将运行到结束。但是顺序,优先没有绝对保证。
当线程的目标run()方法结束时,该线程就完成了。死线程不能再次调用start()方法。//但是它仍然是一个Thread对象,如果具有对Thread实例的引用,则即使该Thread实例不再是一个执行线程,仍然能够调用这个Thread实例上的方法,就像任何其他Java对象一样
只要线程已经启动过,它就永远不能再次启动。//两次启动会导致IllegalThreadStateException这种RuntimeException异常。

线程调度器:
可运行线程变成运行中线程的顺序是没有保证的。
java.lang.Thread类中控制(影响)线程的方法
Java代码
public static void sleep(long millis) throws InterruptedException public static void yield()  
public final void join() throws InterruptedException  
public final void setPriority(int newPriority)  
//默认是5,值越大,优先越高 1-10  

java.lang.Object类中控制(影响)线程的方法
Java代码
public final void wait() throws InterruptedException  
public final void notify()  //唤醒单个线程  
public final void notifyAll()   //唤醒所有线程 


9.2.1 线程状态

新状态--new
可运行状态--runable
运行中状态--running
等待/阻塞/睡眠状态--waiting/blocked/sleeping
死状态--dead

一些方法看起来像是在通知另一个线程阻塞,但其实不是。如果你有对另一个线程的引用,则可以编写像下面的代码:t.sleep()或者t.yield()。但这些是Thread的静态方法-他们不会影响实例t,这样定义,他们是为了影响当前正在执行的线程。


9.2.2 阻止线程执行
一个线程被踢出“运行中”状态,而不是被送回“可运行”或“死”状态。
即:睡眠,等待,因为需要对象的锁而被阻塞。

9.2.3 睡眠
Thread的两个静态方法:
public static void sleep(long millis) throws InterruptedException                      //millis是毫秒
public static void sleep(long millis,int nanos) throws InterruptedException      //nanos是纳秒
当线程的sleep()到期而醒来,并不意味着它将返回到运行中状态。当线程醒来时,它只会返回到可运行状态。当执行代码遇到sleep()调用时,他会让当前正在运行的线程睡眠。不要误以为一个线程能够使另一个线程睡眠。

9.2.4 线程优先级和yield()

调度器在优先级上是没有保证的,主要是看它“喜欢。。还是不喜欢”,真够无奈的。

设置线程的优先级
t.setPriority(int i);

yield()方法
Thread的静态方法yield(),让当前的“运行中”线程回到“可运行”状态,让步给具有相同优先级的其他“可运行”线程,但这是没有任何保证的

join()方法
Thread的非静态方法join()让当前线程加入到引用的线程尾部,这意味着调用方法的线程完成(死状态)之前,主线程不会变为可运行的。
保证当前线程停止运行,直到它所加入的线程完成为止。然而,如果它加入的线程不是活得,则当前线程不需要停止。
public final void join() //要一直等待该线程结束 throws InterruptedException
public final void join(long millis) //millis为0表示要一直等下去 throws InterruptedException//如果等待时间超过5秒,则无论如何停止等待,变成可运行状态。
public final void join(long millis, int nanos) throws InterruptedException

9.3 同步代码

9.3.1 同步和锁 synchronized
只能同步方法(或代码块),不能同步变量或类
每个对象只有一个锁。换句话说同一时间只有一个线程可以使用该对象的同步方法或代码块。
不必同步类中的全部方法。类可以同时具有同步方法和非同步方法。
一旦一个线程获得了对象的锁,就没有任何其他线程可以进入(该对象的)类中的任何同步方法。
如果类同时具有同步和非同步方法,则多个线程仍然能够访问该类的非同步方法
如果线程进入睡眠,则它会保持已有的任何锁,不会释放他们。
线程可以获得多个锁,即线程可以同时获得多个对象的锁。//如果线程获得了锁,接着又试图在同一对象上调用同步方法时,JVM知道这个方法已经具有该对象的锁,因此使用这个线程已经拥有的锁,线程可以随意调用同一对象上的其他同步方法。
静态方法能否同步
可以,静态代码块也可以。例如:

Java代码
public static synchronized int getCount(){ 
return count;  
}  
public static int getCount(){ 
  //注意synchronized关键字后面括号里的内容:即要锁的类或对象 
synchronized(MyClass.class){ 
   return count;  

}  
 


如果线程不能获得锁会怎么样?
会阻塞,等锁释放。实质上,就是线程进入该对象的一个池内,必须在那里等待,直到锁释放,该线程才变成可运行状态。锁被释放不意味着任何特定的线程会得到它。

调用同一个类中非静态同步方法的线程将彼此阻塞,如果他们使用同一个实例进行调用的话。如果使用两个不同的实例调用它们,则他们会获得两个锁,且这两个锁互不干预。
调用同一个类中静态同步方法的线程将始终彼此阻塞-他们都锁定在同一个Class实例上
静态同步方法和非静态同步方法永远不会彼此阻塞。静态方法锁定在Class实例上,而非静态方法锁定在this实例上
对于同步块,在同一个对象上同步的线程将彼此阻塞,在不同的对象上同步的线程将不会彼此阻塞。



何时需要同步?
书云:对于复杂的情况“如果你不想它的话,你的生命就会更长、更愉快。真的如此,我们没有撒谎。”

线程安全类
StringBuffer等,但是不能完全依靠它来提供所需要的线程保护。

9.3.2 线程死锁
当代码等待从对象删除锁而迫使线程执行暂停时,死锁就发生了。
当被锁的对象试图访问另一个被锁的对象,而该对象又要访问第一个被锁的对象时,就会发生死锁现象。换句话说,两个线程都在等待对方释放锁,因此,这些锁永远不会释放!
死锁是一种糟糕的设计,不应当让它发生。

9.4 线程交互

必须在同步方法内调用wait()、notify()和notifyAll()方法!线程不能调用对象上的等待或通知方法,除非它拥有该对象的锁。
当调用wait()方法时,线程会立即放弃她在该对象上的锁。当调用notify()时,如果线程仍然在完成同步代码,则线程在移出同步代码前不会放弃锁
notifyAll()会通知在特定的锁定对象上等待的所有线程,而不是任何对象上等待的所有线程。
没有将对象同步前调用wait()方法,会引起IllegalMonitorStateException

9.4.0 方法和锁的状态
放弃锁   保持锁                                              定义方法的类
wait   notify(线程在调用该方法后不久可能退出同步代码,推出后就会放弃锁)Object
       join,sleep,yield                                     Thread
9.4.1 当多个线程等待时使用notifyAll()

在循环中使用wait()

Object类:wait(),notify(),notifyAll()
Thread类:静态sleep(),yield() 非静态join(),start()
Runnable接口:run()
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics