多线程入门(五)

一、线程组

1.Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。

2.
默认情况下,所有的线程都属于主线程组。
•public final ThreadGroup getThreadGroup()

我们也可以给线程设置分组(创建Thread类时用构造方法)
•Thread(ThreadGroup group, Runnable target,String name) 

3.统一操控(举例,直接拿ThreadGroup的方法设置里面的全部线程
ThreadGroup tg = new ThreadGroup("这是一个新的组");
//通过组名称设置后台线程,表示该组的线程都是后台线程
tg.setDaemon(true);

二、线程池


程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。一定初始大小数量线程们


1.线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。

2.在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池


JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法

public static ExecutorService newCachedThreadPool()

public static ExecutorService newFixedThreadPool(int nThreads)

public static ExecutorService newSingleThreadExecutor()

这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法

•Future<?> submit(Runnabletask)

•<T> Future<T>submit(Callable<T> task)

案例演示

创建线程池对象

创建Runnable实例

提交Runnable实例

关闭线程池

/*
 * 线程池的好处:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
 * 
 * 如何实现线程的代码呢?
 * 		A:创建一个线程池对象,控制要创建几个线程对象。
 * 			public static ExecutorService newFixedThreadPool(int nThreads)
 * 		B:这种线程池的线程可以执行:
 * 			可以执行Runnable对象或者Callable对象代表的线程
 * 			做一个类实现Runnable接口。
 * 		C:调用如下方法即可
 * 			Future<?> submit(Runnable task)
 *			<T> Future<T> submit(Callable<T> task)
 *		D:我就要结束线程池结束使用,可以吗?
 *			可以。
 */
public class ExecutorsDemo {
	public static void main(String[] args) {
		// 创建一个线程池对象,控制要创建几个线程对象。
		// public static ExecutorService newFixedThreadPool(int nThreads)
		ExecutorService pool = Executors.newFixedThreadPool(2);

		// 可以执行Runnable对象或者Callable对象代表的线程
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());

		//结束线程池使用
		pool.shutdown();
	}
}



三、多线程程序实现方案3(了解)

实现Callable接口

//Callable:是带泛型的接口。
//这里指定的泛型其实是call()方法的返回值类型。没写泛型就默认是Object如下
public class MyCallable implements Callable {

	@Override
	public Object call() throws Exception {
		for (int x = 0; x < 100; x++) {
			System.out.println(Thread.currentThread().getName() + ":" + x);
		}
		return null;
	}

}

public class CallableDemo {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		// 创建线程池对象
		ExecutorService pool = Executors.newFixedThreadPool(2);

		// 可以执行Runnable对象或者Callable对象代表的线程
		Future<Integer> f1 = pool.submit(new MyCallable(100));//获取异步结果
		Future<Integer> f2 = pool.submit(new MyCallable(200));

		// V get()
		Integer i1 = f1.get();//获取异步结果
		Integer i2 = f2.get();

		System.out.println(i1);
		System.out.println(i2);

		// 结束
		pool.shutdown();
	}
}



这种实现多线程的好处:

•可以有返回值

•可以抛出异常

弊端:

依赖于线程池存在的,且代码比较复杂,所以一般不用

四、平时最常见的线程使用方法

/*
 * 匿名内部类的格式:
 * 		new 类名或者接口名() {
 * 			重写方法;
 * 		};
 * 		本质:是该类或者接口的子类对象。
 */
public class ThreadDemo {
	public static void main(String[] args) {
		// 继承Thread类来实现多线程
		new Thread() {
			public void run() {
				for (int x = 0; x < 100; x++) {
					System.out.println(Thread.currentThread().getName() + ":"
							+ x);
				}
			}
		}.start();

		// 实现Runnable接口来实现多线程
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int x = 0; x < 100; x++) {
					System.out.println(Thread.currentThread().getName() + ":"
							+ x);
				}
			}
		}) {
		}.start();

		// 更有难度的(只作了解),里面有两个run方法分别是Runnable接口的和Thread子类对象的,那么到底执行谁的run方法?答案是执行Thread子类对象的run
		new Thread(new Runnable() {
			@Override
			public void run() {
				for (int x = 0; x < 100; x++) {
					System.out.println("hello" + ":" + x);
				}
			}
		}) {
			public void run() {
				for (int x = 0; x < 100; x++) {
					System.out.println("world" + ":" + x);
				}
			}
		}.start();
	}
}


五、定时器


定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能

Timer

public Timer()

public void schedule(TimerTask task, long delay)

public void schedule(TimerTask task,long delay,long period)

TimerTask

public abstract void run()

public boolean cancel()

开发中

Quartz是一个完全由java编写的开源调度框架。

/*
 * 定时器:可以让我们在指定的时间做某件事情,还可以重复的做某件事情。
 * 依赖Timer和TimerTask这两个类:
 * Timer:定时
 * 		public Timer()
 * 		public void schedule(TimerTask task,long delay)
 * 		public void schedule(TimerTask task,long delay,long period)
 * 		public void cancel()
 * TimerTask:任务
 */
public class TimerDemo {
	public static void main(String[] args) {
		// 创建定时器对象
		Timer t = new Timer();
		// 3秒后执行爆炸任务
		// t.schedule(new MyTask(), 3000);
		//结束任务
		t.schedule(new MyTask(t), 3000);
	}
}

// 做一个任务
class MyTask extends TimerTask {

	private Timer t;
	
	public MyTask(){}
	
	public MyTask(Timer t){
		this.t = t;
	}
	
	@Override
	public void run() {
		System.out.println("beng,爆炸了");
		t.cancel();
	}

}


六、多线程常见的面试题

1:多线程有几种实现方案,分别是哪几种?

两种。

继承Thread类

实现Runnable接口

扩展一种:实现Callable接口。这个得和线程池结合。



2:同步有几种方式,分别是什么?

两种。

同步代码块

同步方法



3:启动一个线程是run()还是start()?它们的区别?

start();

run():封装了被线程执行的代码,直接调用仅仅是普通方法的调用

start():启动线程,并由JVM自动调用run()方法



4:sleep()和wait()方法的区别

sleep():必须指时间;不释放锁。

wait():可以不指定时间,也可以指定时间;释放锁。



5:为什么wait(),notify(),notifyAll()等方法都定义在Object类中

因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。

而Object代码任意的对象,所以,定义在这里面。



6:线程的生命周期图

新建 -- 就绪 -- 运行 -- 死亡

新建 -- 就绪 -- 运行 -- 阻塞 -- 就绪 -- 运行 -- 死亡

建议:画图解释。

posted @ 2017-11-06 14:11  词汇族  阅读(135)  评论(0编辑  收藏  举报