Java多线程高并发基础篇(三)-线程的生命周期及基本操作举例

在说线程之前,有必要说下线程与进程之间的关系。

附oracle官网链接:https://docs.oracle.com/javase/tutorial/essential/concurrency/procthread.html

这里有原版的关于线程和进程的介绍,我们只简单介绍下,作为知识普及。

一.进程与线程的关系

1.进程:进程一般情况下是程序或者应用程序的同义词,是系统进行资源和调度的基本单位,是操作系统结构的基础。它有自己独立的内存空间,换句话说,它有一套私有的运行时资源。在现代面向线程的计算机结构中,进程作为了线程的容器存在,而进程变为了程序的代名词。但是要注意的是,Java虚拟机的大多数实现都是作为一个进程运行的。

2.线程:在面向线程的计算机结构中,线程有时被称为轻量级的进程。在进程中,各个线程共享进程的资源(内存和文件)。每个应用程序都至少包含一个线程。

3.为什么要使用线程进行并发程序设计?

因为相对于进程来说,线程的切换和调度的成本远远小于进程。

二.线程的生命周期

既然要说线程,就免不了说下线程的生命周期,也就是各个状态之间的转换。



 



 可以看到,在源码Thread类中定义了一个枚举类型State,这个State就是线程生命周期中的各种状态。

根据英文注释,我们可以知道各个状态的含义,这里不细说。

附线程的状态转换图:

后续篇我们逐渐详述介绍这几种状态之间的转换过程。


 三.线程的基本操作(这些都很简单,不在这里详细说)

1.新建线程

新建一个线程有两种方式:一种是继承Thread类,并重写Thread类的run方法(因为你要在run方法中实现你的业务逻辑);另外一种是实现Runnable接口。

public class ThreadDemo extends Thread {
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		System.out.println("继承Thread实现线程执行");
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		new ThreadDemo().start();
	}

}
 
public class RunnableDemo implements Runnable {

	@Override
	public void run() {
		System.out.println("我的第一个线程程序!");
	}

	public static void main(String[] args) {
		new Thread(new RunnableDemo()).start();
	}

}
 其实通过源码来看,实际就是一种方式,直接实现Runnable接口。我们可以看下Thread源码类中:  所以关于如何新建线程的方式的讨论可以休矣。 2.废弃方法(可以不看了) stop方法:暴力终止,会出现数据不一致,废弃也在情理之中,被带有中断机制的方法代替了。 suspend:挂起方法,也被废弃了,是因为调用此方法后,线程挂起后不释放资源,并且线程的状态是RUNNABLE,可能会让我们误判当前的系统状态。  resume:是与suspend配套使用的,但是如果它比suspend先执行,那线程永远挂着了,图就不截了。 3.中断方法: 有关中断的有三个方法: ①Thread类中的interrupt()方法是实例方法,它可以设置线程的中断标识(也就是说并不是调用这个方法就中断了) ②配合Thread类中的实例方法isInterrupted()来判断当前调用线程是否设置了中断标识。 ③Thread类中的interrupted()也是用来判断当前的中断状态,但是 会清除中断标识位
public static void main(String[] args) throws InterruptedException {
		Thread t = new Thread(){
			@Override
			public void run() {
				while(true){
		System.out.println(Thread.currentThread().interrupted());
					if(Thread.currentThread().isInterrupted()){
						System.out.println("我被中断了!");
						break;
					}
				}
			}
		};
		t.start();
		t.interrupt();
	}
 另外要注意的是,使用Thread.sleep(long xx)方法时(还有join方法,凡是报中断异常的方法),如果线程被中断,会抛出中断异常,并且标志位也会被清除。
public static void main(String[] args) throws InterruptedException {
		Thread t = new Thread(){
			@Override
			public void run() {
				while(true){
					try {
						System.out.println(Thread.currentThread().getName()+"|"+Thread.currentThread().isInterrupted());
						Thread.sleep(1000);
						System.out.println(Thread.currentThread().getName()+"|"+Thread.currentThread().isInterrupted());
					} catch (InterruptedException e) {
						e.printStackTrace();
						//如果要正确处理逻辑,这里要恢复中断标志位,重新进行中断
						//重新设置中断标志
						Thread.currentThread().interrupt();
					}
					if(Thread.currentThread().isInterrupted()){
						System.out.println("我被中断了!");
						break;
					}
				}
			}
		};
		t.start();
		t.interrupt();
	}
  4.等待(wait)和通知(notify) 首先要知道,这两方法是Object类中的方法,也就是说任何类都继承了这两方法。 在一个线程中,当一个对象调用wait()方法后,当前线程就会等待在这个对象上。直到另外一个线程调用了obj.notify()方法后,才能解除等待。要注意的第一点,这里的对象是同一个!第二点,这个对象必须被用作监视器锁!第三点,等待的线程必须获得了监视器锁后才能执行,并不是说另一个线程执行了notify()方法后就能被唤醒了!  
