java如何让主线程等待QT与java融合

2011年8月 移动平台大版内专家分月排行榜第二2011年7月 移动平台大版内专家分月排行榜第二2011年3月 移动平台大版内专家分月排行榜第二
2012年8月 移动平台大版内专家分月排行榜第三2012年7月 移动平台大版内专家分月排行榜第三
本帖子已过去太久远了,不再提供回复功能。如何用Qt和Java代码进行混合编程(在Qt for Anroid环境下) - anticevc的专栏 - CSDN博客
如何用Qt和Java代码进行混合编程(在Qt for Anroid环境下)
Qt交叉编译环境
Qt提供Android应用中写Java代码的功能。这就像人们用Android Studio(或者Eclipse等)开发Android应用程序时可用写C++程序一样。默认情况下,Qt只能用Qt提供的模块(大多数情况下已经够用了)写C++程序。但是,在一些特定的情况下你需要用到一些Android API,而Qt有没有提供。一个典型的例子,调用Android默认图库来打开一张图片,或语言合成等你所能想到的应用。如果你在写Android App的时候不想受到这些Qt提供功能的限制,那么请跟我一步一步的学习如何在你的Qt项目中加入Java程序。
注意:我假设你的电脑上已经安装了Qt的Android开发环境
& · &首先创建一个Qt Widgets应用程序,讲Android构建工具设置成你的构建工具。(你可以设置成x86作为模拟器调试也可以设置成armeabi-v7a进行真机调试)。
& · &在你的.pro项目文件中加入下列配置代码。(插入到你想插入的任何地方,但是我推荐你插入到.pro文件的末尾)
& · &现在你可以下载并解压zip文件,拷贝到你的工程目录中,并添加解压后的文件到你的项目中,右键点击你的项目并选择“添加已有文件”&
zip文件中有这样两个文件
& · &现在可以在上述Java文件写Android代码了(假设你已经很熟悉Java代码了)。Java和Qt(C++)之间传递结果需要用到JNI。你可以在网上搜索有关JNI的信息来熟悉JNI,它是Qt(C++)与Android(Java)之间的接口程序。
& · &你可以在你的MainWindow.cpp文件或者其他任何需要访问Java方法的源文件中像这样定义方法。在这里,假设在你的Java代码中有一个名叫“somefunction”的方法要返回String值:
就像Qt 文档中描述的那样,你需要在你的代码中引入下列头文件来允许上文代码中的函数定义:
添加如下头文件:
添加模块到项目文件.pro:
最后,非常重要的一点是“mycompany”,“myappname”和其他本手册中用到的名称必须确保它们在你的整个项目中保持一致。显然可以把它们改成任意你或者你的项目的名称,但是你必须在“AndroidManifest.XML”中同步的修改,在Java代码中、C++代码中也一样,将文件夹名改成“Android-source”等(如果你要引用本demo的话)。你必须保持他们的一致性,否则程序可能会崩溃!
在未来的一段时间内我将陆续多写一些手册来讲述在Qt项目中使用Android特定代码的方法,同时,也欢迎大家在讨论区提交你们遇到的任何问题。
我的热门文章
即使是一小步也想与你分享1308人阅读
JAVA(10)
C/C++(13)
Java多线程问题常用的几种场景(不是全部)通常需要包括如下几个方面:
共享资源的互斥访问(比如:资源初始化过程)。有限资源的访问控制(比如:数据库连接池会限制只有有限个线程可以同时保持链接)。多线程之间访问的通讯配合(比如:典型的生产-消费模式场景)构建线程池Callable & Future读多写少的并发控制(比如:资源初始化过程)。
针对这些比较典型的多线程使用场景,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();
//some codes which need synchronization
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();
//some codes which need multi-thread access control
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之后的有一个新特性就是引入了Callable和Future接口。这里最长用的使用方式就是结合上面第4点提到的线程池的submit方法获得Futurn实例。这样,就不需要阻塞业务当前主线程的执行,在将来的某个时刻在通过Future的get方法获得执行结果。
Future本身的引入,更大的意义是在多线程的环境中引入异步处理的机制,这在某些场景下实现真正的并发非常有意义。
6、读多写少的并发控制
这种比较典型的场景是资源的初始化过程中,某个资源需要初始化一次。只要初始化这一次之后,后面所有的访问全部是读取。
比如某个内存的cache,他会有初始化一堆内容进去。在真正暴露他对外服务之前,我们是需要完成所有资源的cache的,否则可能会造成cache的内容不全而导致的问题。
这里,根据ReadWriteLock的特点,可以将cache初始化的过程用writeLock包住,将资源的读取用readLock包住。这样,除了在writeLock尚未释放之前所有的其他尝试获取readLock的线程需要被Block住之外,其他大多数读取的场景下,多个线程可以共享readLock,可以获得无阻塞的高性能。
原始出处/blog/1633242
Qt 提供了丰富的多线程编程支持。Qt 主要从三个方面对多线程编程提供支持:一、构造了一些基本的与平台无关的线程类;二、提交用户自定义事件的 Thread-safe 方式;三、多种线程间同步机制,如信号量,全局锁。
1、QThread
&&&&&& QThread提供了与平台无关的线程。一个QThread代表单独运行于程序的线程。它与进程中的其他线程共享数据,在多任务操作系统中单独运行。线程使用run()方法开始执行,在run()方法中调用exec()开始事件循环。我们可以通过继承QThread并且重写run()函数编写一个线程。例如:
[c-sharp]&
在main函数中我定义了两个线程,并分别设置了线程名称。运行过程中有三个线程会可能同时运行。主函数线程,myThread1线程,myThread2线程。可以从运行结果看出:
Main Start&
Main Thread 0&
Main Thread 1&
Main Thread 2&
Main Thread 3&
Main Thread 4&
Main Thread 5
Main Thread 1040&
Main Thread 1041&
Main Thread 1042&
Main Thread 1043&
Main Thread 1044&
&MyThread2& 0&
&MyThread2& 1&
&MyThread2& 2&
&MyThread2& 3&
&MyThread2& 4&
&MyThread2& 5&
&MyThread2& 6
&MyThread2& 1158&
&MyThread2& 1159&
&MyThread2& 1160&
&MyThread2& 1161&
&MyThread2& 1162&
&MyThread1& 0&
&MyThread1& 1&
&MyThread1& 2&
&MyThread1& 3&
&MyThread1& 4&
&MyThread1& 5
Main Thread 19990&
Main Thread 19991&
Main Thread 19992&
Main Thread 19993&
Main Thread 19994&
Main Thread 19995&
Main Thread 19996&
Main Thread 19997&
Main Thread 19998&
Main Thread 19999
QThread包含一个枚举类型
enum&&& Priority { IdlePriority, LowestPriority, LowPriority, NormalPriority, ..., InheritPriority } 这个枚举类型表示操作系统如何应安排新创建的线程
QThread::IdlePriority&&& 0&&& 安排只是CPU空闲时在运行
QThread::LowestPriority&&& 1&&& 安排最低优先级
QThread::LowPriority&&& 2&&& 安排低优先级
QThread::NormalPriority&&& 3&&& 安排正常优先级
QThread::HighPriority&&& 4&&& 安排高优先级
QThread::HighestPriority&&& 5&&& 安排高于最高.
QThread::TimeCriticalPriority&&& 6&&& 尽可能安排.
QThread::InheritPriority&&& 7&&& 安排使用继承父类优先级(默认)
公共方法:
QThread ( QObject * parent = 0 )&&& 构造函数,这个线程知道调用它的start函数时才会执行
~QThread ()&&& &&& &&& &&& 析构函数
void&&& exit ( int returnCode = 0 )&&& 调用此方法后,线程将退出事件循环并且返回从调用的QEventLoop::exec()的一个返回值
bool&&& isFinished () const&&& &&& 返回线程是否已完成
bool&&& isRunning () const&&& &&& 返回线程是否正在运行
Priority&&& priority () const&&& 返回线程的优先级.如果线程没有运行,将返回InheritPriority
void&&& setPriority ( Priority priority )& 为正在运行的线程设置优先级,如果线程未运行,将不做任何操作,立即返回.
void&&& setStackSize ( uint stackSize )&&& 设置线程栈的大小的最大值,如果在线程中进行递归调用,那么可以通过增加堆得大小来保证递归不会溢出。
uint&&& stackSize () const&&& &&& 返回当前线程栈的最大值
bool&&& wait ( unsigned long time = ULONG_MAX )阻塞线程直到线程结束运行或Time已经过去
void&&& quit ()&&& &&& 退出事件循环,等同于调用线程的exit(0),如果线程没有事件循环则什么也不做。
void&&& start ( Priority priority = InheritPriority )开始执行线程
void&&& terminate ()&&& 终止线程,线程是否立即被终止,依赖于操作系统.警告:这个函数是危险地,不被鼓励使用
void&&& finished ()&&& 当线程结束运行时此信号被发射
void&&& started ()&&& 当线程开始运行时此信号被发射
void&&& terminated ()&&& 当线程被终止时此信号被发射
静态成员:
QThread *&&& currentThread ()&&& 返回指向当前线程的指针
Qt::HANDLE&&& currentThreadId ()&&& 返回当前线程的句柄
int&&& idealThreadCount ()&&& &&& 返回当前操作系统能运行线程的数量&&&&
void&&& yieldCurrentThread ()&&& &&& 让出CPU给其他线程,对于运行哪个线程完全决定于操作系统。
2、QRunnable
&&&&&& QRunable是所有可运行对象的基类。代表需要执行的一个任务或一段代码的接口。可以通过QThreadPool单独执行线程。QThreadPool可以自动删除QRunable如果此可运行对象的autoDelete()方法返回ture的话。我们可以使用setAutoDelete()函数设置auto-deletion标识.
总之QRunnable提供了简洁有效的可运行对象的创建
&&&&& 优点: 创建过程简洁, 使用方便, 配合着自身的autoDelete特性, 有点“招之即来, 挥之即去”的感觉.
&&&&& 缺点: 无法实时提供自身的运行状态.
3、QThreadPool
&&&&& 线程池,用于管理一个线程的集和。
&&&&& 线程池用于管理和回收线程对象以减少程序中创建线程的花费。
&&&&& 每个Qt应用都有一个全局的QThreadPool对象,通过QThreadPool::globalInstance()获取
activeThreadCount : const int&& &线程池中活动的线程个数
expiryTimeout : int&& &&& &线程经过expiryTimeout未被使用将被认为expired并且exit。这种线程可以重新被restart.默认过期时间是30000毫秒即30秒。如果过期时间设置为负的,那么被创建的线程将永远不会exit直到线程池销毁。设置过期时间不会影响到已经在运行的线程。只有新创建的线程使用这个过期时间。因此推荐在创建线程池后立即设置过期时间.
maxThreadCount : int&& &&& &线程池中允许最大的线程数量。线程池最少允许一个线程,即使这个值被设置为0或负数.默认的最大线程数量是QThread::idealThreadCount()
QThreadPool ( QObject * parent = 0 )&& &
~QThreadPool ()
int&& &activeThreadCount () const&& &返回activeThreadCount属性值
int&& &expiryTimeout () const&& &&& &返回expiryTimeout属性值
int&& &maxThreadCount () const&& &&& &返回maxThreadCount属性值
void&& &releaseThread ()
void&& &reserveThread ()&& &存储一个线程,
void&& &setExpiryTimeout ( int expiryTimeout )
void&& &setMaxThreadCount ( int maxThreadCount )
void&& &start ( QRunnable * runnable, int priority = 0 )&& &存储一个线程并且运行可执行对象并且将可执行对象加入到运行队列中,如果这个线程没有使当前线程数超过最大线程数。
bool&& &tryStart ( QRunnable * runnable )&& &尝试存储一个线程并且运行可执行对象。如果没有这时没有时间片可以运行线程,那么这个方法将什么也不做。否则这个可执行对象线程将使用一个可运行线程立即运行。
void&& &waitForDone ()&& &等待所有线程退出,并且移除所有线程池中线程.
QThreadPool * QThreadPool::globalInstance ()&& &获取全局线程对象
QThreadPool::globalInstance()-&start(Runable实例);
多线程通信
&&&&&& QMutex类提供的是线程之间的访问顺序化
&&&&&& QMutex的目的是保护一个对象、数据结果或者代码实同一时间只有一个线程访问它们。(在Java术语中,它和同步关键字“synchronized”很相似)。一般最好使用QMutexLocker,它能确保锁定和解锁保持一致。
例如,这里有一个方法打印给用户两条消息:
& void someMethod()
&&&& qDebug()&&&Hello&;
&&&& qDebug()&&&World&;
如果同时在两个线程中调用这个方法,结果的顺序将是:
如果你使用了一个互斥量:
& void someMethod()
&&&& mutex.lock();
&&&& qDebug()&&&Hello&;
&&&& qDebug&&&World&;
&&&& mutex.unlock();
用Java的术语,这段代码应该是:
& void someMethod()
&&&& synchronized {
&&&&&& qDebug()&&&Hello&;
&&&&&& qDebug()&&&World&;
成员类型:
&&&&&& enum QMutex::RecursionMode
&&&&&&&&&&&& QMutex::Recursive&&&&&&&&&&&&&&& 1&&&&&& 在这种模式下,一个线程能多次锁定,并且锁定不会被解除直到被相应的解锁。
&&&&&&&&&&&& QMutex::NonRecursive&&&&&&& 0&&&&&&& 在这种模式下,一个下称仅能锁定一次
QMutex::QMutex( Recursion mode = NonRecursive)
&&&&&&&&&&&&&& 构造一个新的互斥量。这个互斥量是在没有锁定的状态下创建的。如果recursive为 Recursive,就构造一个递归互斥量,如果recursive 为NonRecursive(默认值),就构造一个普通互斥量。对于一个递归互斥量,一个线程可以锁定一个互斥量多次并且只有在相同数量的unlock ()调用之后,它才会被解锁
QMutex::~QMutex()&&
&&&&&&&&&&&&& 析构函数,销毁这个互斥量。
void lock()
&&&&&&&&&&&&& 锁定互斥量,如果其他线程已经锁定互斥量,那么这次调用将阻塞知道那个线程把它解锁
bool tryLock()
&&&&&&&&&&&&& 试图锁定互斥量,如果锁被得到,这个函数返回真。如果另一个进程已经锁定了这个互斥量,这个函数返回假,而不是一直等到这个锁可用为止。如果获得锁,这个互斥量必须被使用unlock()解锁,其他线程才能锁定它。
bool tryLock( int timeout)
&&&&&&&&&&&& 试图锁定互斥量。如果锁被得到,这个函数返回真。如果另一个进程已经锁定了这个互斥量,这个函数返回假,而不是一直等到这个锁可用为止。如果其他线程已经锁定这个互斥量,那么这个函数将最多等待timeout毫秒后返回,如果mutex被解锁则返回true,否则返回false.
void unlock()
&&&&&&&&&&&& 解锁这个互斥量。试图对不同的线程中锁定的互斥量进行解锁将会返回一个错误。对一个没有锁定的互斥量进行解锁的结果是将导致未定义的行为(不同的操作系统的线程实现是有很大不同的)
&&&&&&&&& 注:如果使用QMutex.lock()而没有对应的使用QMutex.unlcok()的话就会造成死锁,别的线程永远也得不到接触该mutex锁住的共享资源的机会。尽管可以不使用lock()而使用tryLock(timeout)来避免因为死等而造成的死锁( tryLock(负值)==lock()),但是还是很有可能造成错误。
2、QReadWriteLock
&&&& QReadWriteLock提供了读写锁。
&&&&& 如果你允许多个线程同时有读权限,但如果一个线程正在对资源使用写权限,那么其他线程必须等到这个线程对资源解锁写权限后才能使用读权限访问,即写线程执行的时候会阻塞所有的读线程,而读线程之间的运行不需要进行同步,&
QReadWriterLock 与QMutex相似,除了它对 &read&,&write&访问进行区别对待。它使得多个读者可以共时访问数据。使用QReadWriteLock而不是QMutex,可以使得多线程程序更具有并发性。解决了mutex只允许某个时刻只允许一个线程对共享资源进行访问。
enum&&& RecursionMode { Recursive, NonRecursive }
函数成员:
QReadWriteLock ()
QReadWriteLock ( RecursionMode recursionMode )
~QReadWriteLock ()
void&&& lockForRead ()
void&&& lockForWrite ()
bool&&& tryLockForRead ()
bool&&& tryLockForRead ( int timeout )
bool&&& tryLockForWrite ()
bool&&& tryLockForWrite ( int timeout )
void&&& unlock ()
3、QReadLocker
&&&&&&& 对于QMutex有QMutexLocker来简化使用,而对于QReadWriteLock有 QReadLocker和QWriteLocker
&&&&&&&&& 成员方法:
&&&&&&&& QReadLocker ( QReadWriteLock * lock )
&&&&&&&& ~QReadLocker ()
&&&&&&&& QReadWriteLock *&&& readWriteLock () const
&&&&&&&& void&&& relock ()
&&&&&&&& void&&& unlock ()
4、QWriteLocker
&&&& 函数成员:
&&& QWriteLocker ( QReadWriteLock * lock )
&&& ~QWriteLocker ()
&&& QReadWriteLock *&&& readWriteLock () const
&&& void&&& relock ()
&&& void&&& unlock ()
5、QSemaphore
&&&& QSemaphore类提供了一种通用计数信号。
&&&& QSemaphore是一个顺序化的&互斥量(QMutex)&。一个互斥量仅能被锁定一次,但是&信号量&有可能获得多次。信号量通常用来保护一定数量的相同资源。信号量和互斥量的不同在于,信号量可以在同一时间被多于一个的线程访问。
&&& 例如,假设我们有一个应用程序把数据存储到一个大的树型结构中。应用程序创建了10个线程(通常被称作线程池)来执行树中的搜索。当应用程序搜索树中的一小片数据,它在每个基本节点上使用一个线程来执行搜索。一个信号量就可以用来确保两个线程不必在同一时间内试图对树的同一个分支进行搜索。
&&& QSemaphore::QSemaphore ( int maxcount ) 创建一个新的信号量,并初始化n个资源给它守护
&&& 信号量支持两种基本操作:
&& 1) void acquire(int n = 1)&& 试图获取由信号量看守的n个资源,如果n & avalibable(),这次调用将阻塞直到有那么多资源可用
&& 2)void release(int n = 1)&& 释放n个被信号量守护的资源
&&&&&&& 这个函数也能被用来创建资源,例如:
&&& 一个信号量的非计算实例是在餐馆就餐。信号量被初始化为最大值等于餐馆内的椅子数。当人们来了,他们想要一个座位。当座位满了,信号量就被访问,每次一个人。当人们离开了,访问就被释放,允许更多的人进入。如果一个10人的聚会想坐下,但是这里只有9个座位,那么这10个人就必须等,但是一个4人的聚会就可以坐下了(剩下的座位就是5了,那么那10个人就得等更长的时间了)。
available() const&&& 返回当前信号量可用资源的数量,这个数字不可能为负数
&&& bool tryAcquire(int n = 1)&& 试图获取被当前信号量守护的n个资源,如果available() &=n ,则返回true,否则将立刻返回false且不能获取任何资源。
&&& bool tryAcquire(int n, int timeout)&&&试图获取被当前信号量守护的n个资源,如果available()
&=n ,则返回true,否则将等待timeout毫秒,若期间达到n个可用资源就立即返回true,若timeout毫秒后还没达到返回false,且不获取任何资源。
&&& 注意:如果timeout为负数,则相当于调用tryAcquire(),即一直等待,直到有那么多可用资源.
6、WaitCondition
&&& QWaitCondition类是线程之间允许等待/唤醒的条件。
&&& QWaitConditions允许一个线程告诉其它线程某种条件已经满足,一个或多个线程可以等待一个由wakeOne()或wakeAll()设定的条件QWaitCondition。使用wakeOne()会唤醒一种随机选择的事件或者wakeAll()会把它们全部唤醒。比如,假定每次用户按下一个键,我们有三个任务要同时执行,每个任务都可以放到一个线程中,每个线程的run()都应该是这样:
[c-sharp]&
第四个线程回去读键按下并且每当它接收到一个的时候唤醒其它三个线程,就像这样:
[c-sharp]&
注意这三个线程被唤醒的顺序是未定义的,并且当键被按下时,这些线程中的一个或多个还在do_something(),它们将不会被唤醒(因为它们现在没有等待条件变量)并且这个任务也就不会针对这次按键执行操作。这种情况是可以避免得,比如,就像下面这样做:
[c-sharp]&
&& 互斥量是必须的,因为两个线程试图同时对同一个变量进行修改的结果是不可预知的。
&& QWaitCondition () 创建一个新的等待条件对象
&& ~QWaitCondition ()& 析够函数
&& bool&&& wait ( QMutex * mutex, unsigned long time = ULONG_MAX )
&& & & & 释放锁定的mutex并且在线程事件对象上等待。mutex必须由调用线程初始锁定的。如果mutex没有在锁定状态,这个函数立即返回。如果mutex是一个递归互斥量,这个函数立即返回。mutex将被解锁,并且调用线程将会阻塞,直到下列条件之一满足时才醒来:
& & & & & & * 另一个线程使用wakeOne()或wakeAll()传输信号给它。在这种情况下,这个函数将返回真。
&&&&&&&&&&& * time毫秒过去了。如果time为ULONG_MAX(默认值),那么这个等待将永远不会超时(这个事件必须被传输)。如果等待的事件超时,这个函数将会返回假。
& & & &&mutex&将以同样的锁定状态返回。这个函数提供的是允许从锁定状态到等待状态的原子转换。
&& bool&&& wait ( QReadWriteLock * readWriteLock, unsigned long time = ULONG_MAX )
&&&&&&&& & 释放锁定的readWriteLock并且在线程事件对象上等待。readWriteLock必须由调用线程初始锁定的。如果readWriteLock没有在锁定状态,这个函数立即返回。如果readWriteLock是一个递归互斥量,这个函数将不会正确的释放锁。readWriteLock将被解锁,并且调用线程将会阻塞,直到下列条件之一满足时才醒来:
&&&&&&&&&&& * 另一个线程使用wakeOne()或wakeAll()传输信号给它。在这种情况下,这个函数将返回真。
&&&&&&&&&&& * time毫秒过去了。如果time为ULONG_MAX(默认值),那么这个等待将永远不会超时(这个事件必须被传输)。如果等待的事件超时,这个函数将会返回假。
&&&&&&&&readWriteLock&将以同样的锁定状态返回。这个函数提供的是允许从锁定状态到等待状态的原子转换。&&&
&& void&&& wakeAll ()&& 这将会唤醒所有等待QWaitCondition的线程。这些线程被唤醒的顺序依赖于操组系统的调度策略,并且不能被控制或预知。
&& void&&& wakeOne ()&&& 这将会唤醒所有等待QWaitCondition的线程中的一个线程。这个被唤醒的线程依赖于操组系统的调度策略,并且不能被控制或预知。
&& 7、QThreadStorage
&&&&&&& QThreadStorage是一个模板类提供逐线程数据存储.
&&&&&&& 由于编译器的限制,QThreadStorage仅能存储指针。
&&&&&&& setLocalData()函数保存了一个单一线程值,以供线程调用.这个值能使用localData()函数访问.
&&&&&&& QThreadStorage拥有数据(必须是被新创建在堆上的)和当线程退出就删除数据的所有权。
&&&&&&& hasLocalData()函数允许程序员确定数据是否先前已经设置了使用setLocalData()函数。这对用于延迟初始化有用。
&&&&& & 成员函数:
& & & & QThreadStorage ()
&& & && ~QThreadStorage ()
&& & && bool&&& hasLocalData () const
& & & & T &&&& localData ()&& && && T&&& localData () const
&& & && void&&& setLocalData ( T data )
&&&&&&& 例如,下面的代码使用QThreadStorage去为每个线程存储一个单缓存,有&cacheObject()和removeFromCache()函数。这个cache当线程退出时就自动删除.
&&&&& 注意:
As noted above, QThreadStorage can only store pointers due to compiler limitations.
The QThreadStorage destructor does not delete per-thread data. QThreadStorage only deletes per-thread data when the thread exits or when&&&()
is called multiple times.
QThreadStorage can be used to store data for the&&main()&&&thread. QThreadStorage deletes all data set for the&&main()&&&thread when&&&is
destroyed, regardless of whether or not the&main()&&&thread has actually finished.
非线程安全类
&&&&&& 这个类不是线程安全的,因为假如多个线程都试图修改数据成员 n,结果未定义。这是因为c++中的++和--操作符不是原子操作。实际上,它们会被扩展为三个机器指令:
&&&&&&&&&& 1,把变量值装入寄存器
&&&&&&&&&& 2,增加或减少寄存器中的值
&&&&&&&&&& 3,把寄存器中的值写回内存
&&&&&&&&&& 假如线程A与B同时装载变量的旧值,在寄存器中增值,回写。他们写操作重叠了,导致变量值仅增加了一次。很明显,访问应该串行化:A执行123步骤时不应被打断。
线程安全类:
原始出处http://blog.csdn.net/liuhongwei123888/article/details/6076741
//////我是华丽的分割线///////////////////////////////////////////////
六.异步网络通信
(1)Java NIO Socket通信
套接字通道多路复用的思想是创建一个Selector,将多个通道对它进行注册,当套接字有关注的事件发生时,可以选出这个通道进行操作。
& & 服务器端的代码如下,相关说明就带在注释里了:
&/pre&&pre name=&code& class=&cpp&&&span style=&font-family: Arial, Helvetica, sans-&&&/span&&pre name=&code& class=&cpp&&Selector selector = Selector.open();// 创建ServerSocketChannel,并把它绑定到指定端口上
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(&127.0.0.1&, 7777));
// 设置为非阻塞模式, 这个非常重要
server.configureBlocking(false);
// 在选择器里面注册关注这个服务器套接字通道的accept事件
// ServerSocketChannel只有OP_ACCEPT可用,OP_CONNECT,OP_READ,OP_WRITE用于SocketChannel
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 测试等待事件发生,分为直接返回的selectNow()和阻塞等待的select(),另外也可加一个参数表示阻塞超时
// 停止阻塞的方法有两种: 中断线程和selector.wakeup(),有事件发生时,会自动的wakeup()
// 方法返回为select出的事件数(参见后面的注释有说明这个值为什么可能为0).
// 另外务必注意一个问题是,当selector被select()阻塞时,其他的线程调用同一个selector的register也会被阻塞到select返回为止
// select操作会把发生关注事件的Key加入到selectionKeys中(只管加不管减)
if (selector.select() == 0) { //
// 获取发生了关注时间的Key集合,每个SelectionKey对应了注册的一个通道
Set&SelectionKey& keys = selector.selectedKeys();
// 多说一句selector.keys()返回所有的SelectionKey(包括没有发生事件的)
for (SelectionKey key : keys) {
// OP_ACCEPT 这个只有ServerSocketChannel才有可能触发
if (key.isAcceptable()) {
// 得到与客户端的套接字通道
SocketChannel channel = ((ServerSocketChannel) key.channel()).accept();
// 同样设置为非阻塞模式
channel.configureBlocking(false);
// 同样将于客户端的通道在selector上注册,OP_READ对应可读事件(对方有写入数据),可以通过key获取关联的选择器
channel.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(1024));
// OP_READ 有数据可读
if (key.isReadable()) {
SocketChannel channel = (SocketChannel) key.channel();
// 得到附件,就是上面SocketChannel进行register的时候的第三个参数,可为随意Object
ByteBuffer buffer = (ByteBuffer) key.attachment();
// 读数据 这里就简单写一下,实际上应该还是循环读取到读不出来为止的
channel.read(buffer);
// 改变自身关注事件,可以用位或操作|组合时间
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
// OP_WRITE 可写状态 这个状态通常总是触发的,所以只在需要写操作时才进行关注
if (key.isWritable()) {
// 写数据掠过,可以自建buffer,也可用附件对象(看情况),注意buffer写入后需要flip
// 写完就吧写状态关注去掉,否则会一直触发写事件
key.interestOps(SelectionKey.OP_READ);
// 由于select操作只管对selectedKeys进行添加,所以key处理后我们需要从里面把key去掉
keys.remove(key);
这里需要着重说明一下select操作做了什么(根据现象推的,具体好像没有找到这个的文档说明),他每次检查keys里面每个Key对应的通道的状态,如果有关注状态时,就决定返回,这时会同时将Key对象加入到selectedKeys中,并返回selectedKeys本次变化的对象数(原本就在selectedKeys中的对象是不计的),由于一个Key对应一个通道(可能同时处于多个状态,所以注意上面的if语句我都没有写else),所以select返回0也是有可能的。另外OP_WRITE和OP_CONNET这两个状态是不能长期关注的,只在有需要的时候监听,处理完必须马上去掉。如果没有发现有任何关注状态,select会一直阻塞到有状态变化或者超时什么的。
SelectionKey的其他几个方法,attach(Object)为key设置附件,并返回之前的附件;interestOps()和readyOps()返回关注状态和当前状态;cancel()为取消注册;isValid()表示key是否有效(在key取消注册,通道关闭,选择器关闭这三个事情发生之前,key均为有效的,但不包括对方关闭通道,所以读写应注意异常)。
& & 还有一个状态上面没有使用,OP_CONNECT这个主要是用于客户端,对应的key的方法是isConnectable()表示已经创建好了连接。
非阻塞实现的客户端如下:
Selector selector = Selector.open();
// 创建一个套接字通道,注意这里必须使用无参形式
SocketChannel channel = SocketChannel.open();
// 设置为非阻塞模式,这个方法必须在实际连接之前调用(所以open的时候不能提供服务器地址,否则会自动连接)
channel.configureBlocking(false);
// 连接服务器,由于是非阻塞模式,这个方法会发起连接请求,并直接返回false(阻塞模式是一直等到链接成功并返回是否成功)
channel.connect(new InetSocketAddress(&127.0.0.1&, 7777));
// 注册关联链接状态
channel.register(selector, SelectionKey.OP_CONNECT);
while (true) {
// 前略 和服务器端的类似
// 获取发生了关注时间的Key集合,每个SelectionKey对应了注册的一个通道
Set&SelectionKey& keys = selector.selectedKeys();
for (SelectionKey key : keys) {
// OP_CONNECT 两种情况,链接成功或失败这个方法都会返回true
if (key.isConnectable()) {
// 由于非阻塞模式,connect只管发起连接请求,finishConnect()方法会阻塞到链接结束并返回是否成功
// 另外还有一个isConnectionPending()返回的是是否处于正在连接状态(还在三次握手中)
if (channel.finishConnect()) {
// 链接成功了可以做一些自己的处理,略
// 处理完后必须吧OP_CONNECT关注去掉,改为关注OP_READ
key.interestOps(SelectionKey.OP_READ);
// 后略 和服务器端的类似
(2)Java AIO Socket通信(JDK1.7 以上版本支持)
NIO与AIO最大的区别在于:NIO需要轮询才能达到非阻塞,而AIO模型中,是由服务器的OS完成读写操作,然后再启动线程进行读写操作之后的逻辑处理。这样AIO的性能显然要优于NIO。&NIO在读写就绪的时候通知用户操作处理器,而AIO是在读写完成时(由OS内核完成),通知用户完成操作器,所需要的数据都已经存在于用户程序的可以访问到的内存空间里了。
import java.net.InetSocketA
import java.nio.ByteB
import java.nio.channels.AsynchronousChannelG
import java.nio.channels.AsynchronousServerSocketC
import java.nio.channels.AsynchronousSocketC
import java.pletionH
import java.util.concurrent.ExecutorS
import java.util.concurrent.E
import org.apache.log4j.L
public class AioServer implements Runnable{
final static Logger logger = Logger.getLogger(AioServer.class);
Object lock = new Object();
InetSocketAddress serverAddress =
int backlog = 0;
int buff_size = 1024;
int threadPoolSize = 0;
public AioServer(int port){
this.serverAddress = new InetSocketAddress(port);
initialization();
public AioServer(String ip,int port){
this.serverAddress = new InetSocketAddress(ip,port);
initialization();
void initialization(){
threadPoolSize = threadPoolSize&0? threadPoolSize: Runtime.getRuntime().availableProcessors();
public void run() {
(&aioserver threadPoolSize:&+this.threadPoolSize);
ExecutorService threadPool = Executors.newFixedThreadPool(this.threadPoolSize);
AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(threadPool);
final AsynchronousServerSocketChannel assc = AsynchronousServerSocketChannel.open(channelGroup);
if(this.backlog&0){ assc.bind(serverAddress,this.backlog); }
else { assc.bind(serverAddress); }
(&aioserver listen:&+this.serverAddress);
assc.accept(null, new CompletionHandler&AsynchronousSocketChannel,Object&(){
public void completed(AsynchronousSocketChannel result,
Object attachment) {
assc.accept(null, this);
handler(result,attachment);
public void failed(Throwable exc, Object attachment) {
exc.printStackTrace();
synchronized(lock){
lock.wait();
channelGroup.shutdownNow();
(&aioserver shutdownC.&);
} catch (Exception e) {
e.printStackTrace();
static byte[] echo = &done.&.getBytes();
static int connCount = 1;
void handler(AsynchronousSocketChannel conn,Object att){
(&connect server :&+connCount++);
ByteBuffer buff = ByteBuffer.allocate(this.buff_size);
buff.clear();
int rl = conn.read(buff).get();
buff.flip();
(&recv &+rl+&: &+new String(buff.array(),0,rl));
buff.clear(); //清空buff数据
buff.put(echo);
buff.flip();
int wl = conn.write(buff).get();
(&send &+wl);
conn.close();
}catch(Exception ex){
ex.printStackTrace();
public void setThreadPoolSize(int threadPoolSize){
this.threadPoolSize = threadPoolS
public void setBacklog(int backlog){
this.backlog =
public void shutdown(){
//(&call shutdown()&);
synchronized(lock){
lock.notifyAll();
AioTest1.java&
static void t3(){
AioServer aiose = new AioServer(9777);
//线程模式启动
new Thread(aiose).start();;
//非线程模式启动
aiose.run();
Thread.sleep();
//3秒后关闭
aiose.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
jopts=' -Dcom.sun.management.jmxremote.port=8999'
jopts=$jopts' -Dcom.sun.management.jmxremote.authenticate=false'
jopts=$jopts' -Dcom.sun.management.jmxremote.ssl=false'
jopts=$jopts' -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.EPollSelectorProvider'
cp=aio.jar
cp=$cp:log4j-1.2.16.jar
echo java $jopts -cp $cp AioTest1
java $jopts -cp $cp AioTest1
注:这个run.sh的内容是java开启nio那抄过来的,对aio来说貌似没用,因为linux下aio本来就是Epoll实现。
aio在windows下采用IOCP实现。
(3)QT 异步socket
//main.cpp:
#include &QCoreApplication&
#include &myudp.h&
int main(int argc, char *argv[])
QCoreApplication a(argc, argv);
client.HelloUDP();
return a.exec();
//myudp.h:
// myudp.h
#ifndef MYUDP_H
#define MYUDP_H
#include &QObject&
#include &QUdpSocket&
class MyUDP : public QObject
explicit MyUDP(QObject *parent = 0);
void HelloUDP();
public slots:
void readyRead();
QUdpSocket *
#endif // MYUDP_H
myudp.cpp:
// myudp.cpp
#include &myudp.h&
MyUDP::MyUDP(QObject *parent) :
QObject(parent)
// create a QUDP socket
socket = new QUdpSocket(this);
// The most common way to use QUdpSocket class is
// to bind to an address and port using bind()
// bool QAbstractSocket::bind(const QHostAddress & address,
quint16 port = 0, BindMode mode = DefaultForPlatform)
socket-&bind(QHostAddress::LocalHost, 1234);
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
void MyUDP::HelloUDP()
QByteArray D
Data.append(&Hello from UDP&);
// Sends the datagram datagram
// to the host address and at port.
// qint64 QUdpSocket::writeDatagram(const QByteArray & datagram,
const QHostAddress & host, quint16 port)
socket-&writeDatagram(Data, QHostAddress::LocalHost, 1234);
void MyUDP::readyRead()
// when data comes in
buffer.resize(socket-&pendingDatagramSize());
quint16 senderP
// qint64 QUdpSocket::readDatagram(char * data, qint64 maxSize,
QHostAddress * address = 0, quint16 * port = 0)
// Receives a datagram no larger than maxSize bytes and stores it in data.
// The sender's host address and port is stored in *address and *port
// (unless the pointers are 0).
socket-&readDatagram(buffer.data(), buffer.size(),
&sender, &senderPort);
qDebug() && &Message from: & && sender.toString();
qDebug() && &Message port: & && senderP
qDebug() && &Message: & &&
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:28944次
排名:千里之外
转载:41篇
(1)(3)(4)(19)(18)

我要回帖

更多关于 java如何让程序暂停 的文章

 

随机推荐