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内部的实现是如果运行的程序只剩下守护线程的话,程序将终止运行,直接结束。所以守护线程是作为辅助线程存在的,主要的作用是提供计数等等辅助的功能。下面写个小例子说明一下:
- /**
- * User: yanxuxin
- * Date: Dec 10, 2009
- * Time: 7:16:55 PM
- */
- public class DaemonSample {
- public static void main(String[] args) throws Exception{
- DaemonThread t = new DaemonThread();
- t.setDaemon(true);//this is set t thread as a daemon thread.
- t.start();
- Thread.sleep(2000);
- System.out.println("main thread exit.");
- }
- }
- class DaemonThread extends Thread {
- @Override
- public void run() {
- for(int i = 0; i < 10; i++) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("i=" + i);
- }
- }
- }
这个例子的结果是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{ } |
如果我们生成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
相关推荐
但如果存在守护线程,即使没有其他非守护线程,JVM也会继续运行,直到所有守护线程都完成执行。 创建守护线程的常见应用场景包括后台服务、资源清理、日志记录等。这些任务不直接影响程序的主要业务逻辑,但对程序...
首先,我们来了解什么是线程守护。线程守护在多线程编程中,通常指的是一个线程负责监控其他线程的运行状态。当被守护的线程结束时,守护线程会执行清理工作,确保资源的释放,防止内存泄漏。在Windows操作系统中,...
FreeRTOS线程守护看门狗 主要功能,建立复位定时器,所有加入守护的线程都必需在指定时间内发送心跳信号,超过时间定时器回调函数不会调用清空看门狗的函数 使用教程:...
在J2EE开发过程中经常由于内存溢出等多方面原因,导致tomcat服务器软件自动停止服务,从而影响了Web服务,本代码采用VB开发,主要是监控tomcat是否被关闭,保证tomcat的正常运行!
Java中的守护线程(Daemon Thread)是Java虚拟机(JVM)的一种特殊线程类型,它们在后台执行任务,主要用于为应用程序的其他非守护线程提供服务。守护线程的一个典型例子是垃圾回收器(Garbage Collector),它默默...
在Linux系统中,C++编程时常常需要创建守护线程(daemon thread)来执行特定的任务,比如监控系统状态、定时任务或确保某个服务始终运行。守护线程是一种长期运行的后台进程,它不依赖于终端会话,即使用户注销或者...
java daemon 程序,有助于理解守护线程的概念和使用
Java 的守护线程什么是Java的守护线程( Daemon Thread )?守护线程的区别?守护线程的作用?守护线程如何创建?守护线程如何判断?守护线程使用的注意事项总结 什么是Java的守护线程( Daemon Thread )? Java 提供...
很多人对java的线程不是很了解,甚至连守护线程都么有听过,其实很简单,守护线程就是普通的线程,区别再于,他就是类似一个秘书一样,记录着所有的非守护线程的信息等等,守护线程,是指为工作线程做相关统计、汇总...
在Java编程语言中,守护线程(Daemon Thread)被称为“变形金刚中的守护神”,这是因为它们在后台默默地工作,为应用程序提供必要的服务,而不会阻止程序的退出。理解并正确使用守护线程是Java多线程编程中的一个...
守护线程是一种特殊的线程,它在应用程序启动时创建,并在所有其他非守护线程结束后继续运行,直到应用程序关闭。在ASP.NET环境中,我们可以巧妙地运用守护线程来定期更新在线用户列表。 首先,我们需要创建一个...
"Java守护线程与用户线程详解" Java中的线程可以分为守护线程(DaemonThread)和用户线程(UserThread)两类。守护线程和用户线程的唯一区别是它们对虚拟机的退出(程序的终止)的影响。 当JVM中只有守护线程时,虚拟机会...
10. **什么是守护线程?** 守护线程是后台线程,当所有非守护线程结束时,守护线程也会自动结束。 11. **Java内存模型JMM是什么?** JMM规定了线程如何访问共享变量,确保并发程序的正确性。 12. **volatile...
浅谈java的守护线程与非守护线程 在Java中,有两类线程:UserThread(用户线程)和Daemon Thread(守护线程)。守护线程的作用是为其他线程的运行提供服务,比如说GC线程。它们的本质上来说没有区别,唯一的区别...
守护线程与普通线程的主要区别在于,当JVM中没有非守护线程(用户线程)在运行时,JVM会自动退出,即使仍有守护线程在工作。这使得守护线程常用于提供系统级的服务,例如垃圾回收(Garbage Collection)、内存管理等...
根据提供的信息,我们可以详细探讨如何在ASP.NET中实现在线用户检测功能,并且通过使用后台守护线程来确保这一过程能够持续有效地运行。 ### ASP.NET中实现在线用户检测 #### 背景介绍 在线用户检测是一项重要的...
在Java编程中,守护线程(Daemon Thread)是一种特殊的线程类型,它的主要作用是为应用程序提供后台服务,而不会阻止程序的退出。当所有的非守护线程(用户线程)结束时,JVM会自动关闭守护线程并退出。在本项目...
Java守护线程与非守护线程详解 Java 中有两类线程:User Thread(用户线程)和 Daemon Thread(守护线程)。用户线程即运行在前台的线程,而守护线程是运行在后台的线程。守护线程的作用是为其他前台线程的运行提供...
守护线程,也被称为后台线程,是Java编程语言中的一种特殊线程类型。它们的主要特点是在所有非守护线程(用户线程)结束后,守护线程才会被系统强制结束。在Windows环境下,通常我们使用服务来实现长期运行的任务,...
10. **什么是守护线程?** 守护线程是为其他线程提供服务的线程,JVM会在所有非守护线程结束后自动关闭守护线程。 以上只是Java多线程面试题的一部分,实际面试中可能还会涉及线程通信、线程中断、线程优先级、...