【JAVA秒会技术之玩转多线程】多线程那些事儿(一)

多线程那些事儿(一)

    现在只要出去面试,关于“Java多线程”的问题,几乎没有一家单位不问的,可见其重要性。于是博主抽空研究了一下,确实很有意思!以下是我综合整理了网上的各种资料,和个人的一些理解,写的一篇总结博文,仅供学习、交流。

(一)多线程的概念

        多线程的概念,简单理解:一个进程运行时产生了不止一个线程

        进程的概念,简单理解:正在运行的程序的实例

两者之间的关系:进程就是线程的容器

(二)多线程的好处

        使用多线程可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,这样就大大提高了程序的效率。

(三)线程状态转换

 

1新建状态(New):新创建了一个线程对象。

2就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。

3运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

4阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:

        ①等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。

        ②同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。

        ③其他阻塞:运行的线程执行sleep()join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

(五)创建线程的方式

       创建线程的方式主要有两种:

  (1)一种是继承Tread

package com.liyan.Test;
public class MyThread extends Thread {  //继承Tread类
	@Override
	public void run(){                      //重写run方法
		try{
			.......
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		MyThread myThread = new MyThread();  //产生新的线程对象
		myThread.start();                        //开启线程
	}	
}


  (2)一种是实现Runnable接口

package com.liyan.gcTest;
public class MyThread implements Runnable { //实现Runnable接口
	@Override
	public void run(){   //重写run方法
		try{
			.......
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		MyThread myThread = new MyThread();    //产生新的线程对象
		Thread thread = new Thread(myThread);  //将创建的线程作为参数传入
		thread.start();                            //开启线程
	}	
}


(六)终止线程的方式

   1. 使用退出标志终止线程

       run方法执行完后,线程就会退出。但有时run方法是永远不会结束的。如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。如果想让循环永远运行下去,可以使用whiletrue{……}来处理。但要想使while循环在某一特定条件下退出,最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为truefalse来控制while循环是否退出。下面给出了一个利用退出标志终止线程的例子。

package com.liyan.thread;
public class ThreadDemo extends Thread {
	public volatile boolean exit = false;
	public void run() {
		while (!exit);
	}

	public static void main(String[] args) throws Exception {
		ThreadDemo thread = new ThreadDemo();
		thread.start();
		sleep(5000); // 主线程延迟5秒
		thread.exit = true; // 终止线程thread
		thread.join();
		System.out.println("线程退出!");
	}
}

2. 使用stop方法终止线程   在上面代码中定义了一个退出标志exit,当exittrue时,while循环退出,exit的默认值为false.在定义exit时,使用了一个Java关键字volatile,这个关键字的目的是使exit同步,也就是说在同一时刻只能由一个线程来修改exit的值

    使用stop方法可以强行终止正在运行或挂起的线程。我们可以使用如下的代码来终止线程:

thread.stop();

3. 使用interrupt方法终止线程    虽然使用上面的代码可以终止线程,但使用stop方法是很危险的,就象突然关闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,因此,并不推荐使用stop方法来终止线程。

    使用interrupt方法来终端线程可分为两种情况:

   (1)线程处于阻塞状态,如使用了sleep方法。

   (2)使用while(!isInterrupted()){……}来判断线程是否被中断。

    在第一种情况下使用interrupt方法,sleep方法将抛出一个InterruptedException例外,在第二种情况下线程将直接退出。下面的代码演示了在第一种情况下使用interrupt方法。

package com.liyan.thread;
public class ThreadDemo extends Thread {
	public void run() {
		try {
			sleep(50000); // 延迟50秒
		} catch (InterruptedException e) {
			System.out.println(e.getMessage());
		}
	}

	public static void main(String[] args) throws Exception {
		Thread thread = new ThreadDemo();
		thread.start();
		System.out.println("在50秒之内按任意键中断线程!");
		System.in.read();
		thread.interrupt();
		thread.join();
		System.out.println("线程已经退出!");
	}
}

    输出结果:

在50秒之内按任意键中断线程!
liyan
sleep interrupted
线程已经退出!

    在调用interrupt方法后,sleep方法抛出异常,然后输出错误信息:sleep interrupted.

    注意:在Thread类中有两个方法可以判断线程是否通过interrupt方法被终止。一个是静态的方法interrupted(),一个是非静态的方法isInterrupted(),这两个方法的区别是interrupted用来判断当前线是否被中断,而isInterrupted可以用来判断其他线程是否被中断。因此,while(!isInterrupted())也可以换成while(!Thread.interrupted())。

   【参考:http://www.bitscn.com/pdb/java/200904/161228_3.html

(七)Daemon线程

    在Java中有两类线程:User Thread(用户线程)Daemon Thread(守护线程) 

    用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆:

    只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。

        Daemon的作用是为其他线程的运行提供便利服务,它的优先级比较低,用于为系统中的其它对象和线程提供服务。守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”

    守护线程最典型的应用就是GC (垃圾回收器),当我们的程序中不再有任何运行的hread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是JVM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。

         UserDaemon两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。

  // 设定 daemonThread 为 守护线程,default false(非守护线程)
  daemonThread.setDaemon(true);
	
   // 验证当前线程是否为守护线程,返回 true 则为守护线程
   daemonThread.isDaemon();

   (1thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。      这里有几点需要注意

   (2Daemon线程中产生的新线程也是Daemon

   (3不要认为所有的应用都可以分配给Daemon来进行服务,比如读写操作或者计算逻辑因为你不可能知道在所有的User完成之前,Daemon是否已经完成了预期的服务任务。一旦User退出了,可能大量数据还没有来得及读入或写出,计算任务也可能多次运行结果不一样。这对程序是毁灭性的。造成这个结果理由已经说过了:一旦所有User Thread离开了,虚拟机也就退出运行了。

   【参考:http://blog.csdn.net/shimiso/article/details/8964414

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值