QT与JAVA的比较二

五.多线程

JAVA 版

Java多线程问题常用的几种场景(不是全部)通常需要包括如下几个方面:

 

  1. 共享资源的互斥访问(比如:资源初始化过程)。
  2. 有限资源的访问控制(比如:数据库连接池会限制只有有限个线程可以同时保持链接)。
  3. 多线程之间访问的通讯配合(比如:典型的生产-消费模式场景)
  4. 构建线程池
  5. Callable & Future
  6. 读多写少的并发控制(比如:资源初始化过程)。
针对这些比较典型的多线程使用场景,Java已经在他的工具包中提供了很多有力的工具协助开发人员进行处理。下面会针对这几种典型的场景列举一下比较常用的对应解决方案。当然,许多并发控制工具并非只能解决固定的场景,这里仅列出个人认为比较典型的应用。
1、共享资源的互斥访问
最简单也是最古老的方式是随便创建一个对象(任何类型的实例都可以)作为lock,通过synchronized块进行某个关键代码段的互斥访问需求。比如如下的伪代码:
synchronized (lockObject) {  
    // here is your code.  
} 


这里,如果有第二个线程想要进入这个synchronized块,那没有任何商量的余地,就是必须获得lockObject这把锁。
So,这种方法的特点是——简单而粗暴。当然你可以更简单,比如直接在方法的签名上加上synchronized关键字,那么就相当于使用this作为锁对象的大synchronized块而已。代码使用上貌似更简单。

JDK1.5之后,除了上面这种Java关键字的加锁方式之外,新引入了Lock框架。这样就提供了编程API级别的锁支持,比较常用的写法像下面这样:
public class LockDemoClass{  
  
private final Lock lock = new ReentrantLock();  
  
public void lockOnMethod(){  
    //some codes which does not need synchronization  
    lock.lock();  
    try{  
        //some codes which need synchronization  
    }finally{  
        lock.unlock();  
    }  
}    
}  


这个方式明显看上去比之前的synchronized块繁杂了一些,但却在许多方面提供了更大的灵活性。
关于两者的常见对比大概有如下几个方面:
  • 在性能上,在JDK1.5或者之前的时期,确实有人诟病Java原生的synchronized关键字锁的太重,甚至有人认为弃用synchronized而投降Lock是因为性能更好。这种假设在JDK1.6之后,由于JVM内部对synchronized的优化之后,这种考虑的因素几乎可以忽略不计了。因为synchronized有了大幅度的性能提升。
  • 在灵活度上,Lock明显高于前者,尽管有些灵活性未必被开发人员经常使用。比如:
    • Lock可以在不同方法中分别加锁解锁;
    • 如果你需要,Lock可以在保证等待线程进入互斥代码块的排队顺序(当然这要付出一些性能的代价);
    • 你可以通过设置timeout来控制获取锁时尝试等待的时间,而不是想前者那样无限的等待下去(这时加大死锁可能性的一个重要的因素)。
  • 在线程的通讯机制上,前者使用锁对象上wait/notify/notifyAll(继承自Object)来进行线程间的等待唤醒通讯;后者引入了Condition机制。一个Lock上可以创建多个Condition实例,具体condition的语义由开发人员把控,而线程之间的通讯由Condition的await/signal/signalAll来完成,这三个方法的语义基本上和上面的Object三个方法对应。
2、有限资源的访问控制
这个是Semaphore的典型应用场景。
典型的代码结构如下:
public class SemaphoreDemoClass{  
  
//here 5 can be replaced to any int value  
private final Semaphore semaphore = new Semaphore(5);  
  
public void accessControlMethod(){  
    //some codes which does not need multi-thread access control  
    semaphore.acquire();  
    try{  
        //some codes which need multi-thread access control  
    }finally{  
        semaphore.release();  
    }  
}    
} 


Semaphore本质上很像一个带计数性质的阀门。每次访问这个阀门上的acquire()方法时,Semaphore都会将自身的计数器自减1,当Semaphore本身计数器已经被自减到0的时候,再去访问这个Semaphore上的acquire()方法的线程就会被Block住,于是这种机制就顺利的保证了统一资源的同时访问只能在有限个数目的线程范围内。
而且,从这个机制中可以看出,对于内部计数器最大值为1的Semaphore,就可以是另外一种资源互斥访问的形式了。

