主线程与子线程之间通讯, 子线程与子线程之间的通讯 Java

线程之间的通讯可以有两种方法.

一.利用全局变量. 大家都可以访问,所以使用很方便.



二.利用PostThreadMesagge(ThreadId,Msg,0,0)来进行通讯.

其中要注意的问题是,因为利用CreateThread创建的线程是无消息获取通道,消息映射这些功能,只有有窗体的线程才具备这些功能,所以要自己获取消息队列,利用PeekMessage既可.



以下是我做的例子.



void CCTestDlg::OnBnClickedButtoncreatethread()
{
// TODO: Add your control notification handler code here
int by = sizeof(BYTE);
int a = sizeof(char);
int b = sizeof(short);
int c = sizeof(WORD);
int d = sizeof(int);
int e = sizeof(DWORD);
int f = sizeof(long);
int m =100;
int*p = &m;

int n=10;
const int* const pp = &n;

ABB ca(1,2,3);
ca.GetNum();


ABB dd(4,5,6);
dd.GetNum();


m_hWait1 = ::CreateEvent(0,TRUE,FALSE,0);
m_hWait2 = ::CreateEvent(0,TRUE,FALSE,0);
m_hWait3 = ::CreateEvent(0,TRUE,FALSE,0);
m_hWait4 = ::CreateEvent(0,TRUE,FALSE,0);

DWORD dwID;
HANDLE hHandle = ::CreateThread(NULL,0,ThreadProc,this,0,&dwID);


DWORD dwThread1,dwThread2;
HANDLE hThread1,hThread2;
m_pThread1 = new CThread1;
m_pThread2 = new CThread2;
hThread1 = ::CreateThread(NULL,0,CThread1::ThreadPoc,m_pThread1,0,&dwThread1);
hThread2 = ::CreateThread(NULL,0,CThread2::ThreadPoc,m_pThread2,0,&dwThread2);

if(hThread1)
{
 
  m_Arry.Add(dwThread1);
  m_pThread1->m_pMainWnd = this;
}

if(hThread2)
{
  m_Arry.Add(dwThread2);
  m_pThread2->m_pMainWnd = this;
}

}



CThread1::CThread1()
{
m_hStop = ::CreateEvent(0,true,false,0);
}

CThread1::~CThread1()
{
}

DWORD WINAPI CThread1::ThreadPoc(LPVOID lparam)
{
CThread1 *pThread1 = (CThread1*)lparam;
return pThread1->MainFunc();
}

DWORD CThread1::MainFunc()
{
while(::WaitForSingleObject(m_hStop, 10) != WAIT_OBJECT_0)//main loop
{
  MSG   msg;  
  if(PeekMessage(&msg , NULL, 0, 0,  PM_REMOVE))
  {
   switch(msg.message)  
   {  
   case   WM_THREAD_EXIT_MSG:  
    //   
    AfxMessageBox("Thread1 receive Thread2 exit");
    break; 
   }
  }  
}
DWORD dwid = m_pMainWnd->m_Arry.GetAt(1);
PostThreadMessage(dwid,WM_THREAD_EXIT_MSG,0,0);
return 0;
}

void CThread1::StopServ()
{
SetEvent(m_hStop);
}

void CThread1::ThreadMsgFunction(WPARAM wParam,LPARAM lParam)
{

}

CThread2::CThread2()
{
m_hStop = ::CreateEvent(0,true,false,0);
}

CThread2::~CThread2()
{
}

DWORD WINAPI CThread2::ThreadPoc(LPVOID lparam)
{
CThread2 *pThread2 = (CThread2*)lparam;
return pThread2->MainFunc();
}

DWORD CThread2::MainFunc()
{
//while(::WaitForSingleObject(m_hStop,10)!=WAIT_OBJECT_0)
while(::WaitForSingleObject(m_hStop, 10) != WAIT_OBJECT_0)//main loop
{
  MSG   msg;  
  if(PeekMessage(&msg , NULL, 0, 0,  PM_REMOVE))
  {
   switch(msg.message)  
   {  
    case   WM_THREAD_EXIT_MSG:  
    // 
    AfxMessageBox("Thread2 receive Thread1 exit");
    break; 
   }
  }  
}
DWORD dwid = m_pMainWnd->m_Arry.GetAt(0);
PostThreadMessage(dwid,WM_THREAD_EXIT_MSG,0,0);
return 0;
}

