java程序员从笨鸟到菜鸟之(四十三)线程之高级特性

线程组

ThreadGroup类表示线程组,位于java.lang包下,线程组表示一个线程的集合。特点:能够对一组线程进行管理

Thread类与线程组有关的种构造方法

(1)public Thread(ThreadGroup group,String name)

说明:在创建线程对象的时候,同时指定线程所属的线程组;后面的name是线程的名字而不是线程组的名字

注意1:此种方式不能直接创建线程对象,通过子类创建,并通过子类的构造方法中调用父类此方法

注意2:一旦线程加入到某个线程组,不能在中途改变线程所属的线程组,该线程就一直存在在该线程组中,直至线程消亡

(2)public Thread(ThreadGroup group,Runnable target ,String name)

说明:创建线程类对象,并且将线程组对象作为参数进行传递,后面的name是线程的名字---可以直接创建Thread类的对象

线程组的构造方法

(1)public ThreadGroup(String name):

说明:构造一个新线程组,并同时给线程组起名字

(2)public ThreadGroup(ThreadGroup parent,String name)

说明:不常用,回头补充

线程组常用的方法

(1)setDaemon(boolean daemon)

说明:当daemon是true,将线程组中的所有的线程都设置为守护线程(后台线程)

说明:此类不常用,了解即可;比较有用的方法是uncaughetException()方法

concurrent 并发包

由来:位于java.util下,由于编写多线程的程序代码时,即要保证线程同步,又要避免死锁,还要考虑并发性能,为了造福广大的程序员能编写高效的多线程代码,从JDK5开始,增加了此并发包

此包的主要内容:

(1)用于线程同步的Lock外部锁接口

(2)用于支持线程通信的Condition条件接口----有时间了再补充

(3)支持异步运算的Callable接口和Future接口

先验知识:异步运算--线程1负责运算,线程2等待线程1的运算结果

线程同步的Lock外部锁

前面我们已经提到过每个Java对象都有一个用于同步的锁,但是此锁只是概念上的锁,我们称此锁为Java对象的内部锁

Lock位于java.util.cuncurrent.locks包中,Lock接口及其子类专门实现表示用于同步的锁

两种锁比较

(1)内部锁:使用同步机制的这种方式解决线程安全问题,但是不知道具体的锁对象在哪里添加,并且锁对象在哪里释放锁对象

(2)外部锁Lock:更灵活的获取同步代码块锁的

由于Lock只是个接口,要创建锁对象,必须通过它的子实现类,通常为ReentrantLock

无参构造方式:ReentrantLock()

说明:创建常规的锁

常用的方法

(1)public void lock()

说明:当前线程获取同步代码块锁对象;如果锁对象被其它线程占用,进入阻塞状态(对象锁池)---处理机制类比java对象的内部锁

(2)public void unlock():试图释放此锁

说明:释放线程所占用的同步代码块的锁

明确一点:这两个方法与try-catch-finallly结合

实例1 以卖票为例

package org.westos_01;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicket implements Runnable {
	
	//定义一个100张票
	private int tickets = 100 ;
	
	//定义一个具体锁对象
	private  Lock lock = new ReentrantLock() ;//具体的lock锁
	
	//捕获异常标准格式:try...catch...finally
	//变形格式:try...finally...
	/**
	 * try{
	 * 	可能出现问题的代码
	 * }catch(SocketException e){
	 * 	//不需要进行处理
	 *  //空处理
	 * }
	 */
	@Override
	public void run() {
		
		//模拟电影院一直有票
		while(true){
			//同步机制
//			synchronized(obj){
			
			try{
				//获取锁
				lock.lock() ;
				if(tickets>0){
					//加入延迟操作
					try {
						Thread.sleep(100) ;
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
				}
			}finally{
				//试图释放锁对象
				lock.unlock() ;
			}
			
//			}
			
		}
	}

}
测试类

package org.westos_01;

public class SellTicketDemo {
	
	public static void main(String[] args) {
		
		//创建资源对象
		SellTicket st = new SellTicket() ;
		
		//创建线程对象
		Thread t1 = new Thread(st, "窗口1") ;
		Thread t2 = new Thread(st, "窗口2") ;
		Thread t3 = new Thread(st, "窗口3") ;
		
		//启动线程
		t1.start() ;
		t2.start() ;
		t3.start() ;
	}
}
总结:lock()===synchronized(某个java对象);lock()===notify()------形容的不是太贴切

线程类的第三种创建方式---线程池

线程池的引出:在前面介绍过的实例中,线程在执行完run()方法后,就会结束生命周期。这种方式弊端:在多线程的环境中,不断创建和销毁线程既费时又耗费系统资源;为了提高性能,java.util.concurrent并发包提供了线程池来管理多线程的机制

线程池的好处(创建普通线程的劣势):节约成本,很多子线程调用完毕不会立即被回收掉,而是会回到线程池中被多次利用!

线程池的原理:仅仅创建数量有限的线程,每个线程都不持续不断地执行各种任务

与线程池有关的类和接口

Executor接口表示线程池,JDK5新增了一个Executors工厂类来产生线程池,工厂类中都是静态方法(加s了)

Excutors类的常用的静态方法

(1)public static ExecutorService newFixedThreadPool(int nThreads)

说明:创建拥有固定数目的线程池,空闲的线程会一直保留

(2)public static ExecutorService newCachedThreadPool()

说明:创建拥有缓存的线程池。有任务时才创建线程,空闲的线程在缓冲区中被保留60秒

这两个静态方法的特点返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。

我们常用的是Executor接口的子接口ExecutorService

ExecutorService子接口管理线程池的一些常用方法

(1)Future<?> submit(Runnable task)

说明:提交一个 Runnable 任务用于执行(开启了一个线程),返回一个表示异步运算结果的的Future对象
参数:task - 要提交的任务
返回:表示任务等待完成的 Future 

实例2

实现Runnable接口的子实现类

package tt;

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		//for循环
		for(int x = 0 ; x < 100 ; x ++){
			System.out.println(Thread.currentThread().getName()+":"+x);
		}
	}

}
测试类

