实战篇(二)
本节继续上一节的讨论.
[一个线程在进入对象的休息室(调用该对象的wait()方法)后会释放对该对象的锁],基于这个原因.
在同步中,除非必要,否则你不应用使用Thread.sleep(long l)方法,因为sleep方法并不释放对象的锁.
这是一个极其恶劣的品德,你自己什么事也不干,进入sleep状态,却抓住竞争对象的监视锁不让其它需
要该对象监视锁的线程运行,简单说是极端自私的一种行为.但我看到过很多程序员仍然有在同步方法
中调用sleep的代码.
看下面的例子:
package debug;
class SleepTest{
public synchronized void wantSleep(){
try{
Thread.sleep(1000*60);
}catch(Exception e){}
System.out.println("111");
}
public synchronized void say(){
System.out.println("123");
}
}
class T1 extends Thread{
SleepTest st;
public T1(SleepTest st){
this.st = st;
}
public void run(){
st.wantSleep();
}
}
class T2 extends Thread{
SleepTest st;
public T2(SleepTest st){
this.st = st;
}
public void run(){
st.say();
}
}
public class Test {
public static void main(String[] args) throws Exception{
SleepTest st = new SleepTest();
new T1(st).start();
new T2(st).start();
}
}
我们看到,线程T1的实例运行后,当前线程抓住了st实例的锁,然后进入了sleep.直到它睡满60秒后
才运行到System.out.println("111");然后run方法运行完成释放了对st的监视锁,线程T2的实例才
得到运行的机会.
而如果我们把wantSleep方法改成:
public synchronized void wantSleep(){
try{
//Thread.sleep(1000*60);
this.wait(1000*60);
}catch(Exception e){}
System.out.println("111");
}
我们看到,T2的实例所在的线程立即就得到了运行机会,首先打印了123,而T1的实例所在的线程仍然
等待,直到等待60秒后运行到System.out.println("111");方法.
所以,调用wait(long l)方法不仅达到了阻塞当前线程规定时间内不运行,而且让其它有竞争需求的线程
有了运行机会,这种利人不损己的方法,何乐而不为?这也是一个有良心的程序员应该遵循的原则.
当一个线程调用wait(long l)方法后,线程如果继续运行,你无法知道它是等待时间完成了还是在wait时被其
它线程唤醒了,如果你非常在意它一定要等待足够的时间才执行某任务,而不希望是中途被唤醒,这里有
一个不是非常准确的方法:
long l = System.System.currentTimeMillis();
wait(1000);//准备让当前线程等待1秒
while((System.System.currentTimeMillis() - l) <1000)//执行到这里说明它还没有等待到1秒
//是让其它线程给闹醒了
wait(1000-(System.System.currentTimeMillis()-l));//继续等待余下的时间.
这种方法不是很准确,但基本上能达到目的.
所以在同步方法中,除非你明确知道自己在干什么,非要这么做的话,你没有理由使用sleep,wait方法足够
达到你想要的目的.而如果你是一个很保守的人,看到上面这段话后,你对sleep方法深恶痛绝,坚决不用sleep
了,那么在非同步的方法中(没有和其它线程竞争的对象),你想让当前线程阻塞一定时间后再运行,应该如何
做呢?(这完全是一种卖弄,在非同步的方法中你就应该合理地应用sleep嘛,但如果你坚决不用sleep,那就这
样来做吧)
public static mySleep(long l){
Object o = new Object();
synchronized(o){
try{
o.wait(l);
}catch(Exception e){}
}
}
放心吧,没有人能在这个方法外调用o.notify[All],所以o.wait(l)会一直等到设定的时间才会运行完成.
[虚拟锁的使用]
虚拟锁简单说就是不要调用synchronized方法(它等同于synchronized(this))和不要调用
synchronized(this),这样所有调用在这个实例上的所有同步方法的线程只能有一个线程可以运行.也就是说
如果一个类有两个同步方法 m1,m2,那么不仅是两个以上线调用m1方法的线程只有一个能运行,就是两个分别
调用m1,m2的线程也只有一个能运行.当然非同步方法不存在任何竞争,在一个线程获取该对象的监视锁后这个
对象的非同步方法可以被任何线程调用.
而大多数时候,我们可能会出现这种情况,多个线程调用m1时需要保护一种资源,而多个线程调用M2时要保护的
是另一种资源,如果我们把m1,m2都设成同步方法.两个分别调用这两个方法的线程其实并不产生冲突,但它们都
要获取这个实例的锁(同步方法是同步this)而产生了不必要竞争.
所以这里应该采用虚拟锁.
即将m1和m2方法中各自保护的对象作为属性a1,a2传进来,然后将同步方法改为方法同的同步块分别以a1,a2为
参数,这样到少是不同线程调用这两个不同方法时不会产生竞争,当然如果m1,m2方法都操作同一受保护对象则
两个方法还是应该作为同步方法.这也是应该将方法同步还是采用同步块的理由之一.
package debug;
class SleepTest{
public synchronized void m1(){
System.out.println("111");
try{
Thread.sleep(10000);
}catch(Exception e){}
}
public synchronized void m2(){
System.out.println("123");
}
}
class T1 extends Thread{
SleepTest st;
public T1(SleepTest st){
this.st = st;
}
public void run(){
st.m1();
}
}
class T2 extends Thread{
SleepTest st;
public T2(SleepTest st){
this.st = st;
}
public void run(){
st.m2();
}
}
public class Test {
public static void main(String[] args) throws Exception{
SleepTest st = new SleepTest();
new T1(st).start();
new T2(st).start();
}
}
这个例子可以看到两个线程分别调用st实例的m1和m2方法却因为都要获取st的监视锁而产生了竞争.
T2实例要在T1运行完成后才能运行(间隔了10秒)
而假设m1方法要操作操作一个文件 f1,m2方法要操作一个文件f2,当然我们可以在方法中分别同步f1,
f2,但现在还不知道f2,f2是否存在,如果不存在我们就同步了一个null对象,那么我们可以使用虚拟
锁:
package debug;
class SleepTest{
String vLock1 = "vLock1";
String vLock2 = "vLock2";
public void m1(){
synchronized(vLock1){
System.out.println("111");
try {
Thread.sleep(10000);
}
catch (Exception e) {}
//操作f1
}
}
public void m2(){
synchronized(vLock2){
System.out.println("123");
//操作f2
}
}
}
class T1 extends Thread{
SleepTest st;
public T1(SleepTest st){
this.st = st;
}
public void run(){
st.m1();
}
}
class T2 extends Thread{
SleepTest st;
public T2(SleepTest st){
this.st = st;
}
public void run(){
st.m2();
}
}
public class Test {
public static void main(String[] args) throws Exception{
SleepTest st = new SleepTest();
new T1(st).start();
new T2(st).start();
}
}
我们看到两个分别调用m1和m2的线程由于它们获取不同对象的监视锁,它们没有任何竞争就正常运行,
只有这两个线程同时调用m1或m2才会产生阻塞.
分享到:
相关推荐
本资源"《C#多线程编程实战》完整源码"提供了丰富的实例,适用于学习和实践C#中的多线程概念。 在C#中,多线程允许应用程序同时执行多个独立的任务,提高系统利用率并优化性能。.NET框架为开发者提供了强大的支持,...
《C#多线程编程实战(原书第二版)源码》是一本深入探讨C#中多线程技术的专业书籍,其源码提供了丰富的实践示例,帮助读者掌握并发编程的核心概念和技术。在C#中,多线程是实现高性能、响应式应用程序的关键组成部分...
Java多线程编程实战指南(核心篇) 高清pdf带目录 随着现代处理器的生产工艺从提升处理器主频频率转向多核化,即在一块芯片上集成多个处理器内核(Core),多核处理器(Multicore Processor)离我们越来越近了――如今...
《Java多线程编程实战指南》这本书深入浅出地讲解了Java多线程的核心概念和实战技巧,分为核心篇和设计模式篇,旨在帮助开发者掌握并应用多线程技术。 1. **线程基础** - **线程的创建**:Java提供了两种创建线程...
《Java多线程编程实战指南-核心篇》是一本深入探讨Java并发编程的书籍,旨在帮助读者掌握在Java环境中创建、管理和同步线程的核心技术。Java的多线程能力是其强大之处,使得开发者能够在同一时间执行多个任务,提高...
**五、MFC多线程编程实践** 在MFC中创建多线程,首先创建一个继承自CWinThread的类,然后重写InitInstance和Run方法。InitInstance通常用于线程初始化,Run则包含线程的主要工作。通过调用AfxBeginThread函数或者在...
1. 创建线程:使用`std::thread`构造函数传入一个可调用对象(函数、函数指针或lambda表达式)来创建新线程。 2. 同步与join:通过调用`std::thread::join()`函数等待线程结束,避免悬挂线程;`std::thread::detach...
### 五、多线程编程的最佳实践 1. **最小化共享状态**:尽量减少线程间共享的数据,可以降低同步的复杂度。 2. **使用高级同步工具**:利用语言或库提供的高级同步原语,如Java的`volatile`关键字或C++的`std::...
在Linux系统下进行多线程编程是开发高效并发应用程序的关键技术之一。本文将深入探讨Linux环境中的多线程概念、创建与管理线程的方法、线程同步与通信机制,以及多线程编程中可能遇到的问题和解决策略。 一、多线程...
《深入学习:Java多线程编程》是一本专注于Java并发技术的专业书籍,旨在帮助开发者深入理解和熟练运用Java中的多线程编程。Java多线程是Java编程中的核心部分,尤其在现代高性能应用和分布式系统中不可或缺。理解并...
《Windows多线程编程技术与实例(C++)》是一本深入探讨Windows环境下多线程编程的书籍,特别适合正在学习或已经从事C++多线程开发的人员阅读。本书通过丰富的实例,详细讲解了如何在Windows操作系统中利用C++进行...
"多线程编程" 多线程编程是指在同一个程序中同时运行多个线程,以提高程序的执行效率和响应速度。多线程编程可以分为两类:用户级线程和内核级线程。用户级线程是在用户空间中实现的线程,内核级线程是在内核空间中...
在IT领域,多线程编程是一项关键技能,尤其是在操作系统如Symbian中。多线程技术允许程序同时执行多个任务,提升系统效率和用户体验。以下是对"多线程编程"这个主题的详细解释: 1. **多线程概念**:多线程是指一个...
通过学习和实践这些例子,你将能够熟练掌握C++多线程编程的核心概念,并能够在Windows平台上有效地利用多核处理器的优势,编写高效且可靠的多线程程序。记住,多线程编程需要对并发控制和错误处理有深入的理解,才能...
本书主要讲述采用现代C++ 在x86-64 Linux 上编写多线程TCP 网络服务程序的主流常规技术,重点讲解一种适应性较强的多线程服务器的编程模型,即one loop per thread。这是在Linux 下以native 语言编写用户态高性能...
本篇将深入介绍Linux多线程编程的基本概念、实现方法以及注意事项。 首先,多线程是通过创建多个执行线程来实现并发执行的。每个线程都有自己的调用栈,可以独立执行代码,共享同一地址空间内的资源,如全局变量和...
通过以上知识点的学习和实践,可以深入理解CC++多线程编程,解决并发问题,提升程序性能。提供的文件列表可能包含各种多线程编程的练习题目,包括新员工培训的习题纲要,这些资源对于提升编程技能和理论理解非常有...
在《多线程编程指南》中,你将深入学习这些概念,并通过实例来实践如何在实际项目中应用这些知识。无论你是初学者还是经验丰富的开发者,这本书都会提供有价值的洞见和技巧,帮助你构建高效、可靠的多线程应用程序。