Java线程编程整理

来源:互联网 发布:捕鱼游戏平台源码 编辑:程序博客网 时间:2024/05/19 18:37
在什么情况下用多线程
(1)GUI应用程序
几乎所有的GUI应用程序一定有多线程。
(2)比较花费时间的I/O处理
文件、网络的I/O处理比较花费时间。可以把执行I/O处理与非执行I/O处理的线程分开,这样就可以利用进行I/O处理的时间,同时进行其他处理。
(3)多个客户端
基本上,网路上的服务器必须同时处理 1 个以上的客户端。此时可以设计一个当由客户端链接到服务器时,会自动出来迎接这个客户端的线程。
暂停线程执行操作的方法
Sleep方法
利用Thread类的sleep方法可以暂时停止线程的执行操作。Sleep方法的调用要放在try-catch里,这是因为sleep方法可能会抛出一个InterruptedException异常,InterruptedException是用作取消线程处理操作时的异常。
利用sleep设置的暂停时间并不是很精密,所以不太适合用在实时性控制方面。若用下面的语法结构,则可以ns(0.000000001)为单位设置暂停时间。Thread.sleep(ms,ns);不过一般的java系统不需要用这么精密的控制方式。事实上,控制的精密度有多高取决于java处理系统。
如果要半路唤醒被Thread.sleep的线程,则可以用interrupt方法。
wait set——线程休息室
所有的实例都有一个wait set,wait set是一个在执行该实例的wait方法时、操作停止的线程的集合。它有点类似线程的休息室,而且每个实例都会有。
执行wait方法时,线程会暂时停止操作,进入wait set这个休息室,除非发生下列其中一种情况,否则线程一直停留在wait set这个休息室里。当发生下列任何一种情况时,线程便会退出wait set。
有其他线程以notify方法唤醒该线程。
有其他线程以notifyAll方法唤醒该线程。
有其他线程以interrupt方法唤醒该线程。
wait方法已经到期。
wait方法——把线程放入wait set
使用wait方法时,线程进入wait set。假设现在已经执行一个如下的语句:
obj.wait();
则目前的线程会暂时停止执行,进入obj的wait set。这个操作称为:线程在obj上wait。
如果实例方法里有如下语句:
wait();
则其意义同:
this.wait();
故执行wait的线程会进入this的wait set。此时就变成了线程在this上等待。
如欲执行wait方法,线程需获取锁定(这是规则)。但当线程进入wait set时已经释放了锁定。
Yuarded wait
意义是“被阻挡而等待”。大致上是线程用wait方法等待,等到被notify或notifyAll后再次测试条件的实现方法。使用wait等待的时间,其实是停止在等待区里停止执行,所以不会浪费到java执行环境的处理时间。
等待的范例:
while(!ready)
{
 wait();
}
唤醒的范例:
ready=true;
notifyAll();
busy wait
“忙碌地等待”的意思。线程不用wait等待,而使用yield等待(尽可能把优先级交给别的线程),并不断检测条件的实现方法。因为等待中的线程也持续地运行着,所以会浪费java虚拟机的时间。yield是Thread类的类方法。
等待端的范例:
while(!ready)
{
yield();
}
唤醒端的范例:
Ready=true;
Thread. wait与锁定
当某个线程要执行某个实例的wait方法时,该线程必须获取这个实例的锁定。假设线程执行this的wait方法,当线程执行this的wait方法后,会进入this的等待区,这时线程会解除this的锁定。
而线程可能会因为notify或notifyAll或interrupt方法退出等待区。不过在实际执行下一条语句之前,必须先获取this的锁定才行。
wait()/notify()
通常,多线程之间需要协调工作。例如,浏览器的一个显示图片的线程displayThread想要执行显示图片的任务,必须等待下载线程downloadThread将该图片下载完毕。如果图片还没有下载完,displayThread可以暂停,当downloadThread完成了任务后,再通知displayThread“图片准备完毕,可以显示了”,这时,displayThread继续执行。
  以上逻辑简单的说就是:如果条件不满足,则等待。当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。例如:
  synchronized(obj) 
{
  while(!condition)
{
  obj.wait();
  }
  obj.doSomething();
 }
当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。
  在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A:
  synchronized(obj) 
{
 condition = true;
 obj.notify();
}
需要注意的概念是:
●调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) {...} 代码段内。
 ●调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj) {...} 代码段内唤醒A。
 ●当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。
 ●如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。
●obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。
●当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。
wait()/sleep()的区别
前面讲了wait/notify机制,Thread还有一个sleep()静态方法,它也能使线程暂停一段时间。sleep与wait的不同点是:sleep并不释放锁,因此sleep期间别的线程根本没有办法获得对象锁;并且sleep的暂停和wait暂停是不一样的。obj.wait会使线程进入obj对象的等待集合中并等待唤醒。
但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在wait/sleep/join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。
需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptedException。
wait与锁定
当某个线程要执行某个实例的wait方法时,该线程必须获取这个实例的锁定。假设线程执行this的wait方法,当线程执行this的wait方法后,会进入this的等待区,这时线程会解除this的锁定。
而线程可能会因为notify或notifyAll或interrupt方法退出等待区。不过在实际执行下一条语句之前,必须先获取this的锁定才行。
wait()/notify()
通常,多线程之间需要协调工作。例如,浏览器的一个显示图片的线程displayThread想要执行显示图片的任务,必须等待下载线程downloadThread将该图片下载完毕。如果图片还没有下载完,displayThread可以暂停,当downloadThread完成了任务后,再通知displayThread“图片准备完毕,可以显示了”,这时,displayThread继续执行。
  以上逻辑简单的说就是:如果条件不满足,则等待。当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。例如:
  synchronized(obj) 
{
  while(!condition)
{
  obj.wait();
  }
  obj.doSomething();
 }
