`
uule
  • 浏览: 6304966 次
  • 性别: Icon_minigender_1
  • 来自: 一片神奇的土地
社区版块
存档分类
最新评论

什么是线程?什么是守护线程?

阅读更多

http://itmian.com/2011/05/11/java%e9%9d%a2%e8%af%95%e9%a2%98-%e5%a4%9a%e7%ba%bf%e7%a8%8b/

 

Java程序员面试中的多线程问题:

http://sd.csdn.net/a/20120528/2806046.html

 

 

1、线程:
   一般我们开发的程序都是只有一个主线程的,即MAIN()函数开始,但是在一些特别的场合下,比如服务器接受命令的过程,可能需要同时处理多个客户端发送的命令,这时就需要征对每个客户建立一个线程 。这样建立多线程程序,多线程可以使得在同一时间间隔内,执行多个指令,以至于多个逻辑处理并发的运行。
   在JAVA中有二种方法可以定义一个线程:Runnable接口与Thread类,最终都是由Thread类的 start()方法启动线程,而真正执行的命令在run()方法中 ,另外线程有几种状态:执行,终止,休眠,挂起等。在某种条件下各个状态之间可以互相转换。

 

一个线程结束的标志是:run()方法结束。
 一个锁被释放的标志是:synchronized块或方法结束

值得注意的是:线程的在被激活后不一定马上就运行,而是进入到可运行线程的队列中。

2、进程和线程的区别

   通俗一点说,进程就是程序的一次执行,而线程可以理解为进程中的执行的一段程序片段。
   用一点文词说就是,每个进程都有独立的数据空间 (进程上下文);而线程可以看成是轻量级的进程。
   一般来讲(不使用特 殊技术),同一进程所产生的线程共享同一块内存空间

   同一进程中的两段代码是不可能同时执行的,除非引入线程。

   线程是属于进程的,当进程退出时该进程所产生的线程都会被强制退出并清除
   线程占用的资源要少于进程所占用的资源。
   进程和线程都可以有优先级。
   在线程系统中进程也是一个线程。可以将进程理解为一个程序的第一个线程。

   多进程-----在操作系统中,能同时运行多个任务(程序)。

   多线程-----在同一应用程序中,有多个顺序流同时执行。
    

3、两种线程模型

  
 

4、实现Runnable接口相对于继承Thread类来说,有如下显著的好处:

(1)适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码,数据有效的分离,较好地体现了面向对象的设计思想。

(2)可以避免由于Java的单继承特性带来的局限。我们经常碰到这样一种情况,即当我们要将已经继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable接口的方式了。

(3)有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的 。当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程操作相同的数据,与它们的代码无关。当共享访问相同的对象时,即它们共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造函数 实参传递进去,这个对象就是一个实现了Runnable接口 的类的实例。

 

通过铁路售票程序来理解实现多线程:
用代码来模拟铁路售票系统,实现通过四个售票点发售某日某次列车的100张车票,一个售票点用一个线程表示。

1、一个线程对象只能启动一个线程,无论你调用多少遍start()方法,结果只有一个线程。

 

class ThreadTest extends Thread{
     private int ticket = 100;
     public void run(){
       while(true){
         if(ticket > 0){
            System.out.println(Thread.currentThread().getName() +
              "is saling ticket" + ticket--);
         }else{
           break;
         }
      }
   }
}

public class ThreadDome1{
  public static void main(String[] args){
    ThreadTest t = new ThreadTest();
      t.start();
      t.start();
      t.start();
      t.start();
   }
}

 在ThreadDemo1类的main方法中,我们创建了一个线程对象,并重复启动四次,希望通过这种方式产生四个线 程。从运行的结果来看我们发现其实只有一个线程在运行,这个结果告诉我们:一个线程对象只能启动一个线程,无论你调用多少遍start()方法,结果只有 一个线程。

 

2、

class ThreadTest extends Thread{
  private int ticket = 100;
  
  public void run(){
      while(true){
		  if(ticket > 0){
		  System.out.println(Thread.currentThread().getName() +
			  " is saling ticket" + ticket--);
		  }else{
			 break;
		  }
		}
	}
}

public class ThreadDemo1{
  public static void main(String[] args){
    new ThreadTest().start();
    new ThreadTest().start();
    new ThreadTest().start();
    new ThreadTest().start();
 }
}

 这下达到目的了吗?

 

从结果上看每个票号都被打印了四次,即四个线程各自卖各自的100张票,而不去卖共同的100张票。这种情况是怎么造成的呢?我们需要的是,多个线程去处 理同一个资源,一个资源只能对应一个对象,在上面的程序中,我们创建了四个ThreadTest对象,就等于创建了四个资源,每个资源都有100张票,每 个线程都在独自处理各自的资源。

 

经过这些实验和分析,可以总结出,要实现这个铁路售票程序,我们只能创建一个资源对象,但要创建多个线程去处理同一个资源对象,并且每个线程上所运行的是相同的程序代码。

 

3、

//注意为Runnable
class ThreadTest implements Runnable{
  private int tickets = 100;
  public void run(){
     while(true){
     if(tickets > 0){
       System.out.println(Thread.currentThread().getName() +
          " is saling ticket " + tickets--);
      }
    }
  }
}

public class ThreadDemo1{
  public static void main(String[] args){
  
     ThreadTest t = new ThreadTest();
	 
     new Thread(t).start();
     new Thread(t).start();
     new Thread(t).start();
     new Thread(t).start();
  }
}

 上面的程序中,创建了四个线程,每个线程调用的是同一个Runnable对象中的run()方法,访问的是同一个对象中的变量 (tickets)的实例,这个程序满足了我们的需求

在Windows上可以启动多个记事本程序一样,也就是多个进程使用同一个记事本程序代码。

 

 https://www.cnblogs.com/victory8023/p/5549284.html

JAVA多线程基础:http://tomyz0223.iteye.com/blog/1001778

 

 

关于守护线程:

      对于Thread来说只想说两个方法,一个是setDaemon(false|true),另一个是join()。首先说说守护线程,这么东西是干什么用的?对于Java应用我们都知道main方法是入口,它的运行代表着主线程开始工作了,我们也知道JVM里面有垃圾回收器的存在使得我们放心让main飞奔,然而这背后的故事是垃圾回收线程作为守护着主线程的守护线程默默的付出着。。。很像那个啥啊,呵呵。令人发指的是main这个畜生背后其实有好几个守护线程默默的付出!当然如果硬是要把守护线程比做女人,非守护线程比做男人的话,那么一个男人背后可以有多个默默付出的女人。Java在设计时就默认了这个现实社会不太能实现的现实,并且强制规定如果男人不在了,这个虚拟世界就over了,看来资本主义社会下长大的孩子创造的东西骨子里是封建社会的血脉啊!扯远了,对的,JVM内部的实现是如果运行的程序只剩下守护线程的话,程序将终止运行,直接结束。所以守护线程是作为辅助线程存在的,主要的作用是提供计数等等辅助的功能。下面写个小例子说明一下: 

Java代码   收藏代码
  1. /** 
  2.  * User: yanxuxin 
  3.  * Date: Dec 10, 2009 
  4.  * Time: 7:16:55 PM 
  5.  */  
  6. public class DaemonSample {  
  7.     public static void main(String[] args) throws Exception{  
  8.         DaemonThread t = new DaemonThread();  
  9.         t.setDaemon(true);//this is set t thread as a daemon thread.  
  10.         t.start();  
  11.         Thread.sleep(2000);  
  12.         System.out.println("main thread exit.");  
  13.     }  
  14. }  
  15.   
  16. class DaemonThread extends Thread {  
  17.     @Override  
  18.     public void run() {  
  19.         for(int i = 0; i < 10; i++) {  
  20.             try {  
  21.                 Thread.sleep(1000);  
  22.             } catch (InterruptedException e) {  
  23.                 e.printStackTrace();  
  24.             }  
  25.             System.out.println("i=" + i);  
  26.         }  
  27.     }  
  28. }  


这个例子的结果是main主线程睡两秒之后说再见,而子线程则是在这两秒内计计数然后跟着一起说再见。当然它很不情愿,因为它想计10秒,但是没机会。把t.setDaemonThread(true)注释掉你会看到主线程说再见了,但是子线程快快乐乐的计完数说再见。这就是守护线程的作用,一切以非守护线程为主!很痴情啊,哈哈。 

来源:http://yanxuxin.iteye.com/blog/547266

 

 

两种线程对象的区别:

在单CPU系统中,系统调度在某一时刻只能让一个线程运行,虽然这种调试机制有多种形式(大多数是时间片轮巡为主),但无论如何,要通过不断切换需要运行的线程让其运行的方式就叫并发(concurrent)。而在多CPU系统中,可以让两个以上的线程同时运行,这种可以同时让两个以上线程同时运行的方式叫做并行(parallel)。

  在JAVA中,要开始一个线程,有两种方式。一是直接调用Thread实例的start()方法,二是将Runable实例传给一个Thread实例然后调用它的start()方法。

  在前面已经说过,线程对象和线程是两个完全不同的概念。这里我们再次深入一下,生成一个线程的实例,并不代表启动了线程。而启动线程是说在某个线程对象上启动了该实例对应的线程,当该线程结束后,并不会就立即消失。

本节重点要说的是两种线程对象产生线程方式的区别。

class MyThread extends Thread{
 public int x = 0;
 public void run(){
  for(int i=0;i<100;i++){
    try{
     Thread.sleep(10);
    }catch(Exception e){

          }
    System.out.println(x++);
  }
 }
}


  如果我们生成MyThread的一个实例,然后调用它的start()方法,那么就产生了这个实例对应的线程:

public class Test {
 public static void main(String[] args) throws Exception{
  MyThread mt = new MyThread();
  mt.start();
 }
}


  不用说,最终会打印出0到99,现在我们稍微玩一点花样:

public class Test {
 public static void main(String[] args) throws Exception{
  MyThread mt = new MyThread();
  mt.start();
  System.out.println(101);
 }
}


  也不用说,我们知道由于单CPU的原因,一般会先打印101,然后打印0到99。不过我们可以控制线程让它按我们的意思来运行:

public class Test {
 public static void main(String[] args) throws Exception{
  MyThread mt = new MyThread();
  mt.start();
  mt.join();
  System.out.println(101);
 }
}


  好了,我们终于看到,mt实例对应的线程(假如我有时说mt线程请你不要怪我,不过我尽量不这么说)。在运行完成后,主线程才打印101。因为我们让当前线程(这里是主线程)等待mt线程的运行结束。"在线程对象a上调用join()方法,就是让当前正在执行的线程等待线程对象a对应的线程运行完成后才继续运行。" 请大家一定要深刻理解并熟记这句话,而我这里引出这个知识点的目的是为了让你继续看下面的例子:

public class Test {
 public static void main(String[] args) throws Exception{
  MyThread mt = new MyThread();
  mt.start();
  mt.join();
  Thread.sleep(3000);
  mt.start();
 }
}


  当线程对象mt运行完成后,我们让主线程休息一下,然后我们再次在这个线程对象上启动线程。结果我们看到:

  Exception in thread "main" java.lang.IllegalThreadStateException

  也就是这种线程对象一时运行一次完成后,它就再也不能运行第二次了。我们可以看一下它有具体实现:

public synchronized void start() {
 if (started)
  throw new IllegalThreadStateException();
  started = true;
  group.add(this);
  start0();
 }


  一个Thread的实例一旦调用start()方法,这个实例的started标记就标记为true,事实中不管这个线程后来有没有执行到底,只要调用了一次start()就再也没有机会运行了,这意味着:

  [通过Thread实例的start(),一个Thread的实例只能产生一个线程]

  那么如果要在一个实例上产生多个线程(也就是我们常说的线程池),我们应该如何做呢?这就是Runnable接口给我们带来的伟大的功能。

class R implements Runnable{
 private int x = 0;
 public void run(){
  for(int i=0;i<100;i++){
    try{
     Thread.sleep(10);
    }catch(Exception e){}
    System.out.println(x++);
  }
 }
}


  正如它的名字一样,Runnable的实例是可运行的,但它自己并不能直接运行,它需要被Thread对象来包装才行运行:

public class Test {
 public static void main(String[] args) throws Exception{
  new Thread(new R()).start();
 }
}


  当然这个结果和mt.start()没有什么区别。但如果我们把一个Runnable实例给Thread对象多次包装,我们就可以看到它们实际是在同一实例上启动线程:

public class Test {
 public static void main(String[] args) throws Exception{
  R r = new R();
  for(int i=0;i<10;i++)
    new Thread(r).start();
 }
}


  x是实例对象,但结果是x被加到了999,说明这10个线程是在同一个r对象上运行的。请大家注意,因为这个例子是在单CPU上运行的,所以没有对多个线程同时操作共同的对象进行同步。这里是为了说明的方便而简化了同步,而真正的环境中你无法预知程序会在什么环境下运行,所以一定要考虑同步。

  到这里我们做一个完整的例子来说明线程产生的方式不同而生成的线程的区别:

package debug;
import java.io.*;
import java.lang.Thread;
class MyThread extends Thread{
 public int x = 0;
 public void run(){
  System.out.println(++x);
 }
}

class R implements Runnable{
 private int x = 0;
 public void run(){
  System.out.println(++x);
 }
}
public class Test {
 public static void main(String[] args) throws Exception{
  for(int i=0;i<10;i++){
    Thread t = new MyThread();
    t.start();
  }
  Thread.sleep(10000);//让上面的线程运行完成
  R r = new R();
  for(int i=0;i<10;i++){
    Thread t = new Thread(r);
    t.start();
  }
 }  
}


  上面10个线程对象产生的10个线程运行时打印了10次1。下面10个线程对象产生的10个线程运行时打印了1到10。我们把下面的10个线程称为同一实例(Runnable实例)的多个线程。

来源:http://dev.yesky.com/186/2547686.shtml

 

 

 

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics