线程组
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);
}
}
未完待续。。。