当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。
  在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A:
  synchronized(obj) 
{
 condition = true;
 obj.notify();
}
需要注意的概念是:
●调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) {...} 代码段内。
 ●调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁,也就无法在synchronized(obj) {...} 代码段内唤醒A。
 ●当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。
 ●如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中的一个(具体哪一个由JVM决定)。
●obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行,例如A1,其余的需要等待A1释放obj锁之后才能继续执行。
●当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后,A1,A2,A3中的一个才有机会获得锁继续执行。
wait()/sleep()的区别
前面讲了wait/notify机制,Thread还有一个sleep()静态方法,它也能使线程暂停一段时间。sleep与wait的不同点是:sleep并不释放锁,因此sleep期间别的线程根本没有办法获得对象锁;并且sleep的暂停和wait暂停是不一样的。obj.wait会使线程进入obj对象的等待集合中并等待唤醒。
但是wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在wait/sleep/join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。
需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptedException。
Spin lock
“旋转而锁定”的意思。表现出条件成立前while语句不断“旋转”的样子。
Polling
“进行舆论调查”的意思。反复检测某个事件是否发生,如果发生,如果发生就进行对应的处理。
notify方法——从wait set中拿出线程
使用notify(通知)方法,可以从wait set里抓取一个线程。假设现在已经执行一个如下的语句:
obj.notify();
则从obj的wait set里的线程中抓取一个,唤醒这个线程。被唤醒的线程便退出wait set。
线程必须有欲调用的实例的锁定,才能执行notify方法,这一点跟wait方法一样(也是规则)。
被notify唤醒的线程并不是在notify的一瞬间重新开始执行。因为在notify的那一刻,执行notify的线程还握着锁定不放,所以其他线程无法获取该实例的锁定。
假设在执行notify方法时,wait set里并非只有一个线程。规格并没有注明此时该选那个线程,选择方式以java系统而异。
notifyAll——从wait set里拿出所有的线程
使用notifyAll(通知全体)方法时,会将在wait set里苦等的线程全部拿出来。假设现在的情况是:
obj.notifyAll();
则会唤醒所有停留在obj的wait set里的线程。
若实例方法写成:
notifyAll();
其意义同:
this.notifyAll();
故这个语句所在的方法的实例的wait set里的线程都会被放出来。
跟wait方法和notify方法一样,线程必须获取调用实例的锁定,才能调用notifyAll方法。
notify方法和notifyAll方法非常相似,应该选用那一个呢?选用notify的话,唤醒的线程比较少,程序处理的速度当然比notifyAll要略胜一筹。选用notify时,若部分处理不好,可能会有程序挂掉的危险性。一般来说,选择notifyAll写出来的程序代码要比选择notify可靠。
线程同步方法
synchronized方法
要用synchronized关键字时,就要思考:
这个synchronized是要保护什么的?
无论是synchronized方法还是synchronized块,synchronized势必保护着“某个共享的资源”,使该资源不会被多个线程同时访问。
确认保护什么后,就要思考其他地方也妥善保护到了吗?
若这些资源在其他许多地方还使用着,这里使用synchronized小心保护着,但其他地方并没有做好保护措施。那其实这个资源还是没被保护的。就好像把大门和后门都锁的好好的,但如果窗户是开着的,还是没有意义一样。
当一个方法加上关键字synchronized后,当前实例的该synchronized方法在同一时间只能让一个线程执行。这种线程称为synchronized方法,也称为同步方法。
该以什么单位来保护呢?
以不让多个线程穿插操作为标准。
获取谁的锁定来保护呢?
要调用synchronized实例方法的线程,一定会获取this的锁定,一个实例的锁定在同一时间只能有一个线程得到。
如果实例不同,那么锁定也不同了。虽然我们用synchronized来保护,但如果有多个相异实例,那么多线程仍然可以分别执行不同实例的synchronized方法。
使用synchronized块时,特别要考虑“获取谁的锁定来保护呢”这种情况。因为synchronized块需要明确表明获取哪个对象的锁定。例如:
Synchronized(obj)
{
 …… ……
}
这样的程序代码中,obj就是我们获取锁定的对象。请小心不要把这个对象写错,获取错误对象的锁定,就好像要保护自己的家,却反而去锁邻居的门一样。
synchronized阻挡
如果只想启动方法里一部分线程,而非启动整个方法时,可以用synchronized阻挡,其格式如下:
Synchronized(表达式)
{
…… ……
}
则可以把获取锁定的实例传给“表达式”,如欲更精密的控制共相互斥范围可以用synchronized阻挡。
synchronized实例方法和synchronized阻挡
假设有一个类型如下的synchronized实例方法:
public synchronized void Method()
{
 …… ……
}
在功能上,与下面这个以synchronized阻挡为主的方法有异曲同工之妙:
public void Method()
{
 Synchronized(this)
{
…… ……
}
}
也就是说,synchronized实例方法是使用this锁定去做线程互斥的。

Synchronized类方法和synchronized阻挡
假设有一个类型如下的synchronized类方法,synchronized类方法有限制同时只能有一个线程执行的功能:
static synchronized void Method()
{
 …… ……
}
在功能上,与下面这个以synchronized阻挡为主的方法有异曲同工之妙:
public class Something
{
public void Method()
{
 Synchronized(Something.class)
{
…… ……
}
}
}
也就是说,synchronized类方法是使用该类的类对象的锁定去做线程互斥的。Something.class是对应于Something类的java.lang.Class类的实例。


原创粉丝点击