public class WaitNotifyDemo{
	final static Object obj = new Object();
	static class T1  extends Thread {
		@Override
		public void run() {
			synchronized (obj) {
				System.out.println(System.currentTimeMillis()+":T1 start!");
				try {
					obj.wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(System.currentTimeMillis()+":T1 end!");
			}
		}
	}
	static class T2  extends Thread {
		@Override
		public void run() {
			synchronized (obj) {
				System.out.println(System.currentTimeMillis()+":T2 start!");
				obj.notify();
				try {
					Thread.sleep(2000);//为了看的明显,等待2s
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(System.currentTimeMillis()+":T2 end!");
			}
		}
	}
	public static void main(String[] args) {
		new T1().start();
		new T2().start();
	}

}
   5.线程的加入(join)和谦让(yield) 线程的加入:在一个线程A中调用线程B的join()方法,那么线程A就必须等线程B的逻辑执行完成后,才能继续往下执行自己A的逻辑。加入实际就是一起走的意思!下面例子,最后i肯定是100000.
public class ThreadJoinDemo{
	public volatile static int i = 0;
	static class T1  extends Thread {
		@Override
		public void run() {
			for(i=0;i<100000;i++);
		}
	}
	public static void main(String[] args) throws InterruptedException {
		T1 t1 = new T1();
		t1.start();
		t1.join();
		System.out.println(i);
	}

}
 线程资源让出-yield:Thread.yield()是一个静态的本地方法。让出的意思很明确,让出CPU资源。但是,让出了我还要争夺的!  四.线程的分类管理 1.线程的分组-线程组 给线程分个组,起个好听的名字吧,方便管理,默认新建的线程所在的线程组是创建线程的父组。
public class ThreadGroupDemo implements Runnable{

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ThreadGroup tg = new ThreadGroup("zhaodf");
		Thread t1 = new Thread(tg, new ThreadGroupDemo(), "T1");
		Thread t2 = new Thread(tg, new ThreadGroupDemo(), "T2");
		Thread t3 = new Thread(new ThreadGroupDemo());
		t1.start();
		t2.start();
		t3.start();
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println(Thread.currentThread().getThreadGroup().getName()+"_"+Thread.currentThread().getName());
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
  2.守护线程:守护线程守护的是谁?守护的是用户线程,用户线程不存在了,它就没有意义了,因此也就停掉了。
public class Daemon {
	public static class DaemonT extends Thread{
		@Override
		public void run() {
			while(true){
				System.out.println(System.currentTimeMillis()+"_"+Thread.currentThread().getName()+"_i am alive");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
	
	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		Thread t = new DaemonT();
		//必须在start方法之前设置守护线程,这样t变为守护线程,而主线程main成为用户线程。当主线程休眠2秒后,守护线程也退出
		//如果放在start之后,会抛出Exception in thread "main" java.lang.IllegalThreadStateException异常
		t.setDaemon(true);
		t.start();
		System.out.println(System.currentTimeMillis()+"_"+"主线程名称:"+Thread.currentThread().getName());
		Thread.sleep(2000);
	}

}
   3.线程的优先级: 线程的优先级并不能保证线程优先执行,这点要注意。
public class PriorityDemo{

	public static class HightPriority extends Thread{
		static int count = 0;
		@Override
		public void run() {
			// TODO Auto-generated method stub
			while(true){
				synchronized (PriorityDemo.class) {
					count++;
					if(count>10000){
						System.out.println("HightPriority is complete");
						break;
					}
				}
			}
		}

	}
	
	public static class LowerPriority extends Thread{
		static int count = 0;
		@Override
		public void run() {
			// TODO Auto-generated method stub
			while(true){
				synchronized (PriorityDemo.class) {
					count++;
					if(count>10000){
						System.out.println("LowerPriority is complete");
						break;
					}
				}
			}
		}
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Thread high = new HightPriority();
		Thread low = new LowerPriority();
		//设置优先级并不能保证线程一定优先执行
		high.setPriority(Thread.MAX_PRIORITY);
		low.setPriority(Thread.MIN_PRIORITY);
		high.start();
		low.start();
	}

}
 

猜你喜欢

转载自zhaodengfeng1989.iteye.com/blog/2415834