您的位置:首页 > 职场人生

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

2017-12-08 16:43 393 查看

线程组

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()

说明:创建拥有缓存的线程池
4000
。有任务时才创建线程,空闲的线程在缓冲区中被保留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);
}
}
未完待续。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  线程高级特性
相关文章推荐