void CThread2::StopServ()
{
SetEvent(m_hStop);
}

void CThread2::ThreadMsgFunction(WPARAM wParam,LPARAM lParam)
{

}
void CCTestDlg::OnBnClickedButtonExitThread1()
{
// TODO: Add your control notification handler code here
m_pThread1->StopServ();
}

void CCTestDlg::OnBnClickedButtonExithread2()
{
// TODO: Add your control notification handler code here
m_pThread2->StopServ();
}

Java 线程应该注意的问题
2005.10.21  来自:网易科技      共有评论()条 发表评论     收藏
Java的线程编程非常简单。但有时会看到一些关于线程的错误用法。下面列出一些应该注意的问题。

   Java的线程编程非常简单。但有时会看到一些关于线程的错误用法。下面列出一些应该注意的问题。

   1.同步对象的恒定性All java objects are references.

  对于局部变量和参数来说,java里面的int, float, double, boolean等基本数据类型,都在栈上。这些基本类型是无法同步的;java里面的对象(根对象是Object),全都在堆里,指向对象的reference在栈上。

  java中的同步对象,实际上是对于reference所指的“对象地址”进行同步。

  需要注意的问题是,千万不要对同步对象重新赋值。举个例子。

  class A implements Runnable{

  Object lock = new Object();

  void run(){

  for(...){

  synchronized(lock){

  // do something

  ...

  lock = new Object();   }   }   }   run函数里面的这段同步代码实际上是毫无意义的。因为每一次lock都给重新分配了新的对象的reference,每个线程都在新的reference同步。

  大家可能觉得奇怪,怎么会举这么一个例子。因为我见过这样的代码,同步对象在其它的函数里被重新赋了新值。

  这种问题很难查出来。

  所以,一般应该把同步对象声明为final.

  final Object lock = new Object();

  使用Singleton Pattern 设计模式来获取同步对象,也是一种很好的选择。

  2.如何放置共享数据实现线程,有两种方法,一种是继承Thread类,一种是实现Runnable接口。

  上面举的例子,采用实现Runnable接口的方法。本文推荐这种方法。

  首先,把需要共享的数据放在一个实现Runnable接口的类里面,然后,把这个类的实例传给多个Thread的构造方法。这样,新创建的多个Thread,都共同拥有一个Runnable实例,共享同一份数据。

  如果采用继承Thread类的方法,就只好使用static静态成员了。如果共享的数据比较多,就需要大量的static静态成员,令程序数据结构混乱,难以扩展。这种情况应该尽量避免。

  编写一段多线程代码,处理一个稍微复杂点的问题。两种方法的优劣,一试便知。

  3.同步的粒度线程同步的粒度越小越好,即,线程同步的代码块越小越好。尽量避免用synchronized修饰符来声明方法。尽量使用synchronized(anObject)的方式,如果不想引入新的同步对象,使用synchronized(this)的方式。而且,synchronized代码块越小越好。

  4.线程之间的通知这里使用“通知”这个词,而不用“通信”这个词,是为了避免词义的扩大化。

  线程之间的通知,通过Object对象的wait()和notify() 或notifyAll() 方法实现。

  下面用一个例子,来说明其工作原理:

  假设有两个线程,A和B。共同拥有一个同步对象,lock。

  1.首先,线程A通过synchronized(lock) 获得lock同步对象,然后调用lock.wait()函数,放弃lock同步对象,线程A停止运行,进入等待队列。

  2.线程B通过synchronized(lock) 获得线程A放弃的lock同步对象,做完一定的处理,然后调用 lock.notify() 或者lock.notifyAll() 通知等待队列里面的线程A。

  3.线程A从等待队列里面出来,进入ready队列,等待调度。

  4.线程B继续处理,出了synchronized(lock)块之后,放弃lock同步对象。

  5.线程A获得lock同步对象,继续运行。

  例子代码如下:

  public class SharedResource implements Runnable{

  Object lock = new Object();

  public void run(){

  // 获取当前线程的名称。

  String threadName = Thread.currentThread().getName();

  if( “A”.equals(threadName)){

  synchronized(lock){ //线程A通过synchronized(lock) 获得lock同步对象

  try{

  System.out.println(“ A gives up lock.”);

  lock.wait(); // 调用lock.wait()函数,放弃lock同步对象,

  // 线程A停止运行,进入等待队列。

  }catch(InterruptedException e){   }   // 线程A重新获得lock同步对象之后,继续运行。

  System.out.println(“ A got lock again and continue to run.”);

  } // end of synchronized(lock)   }   if( “B”.equals(threadName)){

  synchronized(lock){//线程B通过synchronized(lock) 获得线程A放弃的lock同步对象

  System.out.println(“B got lock.”);

  lock.notify(); //通知等待队列里面的线程A,进入ready队列,等待调度。

  //线程B继续处理,出了synchronized(lock)块之后,放弃lock同步对象。

  System.out.println(“B gives up lock.”);

  } // end of synchronized(lock)

  boolean hasLock = Thread.holdsLock(lock); // 检查B是否拥有lock同步对象。

  System.out.println(“B has lock ? -- ” hasLock); // false.   }   }   }   public class TestMain{

  public static void main(){

  Runnable resource = new SharedResource();

  Thread A = new Thread(resource,”A”);

  A.start();

  // 强迫主线程停止运行,以便线程A开始运行。

  try {

  Thread.sleep(500);

  }catch(InterruptedException e){   }   Thread B = new Thread(resource,”B”);

  B.start();   }   }

   5.跨类的同步对象对于简单的问题,可以把访问共享资源的同步代码都放在一个类里面。

  但是对于复杂的问题,我们需要把问题分为几个部分来处理,需要几个不同的类来处理问题。这时,就需要在不同的类中,共享同步对象。比如,在生产者和消费者之间共享同步对象,在读者和写者之间共享同步对象。

  如何在不同的类中,共享同步对象。有几种方法实现,

  (1)前面讲过的方法,使用static静态成员,(或者使用Singleton Pattern.)

  (2)用参数传递的方法,把同步对象传递给不同的类。

  (3)利用字符串常量的“原子性”。

  对于第三种方法,这里做一下解释。一般来说,程序代码中的字符串常量经过编译之后,都具有唯一性,即,内存中不会存在两份相同的字符串常量。

  (通常情况下,C ,C语言程序编译之后,也具有同样的特性。)

  比如,我们有如下代码。

  String A = “atom”;

  String B = “atom”;

  我们有理由认为,A和B指向同一个字符串常量。即,A==B。

  注意,声明字符串变量的代码,不符合上面的规则。

  String C= new String(“atom”);

  String D = new String(“atom”);

  这里的C和D的声明是字符串变量的声明,所以,C != D。

  有了上述的认识,我们就可以使用字符串常量作为同步对象。

  比如我们在不同的类中,使用synchronized(“myLock”), “myLock”.wait(),“myLock”.notify(), 这样的代码,就能够实现不同类之间的线程同步。

  本文并不强烈推荐这种用法,只是说明,有这样一种方法存在。

  本文推荐第二种方法,(2)用参数传递的方法,把同步对象传递给不同的类。

关于java多线程一些心得 - Java综合 - Java - ITeye论坛(转载)
分类: JAVA 2011-06-29 17:08 86人阅读 评论(0) 收藏 举报
1、主线程死掉之后 ,所有在主线程上启动的线程并不会死掉
2、通过把线程加入ThreadGroup可以操作其他同组线程的生命
3、如果想在子线程中杀掉主线程,直接把主线程的Thread的传入子线程即可
4、要想关闭多线程程序,需要把当前所有的线程全部杀死才能关闭
5、各个线程互补影响~父子线程也不会有影响



千言万语汇总就那么几句话

When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method named main of some designated class). The Java Virtual Machine continues to execute threads until either of the following occurs:

    * The exit method of class Runtime has been called and the security manager has permitted the exit operation to take place.
    * All threads that are not daemon threads have died, either by returning from the call to the run method or by throwing an exception that propagates beyond the run method.
lirong1978
线程到底有啥用啊,不就是同时执行多个任务么,不就是CPU分配时间片一会儿执行这个,一会儿执行那个,跟先把这个执行完,再执行那个有什么区别啊,谁能说下呢


呵呵 有些程序是需要同时处理多个通道的....
c++一般采用多进程的方式~~直接fork
java的fork的话 会启动多个虚拟机进行执行~~所有采用多线程
多成成好像CPU利用率上面更好一些~~具体的就忘掉了

lirong1978 写道
线程到底有啥用啊,不就是同时执行多个任务么,不就是CPU分配时间片一会儿执行这个,一会儿执行那个,跟先把这个执行完,再执行那个有什么区别啊,谁能说下呢

因为并不是所有的线程都能很好的利用CPU,有可能有些操作会阻塞,如果顺序执行的话,那那个阻塞的方法岂不是拦着别的方法的执行,如果多线程的话,那个阻塞的方法只塞他自己的线程,别的线程照样跑。
经典的运用就是ServerSocket.accept(),一个线程一直等别人连接,另外可以起一些线程去处理连接到的socket,如果不用多线程,怎么处理的了?除非转换观念用NIO。

lirong1978 写道
线程到底有啥用啊,不就是同时执行多个任务么,不就是CPU分配时间片一会儿执行这个,一会儿执行那个,跟先把这个执行完,再执行那个有什么区别啊,谁能说下呢

你说的是并发,利用的单CPU轮询。现代意义的多线程是并行的,利用多CPU处理器,同时工作。

dyllove98 写道
呵呵 有些程序是需要同时处理多个通道的....
c++一般采用多进程的方式~~直接fork
java的fork的话 会启动多个虚拟机进行执行~~所有采用多线程
多成成好像CPU利用率上面更好一些~~具体的就忘掉了


JDK1.2 之前采用所谓的Green Thread,不完全依赖于OS的线程调度。

后面的版本就不同了,直接利用OS或者是CPU线程调度,值得一提的是,Java线程和OS线程的映射比率是M:N(可能一对一,当M=N时)。

借宝地问下。
setDeamon(boolean)
这个方法的具体作用是什么? 与no-Deamon有什么区别或者权限么?

至于为什么要多线程?

我记得书本上的解释是:当年进程切换太耗资源,系统需要保存现场,还需要内存寻址等等,而多线程共用进程资源,在进程内寻址,在做切换时容易得多,所以就有了多进程和多线程之分。

我觉得c++也应该提倡多线程而非多进程,而跟语言没关系。

另外在多CPU环境下,多线程才真正算个“多线程”,呵呵

setDeamon(boolean)用来设置线程为守护线程。
如果一个线程是守护线程,那么它创建的线程默认也是守护线程。

**不过我觉得你还是没有回答我的问题。
自己google了下:
Any Java thread can be a daemon thread. Daemon threads are service providers for other threads running in the same process as the daemon thread. The run() method for a daemon thread is typically an infinite loop that waits for a service request.
When the only remaining threads in a process are daemon threads, the interpreter exits. This makes sense because when only daemon threads remain, there is no other thread for which a daemon thread can provide a service.
做过实验才明白,原来当只存在Daemon线程且没有用户线程的时候,JVM退出,以前写thread的时候还真没注意,实验如下:

Java代码 
public class DaemonThread extends Thread{  
 
    public void  run(){  
        while(true){  
        System.out.println("I'm daemon");  
        }  
    }  
    public static void main(String args[]){  
        DaemonThread daemon=new DaemonThread();  
        daemon.setDaemon(true);  
        daemon.start();  
    }  

[java] view plaincopy
public class DaemonThread extends Thread{ 
public void  run(){ 
while(true){ 
System.out.println("I'm daemon"); 


public static void main(String args[]){ 
DaemonThread daemon=new DaemonThread(); 
daemon.setDaemon(true); 
daemon.start(); 



可以看到JVM很快就退出了,虽然这里是一个while(true)循环。

**关于JVM的进程间通讯,没深入了解过,不妨说说?

至于Java多线程关于锁概念和进程间通讯,我觉得看了JVM的内存实现之后,大致就会明白了



小弟不才,线程间通讯这个话题,我了解也不是很多,个人觉得去baidu上搜一下经典的讲解才是王道。

不过我的一个简单理解是:Java的进程有自己的内存(比如有内存存放变量a),进程创建出来的各个线程也有自己的内存(如果线程用到变量a,那么线程有单独的内存存放变量a的一个copy)。
如果,线程同步变量a,那么线程第一次读取a的值的时候,就从进程内存中读取,否则从自己内存中读取。同理,如果线程同步变量a,每次写变量a的时候,不仅把值写入自己的内存中,也同时把值写入进程的内存中。

欢迎指正

zhaspe 写道

如果,线程同步变量a,那么线程第一次读取a的值的时候,就从进程内存中读取,否则从自己内存中读取。同理,如果线程同步变量a,每次写变量a的时候,不仅把值写入自己的内存中,也同时把值写入进程的内存中。

欢迎指正


你的意思是说,如果对共享资源变量a不做线程同步,那么各自的线程从自己内存中读?

这个显然与多线程读取共享资源,发生读脏数据的情形是矛盾的呀。

Java 线程应该注意的问题
2005.10.21  来自:网易科技      共有评论()条 发表评论     收藏
Java的线程编程非常简单。但有时会看到一些关于线程的错误用法。下面列出一些应该注意的问题。

   Java的线程编程非常简单。但有时会看到一些关于线程的错误用法。下面列出一些应该注意的问题。

   1.同步对象的恒定性All java objects are references.

  对于局部变量和参数来说,java里面的int, float, double, boolean等基本数据类型,都在栈上。这些基本类型是无法同步的;java里面的对象(根对象是Object),全都在堆里,指向对象的reference在栈上。

  java中的同步对象,实际上是对于reference所指的“对象地址”进行同步。

  需要注意的问题是,千万不要对同步对象重新赋值。举个例子。

  class A implements Runnable{

  Object lock = new Object();

  void run(){

  for(...){

  synchronized(lock){

  // do something

  ...

  lock = new Object();   }   }   }   run函数里面的这段同步代码实际上是毫无意义的。因为每一次lock都给重新分配了新的对象的reference,每个线程都在新的reference同步。

  大家可能觉得奇怪,怎么会举这么一个例子。因为我见过这样的代码,同步对象在其它的函数里被重新赋了新值。

  这种问题很难查出来。

  所以,一般应该把同步对象声明为final.

  final Object lock = new Object();

  使用Singleton Pattern 设计模式来获取同步对象,也是一种很好的选择。

  2.如何放置共享数据实现线程,有两种方法,一种是继承Thread类,一种是实现Runnable接口。

  上面举的例子,采用实现Runnable接口的方法。本文推荐这种方法。

  首先,把需要共享的数据放在一个实现Runnable接口的类里面,然后,把这个类的实例传给多个Thread的构造方法。这样,新创建的多个Thread,都共同拥有一个Runnable实例,共享同一份数据。

  如果采用继承Thread类的方法,就只好使用static静态成员了。如果共享的数据比较多,就需要大量的static静态成员,令程序数据结构混乱,难以扩展。这种情况应该尽量避免。

  编写一段多线程代码,处理一个稍微复杂点的问题。两种方法的优劣,一试便知。

  3.同步的粒度线程同步的粒度越小越好,即,线程同步的代码块越小越好。尽量避免用synchronized修饰符来声明方法。尽量使用synchronized(anObject)的方式,如果不想引入新的同步对象,使用synchronized(this)的方式。而且,synchronized代码块越小越好。

  4.线程之间的通知这里使用“通知”这个词,而不用“通信”这个词,是为了避免词义的扩大化。

  线程之间的通知,通过Object对象的wait()和notify() 或notifyAll() 方法实现。

  下面用一个例子,来说明其工作原理:

  假设有两个线程,A和B。共同拥有一个同步对象,lock。

  1.首先,线程A通过synchronized(lock) 获得lock同步对象,然后调用lock.wait()函数,放弃lock同步对象,线程A停止运行,进入等待队列。

  2.线程B通过synchronized(lock) 获得线程A放弃的lock同步对象,做完一定的处理,然后调用 lock.notify() 或者lock.notifyAll() 通知等待队列里面的线程A。

  3.线程A从等待队列里面出来,进入ready队列,等待调度。

  4.线程B继续处理,出了synchronized(lock)块之后,放弃lock同步对象。

  5.线程A获得lock同步对象,继续运行。

  例子代码如下:

  public class SharedResource implements Runnable{

  Object lock = new Object();

  public void run(){

  // 获取当前线程的名称。

  String threadName = Thread.currentThread().getName();

  if( “A”.equals(threadName)){

  synchronized(lock){ //线程A通过synchronized(lock) 获得lock同步对象

  try{

  System.out.println(“ A gives up lock.”);

  lock.wait(); // 调用lock.wait()函数,放弃lock同步对象,

  // 线程A停止运行,进入等待队列。

  }catch(InterruptedException e){   }   // 线程A重新获得lock同步对象之后,继续运行。

  System.out.println(“ A got lock again and continue to run.”);

  } // end of synchronized(lock)   }   if( “B”.equals(threadName)){

  synchronized(lock){//线程B通过synchronized(lock) 获得线程A放弃的lock同步对象

  System.out.println(“B got lock.”);

  lock.notify(); //通知等待队列里面的线程A,进入ready队列,等待调度。

  //线程B继续处理,出了synchronized(lock)块之后,放弃lock同步对象。

  System.out.println(“B gives up lock.”);

  } // end of synchronized(lock)

  boolean hasLock = Thread.holdsLock(lock); // 检查B是否拥有lock同步对象。

  System.out.println(“B has lock ? -- ” hasLock); // false.   }   }   }   public class TestMain{

  public static void main(){

  Runnable resource = new SharedResource();

  Thread A = new Thread(resource,”A”);

  A.start();

  // 强迫主线程停止运行,以便线程A开始运行。

  try {

  Thread.sleep(500);

  }catch(InterruptedException e){   }   Thread B = new Thread(resource,”B”);

  B.start();   }   }

   5.跨类的同步对象对于简单的问题,可以把访问共享资源的同步代码都放在一个类里面。

  但是对于复杂的问题,我们需要把问题分为几个部分来处理,需要几个不同的类来处理问题。这时,就需要在不同的类中,共享同步对象。比如,在生产者和消费者之间共享同步对象,在读者和写者之间共享同步对象。

  如何在不同的类中,共享同步对象。有几种方法实现,

  (1)前面讲过的方法,使用static静态成员,(或者使用Singleton Pattern.)

  (2)用参数传递的方法,把同步对象传递给不同的类。

  (3)利用字符串常量的“原子性”。

  对于第三种方法,这里做一下解释。一般来说,程序代码中的字符串常量经过编译之后,都具有唯一性,即,内存中不会存在两份相同的字符串常量。

  (通常情况下,C ,C语言程序编译之后,也具有同样的特性。)

  比如,我们有如下代码。

  String A = “atom”;

  String B = “atom”;

  我们有理由认为,A和B指向同一个字符串常量。即,A==B。

  注意,声明字符串变量的代码,不符合上面的规则。

  String C= new String(“atom”);

  String D = new String(“atom”);

  这里的C和D的声明是字符串变量的声明,所以,C != D。

  有了上述的认识,我们就可以使用字符串常量作为同步对象。

  比如我们在不同的类中,使用synchronized(“myLock”), “myLock”.wait(),“myLock”.notify(), 这样的代码,就能够实现不同类之间的线程同步。

  本文并不强烈推荐这种用法,只是说明,有这样一种方法存在。

  本文推荐第二种方法,(2)用参数传递的方法,把同步对象传递给不同的类。

猜你喜欢

转载自lvwenwen.iteye.com/blog/1456566