package tt;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorsDemo {

	public static void main(String[] args) {
		
		//创建线程池对象,使用Executors工厂类

		ExecutorService pool = Executors.newFixedThreadPool(2) ;
		
		//下来使用ExecutorService(跟踪多个异步任务)一些方法
		
		//使用submit(Runnable target):提交多个任务
		pool.submit(new MyRunnable()) ;
		pool.submit(new MyRunnable()) ;
		
		//结束线程池

		pool.shutdown() ;
		
		
	}
}
运行流程:Executors的newFixedThreadPool()创建一个固定数目线程线程池对象,submit()提交任务(开启线程),shutdown()关闭线程池
补充:shutdown():预备关闭线程池,如果有任务正在执行必须等这些任务执行完毕,才会关闭线程池.哪些还没有开始执行的任务不再执行

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

说明:提交一个返回值的任务用于执行,返回一个表示异步运算结果的的Future对象

特点:Callable也是一个接口(里面有一个call()的抽象方法,要重写此方法,类似run()方法);带泛型

注意:call()方法的返回值是和Callable中的泛型是一致的!call()方法的说明:V call() throws Exception

实例3

实现Callable接口的子实现类

package tt;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Object> {
	
	
	//call()方法的返回值是和Callable中的泛型是一致的!
	@Override
	public Object call() throws Exception {
		
		for(int x = 0 ; x < 100 ; x ++){
			System.out.println(Thread.currentThread().getName()+":"+x);
		}
		return null;
		
	}

}
测试类

package tt;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CallableDemo {
	
	public static void main(String[] args) {
		
		//创建线程池对象,利用工厂类
		ExecutorService Threadpool = Executors.newFixedThreadPool(2) ;
		
		//提交Callable任务(异步任务)
		Threadpool.submit(new MyCallable()) ;//相当于线程中的start()方法
		Threadpool.submit(new MyCallable()) ;
		
		//结束线程池
		Threadpool.shutdown() ;
	}
}

运行流程:Executors的newFixedThreadPool()创建一个固定数目线程线程池对象,submit()提交任务(开启线程),shutdown()关闭线程池

说明:submit都是向线程池提交任务,线程池会调度空闲的线程来执行任务。至于何时执行该任务,由线程池在运行时动态决定

Future接口的说明

public interface Future<V>

Future 表示异步计算的结果,它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果

V :此 Future 的 get 方法所返回的结果类型

常用方法:V get():

实例4 

实现Callable接口的子实现类

package org.tt;

import java.util.concurrent.Callable;

  //需求:线程求和
public class MyCallable implements Callable<Integer> {
	
	private int number ;
	public MyCallable(int number){
		this.number = number ;
	}
	
	@Override
	public Integer call() throws Exception {
		//定义最终结果变量
		int sum = 0 ;
		for(int x = 1 ; x <=number;  x ++ ){
			sum += x ;
		}
		return sum;
	}

	
}
测试类

package tt;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class CallableDemo {
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		
		//创建线程池对象,利用工厂类:Executors
		ExecutorService ThreadPool = Executors.newFixedThreadPool(2) ;
		
		//提交2异步任务,分别计算1-100,1-200之间的和
		Future<Integer> f1 = ThreadPool.submit(new MyCallable(100)) ;
		Future<Integer> f2 = ThreadPool.submit(new MyCallable(200)) ;
		
		//分别调用Future接口中  get()方法,返回具体的结果
		Integer v1 = f1.get() ;
		Integer v2 = f2.get() ;
		
		//输出结果
		System.out.println("v1:"+v1);
		System.out.println("v2:"+v2);
	}
}
未完待续。。。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值