3、多线程之间访问的通讯配合 
通常情况下,我们认为较优的多线程使用场景是:多线程访问的资源是可以切分的,每个线程操控的资源和其他线程是不相干的。这种场景最爽,每个线程不需要鸟其他线程,只要自己单干就好。
但现实很残酷,绝大部分的多线程使用场景都是需要“团队合作”的。有团队合作,就需要有沟通。
关于线程间通讯沟通机制,已经在前面的共享资源的互斥访问中做了一些介绍。这里再补充一些细节场景:
  • 如果是生产-消费模式,可以借助JDK1.5之后BlockingQueue机制去做(具体选用的BlockingQueue的实现类根据具体情况选择)
  • 多个线程需要步调一致行动,必须保证同一时间点一起执行,比如模仿高并发时的模拟;多个线程必须保证等待其他线程都完成任务之后才可以进入下一步操作(当然两个线程之间的协调等待也可以通过join()来实现)。这两种典型的场景就可以使用CountDownLatch来完成。CountDownLatch内部和Semaphore实现机制相同,都会维护一个计数器,但不同的是,前者只有计数器为0时才允许线程开始执行。
  • 两个线程之间构建的生产-消费模型,但采用“互不干涉”的模式进行交互。注意:这里和一般的生产-消费模式一个最大的区别是,他不是即时生产即时消费的,而是双方分别进行自己的生产和消费(通常会使用两个资源,比如两个队列分别进行生产和消费),其中任何一方ready之后,就可以利用Exchanger.exchange(resourceObject)来完成生产资源和消费资源互换。
4、构建线程池
通过Executors的相应的静态方法可以获得具体的ExecutorService的实例(通常为ThreadPoolExecutor),通过这个具体的线程池的submit方法,可以提交执行自己业务线程。这里线程池内部按照什么机制安排被提交的线程,主要取决于构建ThreadPoolExecutor时,所使用的构造函数的参数,比如不同的内部BlockingQueue

5、Callable & Future
传统的Thread都是Runnable风格,没有返回值。如果你想得到一个线程执行的结果,只能通过join等方法,Block在那里,等待线程执行结束。
JDK1.5之后的有一个新特性就是引入了CallableFuture接口。这里最长用的使用方式就是结合上面第4点提到的线程池的submit方法获得Futurn实例。这样,就不需要阻塞业务当前主线程的执行,在将来的某个时刻在通过Future的get方法获得执行结果。
Future本身的引入,更大的意义是在多线程的环境中引入异步处理的机制,这在某些场景下实现真正的并发非常有意义。

6、读多写少的并发控制
这种比较典型的场景是资源的初始化过程中,某个资源需要初始化一次。只要初始化这一次之后,后面所有的访问全部是读取。
比如某个内存的cache,他会有初始化一堆内容进去。在真正暴露他对外服务之前,我们是需要完成所有资源的cache的,否则可能会造成cache的内容不全而导致的问题。
这里,根据ReadWriteLock的特点,可以将cache初始化的过程用writeLock包住,将资源的读取用readLock包住。这样,除了在writeLock尚未释放之前所有的其他尝试获取readLock的线程需要被Block住之外,其他大多数读取的场景下,多个线程可以共享readLock,可以获得无阻塞的高性能。

原始出处http://hittyt.iteye.com/blog/1633242

QT版本

Qt 提供了丰富的多线程编程支持。Qt 主要从三个方面对多线程编程提供支持:一、构造了一些基本的与平台无关的线程类;二、提交用户自定义事件的 Thread-safe 方式;三、多种线程间同步机制,如信号量,全局锁。

 

1、QThread

       QThread提供了与平台无关的线程。一个QThread代表单独运行于程序的线程。它与进程中的其他线程共享数据,在多任务操作系统中单独运行。线程使用run()方法开始执行,在run()方法中调用exec()开始事件循环。我们可以通过继承QThread并且重写run()函数编写一个线程。例如:

[cpp]  view plain copy
  1. //mythread.h  
  2. #ifndef MYTHREAD_H  
  3. #define MYTHREAD_H  
  4. #include <QThread>  
  5. class MyThread : public QThread  
  6. {  
  7. public:  
  8.     MyThread();  
  9.     MyThread(int count);  
  10.     void run();  
  11. private:  
  12.     int count;  
  13. };  
  14. #endif // MYTHREAD_H  
 

 

[cpp]  view plain copy
  1. //mythread.cpp  
  2. #include "mythread.h"  
  3. #include "QThread"  
  4. #include <QDebug>  
  5. MyThread::MyThread()  
  6. {  
  7. }  
  8. MyThread::MyThread(int count)  
  9. {  
  10.     this->count = count;  
  11. }  
  12. void MyThread::run()  
  13. {  
  14.     while(count<20000)//为了查看结果尽量设置大一些  
  15.     {  
  16.         qDebug()<< QThread::currentThread()->objectName()<<this->count++;  
  17.     }  
  18. }  

[c-sharp]  view plain copy
  1. //main.cpp  
  2. #include <QtCore/QCoreApplication>  
  3. #include "mythread.h"  
  4. #include <QDebug>  
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值