程序、进程、线程之间的关系
程序:一段静态的代码,静态的对象。
进程:程序的一次执行过程,正在运行的程序。如:运行中的QQ、运行中的MP3就是分别的两个进程。
程序是静态的,进程是动态的。
线程:进程可以细划分为线程。是一个程序内部的一条执行路径。
如果一个程序在同一个时间执行了多个任务,那么就说这个程序是支持多线程的。
如:操作系统下可以运行多个进程,每个进程下可以执行多个线程。
什么时候使用到多线程
1)程序需要在同一时间执行两个和多个任务的时候
2)程序需要在执行的时候有一些等待的任务;如用户的输入、搜索、文件的读写操作、网络的操作等。
3)需要一些后台运行的程序时。
创建线程的三种方式:
public class TraditionalThread {
/**
* 创建线程的三种方式
*/
public static void main(String[] args) {
Thread thread = new Thread(){
//
使用匿名内部类创建线程
public void run() {
while(true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(“1:” + Thread.currentThread().getName());
}
}
};
thread.start();
//
实现 Runnable
Thread thread2 = new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(“1:” + Thread.currentThread().getName());
}
}
});
thread2.start();
/**
* 实现Callable
*/
int taskSize = 5;
// 创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(taskSize);
// 创建多个有返回值的任务
List<Future> list = new ArrayList<Future>();
Vector vector=new Vector();
List<String> list1=new ArrayList<String>();
list1.add(“from capture where id in(1,2)”);
list1.add(“from capture where id in(3,4)”);
list1.add(“from capture where id in(5,6)”);
list1.add(“from capture where id in(7,8)”);
list1.add(“from capture where id in(9,10)”);
for (String s:list1) {
Callable c = new MyCallable(s, vector);
// 执行任务并获取Future对象
Future f = pool.submit(c);
list.add(f);
}
// 关闭线程池
pool.shutdown();
// 获取所有并发任务的运行结果
System.out.println(vector);
}
}
class MyCallable implements Callable<Object> {
private String sql;
private Vector vector;
MyCallable(String sql,Vector vector) {
this.sql = sql;
this.vector=vector;
}
public Object call() throws Exception {
// System.out.println(“>>>” + taskNum + “任务启动”);
vector.add(sql);
return vector;
}
}
窗口售票的案例:
1)继承Thread类的方式:
/**
* 三个窗口共同出售100张票 为了保证ticket100 被三个实例共用 需要声明为static
* 否则每个线程都会有着自己的实例 也就是说每个实例都有着ticket=100属性。
* 即:各自有着100张票 这是不合理的。
*/
public class TestWindow {
public static void main(String[] args) {
Window window1 = new Window();
Window window2 = new Window();
Window window3 = new Window();
window1.setName("窗口1: ");
window2.setName("窗口2: ");
window3.setName("窗口3: ");
window1.start();
window2.start();
window3.start();
}
}
class Window extends Thread {
static int ticket = 100;
public void run() {
while (true) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "售票,票号为" +ticket--);
}else{
break;
}
}
}
}
2)实现Runnable接口的方式
/** * 创建线程的方式二 * 实现Runnable接口的方式创建线程 */ public class MyRunnable { public static void main(String[] args) { Window window = new Window(); Thread thread1 = new Thread(window); Thread thread2 = new Thread(window); Thread thread3 = new Thread(window); thread1.setName("窗口1:"); thread2.setName("窗口2:"); thread3.setName("窗口3:"); thread1.start(); thread2.start(); thread3.start(); } } class Window implements Runnable { int ticket = 10; public void run() { while (true) { if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket--); } else { break; } } } }
3)实现Callable接口的方式
详细介绍Callable
而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
Callable接口代表一段可以调用并返回结果的代码;Future接口表示异步任务,是还没有完成的任务给出的未来结果。所以说Callable用于产生结果,Future用于获取结果。
Callable接口使用泛型去定义它的返回类型。Executors类提供了一些有用的方法在线程池中执行Callable内的任务。由于Callable任务是并行的(并行就是整体看上去是并行的,其实在某个时间点只有一个线程在执行),我们必须等待它返回的结果。
java.util.concurrent.Future对象为我们解决了这个问题。在线程池提交Callable任务后返回了一个Future对象,使用它可以知道Callable任务的状态和得到Callable返回的执行结果。Future提供了get()方法让我们可以等待Callable结束并获取它的执行结果。
那么怎么使用Callable呢?
ExecutorService
一般情况下是配合
ExecutorService
来使用的,在ExecutorService接口中声明了若干个submit方法的重载版本:
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
第一个submit方法里面的参数类型就是Callable。
暂时只需要知道Callable一般是和ExecutorService配合来使用的,具体的使用方法讲在后面讲述。
一般情况下我们使用第一个submit方法和第三个submit方法,第二个submit方法很少使用。
Future
Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
Future类位于java.util.concurrent包下,它是一个接口:
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
在Future接口中声明了5个方法,下面依次解释每个方法的作用:
cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
isDone方法表示任务是否已经完成,若任务完成,则返回true;
get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
也就是说Future提供了三种功能:
1)判断任务是否完成;
2)能够中断任务;
3)能够获取任务执行结果。
因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。
FutureTask
FutureTask实现了RunnableFuture接口,这个接口的定义如下:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
可以看到这个接口实现了Runnable和Future接口,接口中的具体实现由FutureTask来实现。这个类的两个构造方法如下 :
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
sync = new Sync(callable);
}
public FutureTask(Runnable runnable, V result) {
sync = new Sync(Executors.callable(runnable, result));
}
如上提供了两个构造函数,一个以Callable为参数,另外一个以Runnable为参数。这些类之间的关联对于任务建模的办法非常灵活,允许你基于FutureTask的Runnable特性(因为它实现了Runnable接口),把任务写成Callable,然后封装进一个由执行者调度并在必要时可以取消的FutureTask。
FutureTask可以由执行者调度,这一点很关键。它对外提供的方法基本上就是Future和Runnable接口的组合:get()、cancel、isDone()、isCancelled()和run(),而run()方法通常都是由执行者调用,我们基本上不需要直接调用它。
一个FutureTask的例子
/** * 创建线程的方式三 * 实现Callable接口方式 然后重写call()方法 * 1)此方式和Runnable类似 ; 但是Runnable不会返回结果,并且无法抛出返回结果的异常 * 而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值, * 2)下面的应用通过ExecutorService的submit方法执行Callable * 3)FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值, */ public class MyCallAble { public static void main(String[] args) { int taskSize = 5; // 创建一个线程池并返回ExecutorService实例 ExecutorService executor = Executors.newFixedThreadPool(taskSize); SelfCallable callable1 = new SelfCallable(1000); // 要执行的任务 SelfCallable callable2 = new SelfCallable(2000); FutureTask<String> futureTask1 = new FutureTask<String>(callable1);// 将Callable写的任务封装到一个由执行者调度的FutureTask对象 FutureTask<String> futureTask2 = new FutureTask<String>(callable2); executor.submit(futureTask1); // 执行任务 等同于 Future future1= executor.submit(callable1); executor.submit(futureTask2);//Future future2= executor.submit(callable2); try { System.out.println(futureTask1.get());//通过future获取返回值 future1.get() System.out.println(futureTask2.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } class SelfCallable implements Callable<String> { private long waitTime; public SelfCallable(int times) { this.waitTime = times; } public String call() { try { Thread.sleep(waitTime); } catch (InterruptedException e) { e.printStackTrace(); } return Thread.currentThread().getName() + " Test"; } }
总结:
线程中的常用方法
start():启动线程;并执行相应的run方法。
run():子线程要执行的代码存放在run()方法中。
currentThread():获取当前的线程。
getName():获取当前线程的名字。
setName():设置当前线程的name。
yield():调用此方法的线程会释放当前CPU的执行权。
join():方法可以使得一个线程在另一个线程结束后再执行。如果join()方法在一个线程实例上调用,当前运行着的线程将阻塞直到这个线程实例完成了执行。
isAlive():判断当前的线程是否存活。
sleep(long l):让当前的线程睡眠l毫秒。
interrupt():打断其睡眠阻塞。
以下的三个方法都是对象的方法
wait():让当前的线程挂起并放弃CPU、同步资源、使别的线程可以访问并修改共享的资源,而当前的线程只能排队等候再次对资源的访问。会释放对象的锁让别的线程使用同步资源。
notify():唤醒正在排队等候的同步资源线程优先级最高者结束等待。
notifyAll():唤醒正在排队等候的同步资源所有线程结束等待。
Object提供的这三个方法只能在Synchronized方法和Synchronized代码块使用,否则会报异常。
比较创建线程三种方式的区别
1)避免了Java单继承的局限性。
2)如果多个线程操作同一份资源(数据)更适合使用实现Runnable接口的方式。
3)Callable优于Thread和Runnable的地方就是 可以获取到返回值。
定时器
public class TraditionalTimerTest {
private static int count = 0;
public static void main(String[] args) {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
System.out.println(“bombing!”);
}
}, 10000);
}}
每隔10s打印bombing
其中Timer为定时器,而TimerTask为任务。
线程的同步互斥(线程安全问题)
当多个线程访问同一个对象的时候就会出现线程安全问题
。(如银行的存款和取款)
使用synchronized关键字 来修饰一个方法或者一个代码块,来保证在同一个时刻最多只有一个线程
来执行代代码。
方式一:
同步代码块
2、同步监视器:由一个类的对象充当,那个线程获取此监视器,谁就执行大括号里被同步的代码。俗称”锁” Note:所有的线程必须共用一把锁
任何对象都能作为同步监视器,但是只能是类的全局变量,不能是局部变量。
在实现的方式创建线程可以使用this作为同步监视器,使用继承的方式创建线程不能使用this作为同步监视器。
可以使用this 即:当前的对象作为同步监视器。
/** * 创建线程的方式二 * 实现Runnable接口的方式创建线程 */ public class MyRunnable { public static void main(String[] args) { Window window = new Window(); Thread thread1 = new Thread(window); Thread thread2 = new Thread(window); Thread thread3 = new Thread(window); thread1.setName("窗口1:"); thread2.setName("窗口2:"); thread3.setName("窗口3:"); thread1.start(); thread2.start(); thread3.start(); } } class Window implements Runnable { int ticket = 10; Object obj = new Object(); public void run() {
//Animal animal=new Animal();//局部变量 while (true) { synchronized (obj) {// 使用this替换obj 表示执行此方法的当前对象 不能使用animal局部变量作为同步监视器。 if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket--); } } } } }
方式二:
同步方法
操作共享数据的方法声明为synchronized。即为同步方法,能够保证当其中一个线程执行此方法的时候,其他线程在此方法外进行等待直至此线程执行完毕。
同步方法也有锁机制:同步方法的锁就是this。
/** * 创建线程的方式二 * 实现Runnable接口的方式创建线程 */ public class MyRunnable { public static void main(String[] args) { Window window = new Window(); Thread thread1 = new Thread(window); Thread thread2 = new Thread(window); Thread thread3 = new Thread(window); thread1.setName("窗口1:"); thread2.setName("窗口2:"); thread3.setName("窗口3:"); thread1.start(); thread2.start(); thread3.start(); } } class Window implements Runnable { int ticket = 10; Object obj = new Object(); public void run() { while (true) { // synchronized (obj) {// 使用this替换obj 表示执行此方法的当前对象 // if (ticket > 0) { // System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket--); // } // } show();//同步方法 } } public synchronized void show(){ if (ticket > 0) { System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket--); } } }
案例:
两个储户分别向这个账户中存3000 一次存储1000 存3次 每次存储完打余额。
/** * 银行有一个账户 * 两个储户分别向这个账户中存3000 一次存储1000 存3次 每次存储完打余额。 * 1、是否需要多线程 两个储户 * 2、是否有共享数据 同一个账户 * 3、是否考虑线程的同步 */ public class TestAccount { public static void main(String[] args) { Account account = new Account(); Customer customer1 = new Customer(account); Customer customer2 = new Customer(account); Thread t1=new Thread(customer1); Thread t2=new Thread(customer2); t1.setName("储户1:"); t2.setName("储户2:"); t1.start(); t2.start(); } } class Customer implements Runnable { Account account; public Customer(Account account) { this.account = account; } public void run() { for (int i = 0; i < 3; i++) { account.deposit(1000); } } } class Account { double balance;//余额 public Account() { } public synchronized void deposit(double amt) { this.balance += amt; try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":" + balance); }
线程的中断
Thread.interrupt():中断睡眠阻塞。
public static void main(String[] args) {
/**
* 林永健,处于睡眠阻塞的线程
*/
final Thread lin=new Thread(new Runnable(){
public void run(){
System.out.println(“林:刚美完容,睡觉去了!”);
try{
Thread.sleep(1000000);//此线程进入睡眠阻塞
}catch(InterruptedException e){
System.out.println(“林:干嘛呢,干嘛呢!都破相了!”);
}
}
});
/**
* 黄宏线程,用于中断林永健睡眠阻塞
*/
Thread huanghong=new Thread(new Runnable(){
public void run(){
System.out.println(“开始砸墙!”);
for(int i=0;i<5;i++){
System.out.println(“黄:80!”);
try{Thread.sleep(1000);
}catch(InterruptedException e){
}
}
System.out.println(“咣当!”);
System.out.println(“黄:搞定!”);
/**
* 一个方法的局部内部类中若要引用该方法的其他局部变量
* 那么这个变量必须是final的
*
*/
lin.interrupt();//中断林永健线程,打断其睡眠阻塞
}
});
lin.start();
huanghong.start();
}
}
线程的死锁
1)死锁的问题:处理线程同步时容易出现。
2)不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
3)写代码时,要避免死锁!
public class TestDeadLock {
static StringBuffer sb1 = new StringBuffer();
static StringBuffer sb2 = new StringBuffer();
public static void main(String[] args) {
new Thread() {
public void run() {
synchronized (sb1) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sb1.append("A");
synchronized (sb2) {
sb2.append("B");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
new Thread() {
public void run() {
synchronized (sb2) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sb1.append("C");
synchronized (sb1) {
sb2.append("D");
System.out.println(sb1);
System.out.println(sb2);
}
}
}
}.start();
}
}
线程通信
notify():唤醒正在排队等候的同步资源线程优先级最高者结束等待。
notifyAll():唤醒正在排队等候的同步资源所有线程结束等待。
Object提供的这三个方法只能在Synchronized方法和Synchronized代码块使用,否则会报异常。
案例:使用两个线程打印1-100 线程1、线程2交替打印。
//线程通信。如下的三个关键字使用的话,都得在同步代码块或同步方法中。
//wait():一旦一个线程执行到wait(),就释放当前的锁。
//notify()/notifyAll():唤醒wait的一个或所有的线程
//使用两个线程打印 1-100. 线程1, 线程2 交替打印
class PrintNum implements Runnable {
int num = 1;
Object obj = new Object();
public void run() {
while (true) {
synchronized (obj) {
obj.notify();//第二个线程握住锁后 唤醒第一个线程
if (num <= 100) {
try {
Thread.currentThread().sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":"
+ num);
num++;
} else {
break;
}
try {
obj.wait();//释放锁 让另一个线程执行
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
public class TestCommunication {
public static void main(String[] args) {
PrintNum p = new PrintNum();
Thread t1 = new Thread(p);
Thread t2 = new Thread(p);
t1.setName("甲");
t2.setName("乙");
t1.start();
t2.start();
}
线程的生命周期
现成生命周期的5中状态
新建状态 :Thread thread=new Thread();
就绪状态 :线程已经被启动(start),但是等着cpu分配时间片段。
运行状态: 线程获得了cpu的资源。
堵塞状态: 由于某种原因导致正在运行的线程让出了cpu并暂停了自己的执行。
死亡状态 :线程执行完毕或者被其他的线程杀死。
守护线程
java的垃圾回收就是一个守护线程
某个线程对象调用setDaemon(true)那么这个线程就是守护线程。守护线程必须在线程启动的时候执行。
/**
* 后台线程与前台线程
* @author Administrator
*
*/
public class ThreadDemo3 {
public static void main(String[] args) {
Thread rose=new Thread(new Runnable(){
public void run(){
for(int i=0;i<10;i++){
System.out.println(“rose:Let me go !”);
try{ Thread.sleep(1000);
}catch(InterruptedException e){
}
}
System.out.println(“rose:AAAAAaaaaa….”);
System.out.println(“噗通!”);
}
});
Thread jack=new Thread(new Runnable(){
public void run(){
while(true){
System.out.println(“jack:you jump! I jump!”);
try{
Thread.sleep(1000);
}catch(InterruptedException e){
}
}
}
});
/**
* 设置后台线程的方法必须在该线程启动前调用
*/
jack.setDaemon(true);
jack.start();
rose.start();
System.out.println(“main方法执行结束了”);
}
}
线程中的sleep和notify方法
/**
* Wait() notify()方法
* @author Administrator
*
*/
public class WaiAndNotifyDemo {
public static boolean finish=false;
public static void main(String[] args) {
/**
* 两个线程并发运行
* 一个线程用于下载图片
* 另一个线程用于显示图片
* 这里就出现了一个问题,显示图片的线程应当等待下载图片的
* 线程将图片下载后再进行显示。
*/
String ids=”1,2,3,4,5,6,7,8,9,10″;
String[] split=ids.split(“,”);
List list=new ArrayList();
for(String str:split){
list.add(str);
}
List<String> result = new ArrayList<String>();
String temp=null;
for (int i = 0; i < list.size(); i++) {
if(i%2==0){
temp=list.get(i)+”,”;
}
if (i%2!=0) {
temp+=list.get(i);
result.add(temp);
temp=null;
}
}
for(String t:result){
System.out.println(t);
}
//下载图片的线程
final Thread downLoadThread =new Thread(new Runnable(){
public void run(){
System.out.println(“开始下载图片。。。”);
try{
Thread.sleep(5000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(“下载图片完毕”);
finish=true;
/**
* 通知在当前对象上等待的线程回到runnable状态
* 这里的this就是downLoadThread
* 而下面的displayThread 就是在当前对象上等待的
* 所以调用this.notify()方法会调用displayThread
* 解除等待阻塞,使其可以继续进行
*/
synchronized(this){
this.notify();
}
}
});
//显示图片的线程
Thread displayThread =new Thread(new Runnable(){
public void run(){
try{
/**
* 当显示线程通过调用一个对象的wait方法后
* 那嘛这个线程就在当前线程上等待,进入阻塞
* wait 等待阻塞 与睡眠阻塞的区别在于:
* sleep()会在指定的时间消耗完毕后自动回到Runnable状态
*
* wait() 阻塞不会自动回到Runnable,直接调用了这个对象的notify
* ()方法,当前线程才会到runnable
*
*/
synchronized(downLoadThread){
/**
* 当前线程在哪个对象上等待,就需要获取那个对象的锁
*/
downLoadThread.wait();}
}catch(InterruptedException e){
e.printStackTrace();
}
// if(!finish){
//
throw new RuntimeException(“图片没有下载完成!”);
//
}
System.out.println(“开始显示图片!”);
}
});
displayThread.start();
downLoadThread.start();
}
}
需求:从浏览器中发送请求:请求的参数为10个id的值,如何把这10个id分成5个线程进行查询
思路:
contoller ids 1,2,3,4,5,6,7,8,9,10(controller中的值是从url中传入的)
service:
1. ids/5 1,2 3,4 5,6 7,8 9,10
2. hql from t where id in(1,2)
hql from t where id in(3,4)
hql from t where id in(5,6)
hql from t where id in(7,8)
hql from t where id in(9,10)
3. 5个线程,1个线程传递一个hql,执行查询
基础知识:
java创建线程的三种方式:Callable,Runnable,Thread比较及用法
第一种方式:继承Thread类
GetName()方法返回调用该方法的线程的名字。
public class MyThread extends Thread{
int i=0;
public void run(){
for(int i=0; i<100;i++){
System.out.println(getName()+” “+i);
}
}
public static void main(String[] args) {
Thread t1=new MyThread();
Thread t2=new MyThread();
t1.start();
t2.start();
}
}
第二种方法:实现Runnable接口
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
-
public
class
RunnableThreadTest
implements
Runnable
-
{
-
-
private
int
i;
-
public
void
run()
-
{
-
for
(i =
0
;i <
100
;i++)
-
{
-
System.out.println(Thread.currentThread().getName()+
” ”
+i);
-
}
-
}
-
public
static
void
main(String[] args)
-
{
-
for
(
int
i =
0
;i <
100
;i++)
-
{
-
System.out.println(Thread.currentThread().getName()+
” ”
+i);
-
if
(i==
20
)
-
{
-
RunnableThreadTest rtt =
new
RunnableThreadTest();
-
new
Thread(rtt,
“新线程1”
).start();
-
new
Thread(rtt,
“新线程2”
).start();
-
}
-
}
-
}
-
}
第三种方式:
通过Callable和Future创建线程
(
1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
(2)创建线程池,创建有多个返回值的任务,也就是说把返回值的任务放到一个集合中即:集合的泛型为Future类型。
(3)创建接口实现类的对象,往往在创建的对象的过程中构造方法需要传入要操作的属性值
(4)执行任务并获得Future对象,此对象的获得是通过线程池对象调用submit方法获得的,
把Future对象添加到集合中。
如果创建多个返回值的任务:红色的部分是必不可少的。
(5)关闭线程池,Future对象调用f.get()方法获取任务的返回值。
使用线程池时call方法的执行是在submit方法执行时开始执行。
@SuppressWarnings(“unchecked”)
public class CallableThreadTest{
public static void main(String[] args) throws ExecutionException,InterruptedException {
System.out.println(“—-程序开始运行—-“);
Date date1 = new Date();
int taskSize = 5;
// 创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(taskSize);
// 创建多个有返回值的任务
List<Future> list = new ArrayList<Future>();
for (int i = 0; i < taskSize; i++) {
Callable c = new MyCallable(i+””);
// 执行任务并获取Future对象
Future f = pool.submit(c);
list.add(f);
}
// 关闭线程池
pool.shutdown();
// 获取所有并发任务的运行结果
for (Future f : list) {
// 从Future对象上获取任务的返回值,并输出到控制台
System.out.println(“>>>” + f.get().toString());
}
}
}
class MyCallable implements Callable<Object> {
private String taskNum;
MyCallable(String string) {
this.taskNum = string;
}
public Object call() throws Exception {
System.out.println(“>>>” + taskNum + “任务启动”);
Date dateTmp1 = new Date();
Thread.sleep(1000);
Date dateTmp2 = new Date();
long time = dateTmp2.getTime() – dateTmp1.getTime();
System.out.println(“>>>” + taskNum + “任务终止”);
return taskNum + “任务返回运行结果,当前任务时间【” + time + “毫秒】”;
}
}
书写的过程:
在main方法中或者service方法中定义线程池的大小,并且创建线程池;创建有多个返回值的任务;创建线程(创建的方式大同小异,往往都是通过增强for循环创建)for循环中的内容基本固定,唯一不同的是实现类的构造方法的内容,构造方法中的内容需要在类的属性中进行定义;
int taskSize = 5;
// 1)创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(taskSize);
// 2)创建多个有返回值的任务
List<Future> list = new ArrayList<Future>();
//3) 创建线程
for (int i = 0; i < taskSize; i++) {
Callable c = new MyCallable(i+””);
// 执行任务并获取Future对象
Future f = pool.submit(c);
list.add(f);
}
项目代码:
public List<CaptureLog> getFaceLogList(String ids,Integer currentPage,Integer pageSize, Long logStartTime, Long logEndTime,Integer timeType, String cameraIds, Integer gender, Integer minAge,Integer maxAge, Integer feature) {
String[] split = ids.split(“,”);
List<String> list = new ArrayList<String>();
for (String str1 : split) {
list.add(str1);
}
List<String> result = new ArrayList<String>();
String temp=null;
for (int i = 0; i < list.size(); i++) {
if(i%2==0){
temp=list.get(i)+”,”;
}
if (i%2!=0) {
temp+=list.get(i);
result.add(temp);
temp=null;
}
}
List<String> sqlList = new ArrayList<String>(result.size());
for(String str : result) {
String conditionResult = structureSearchCondition(str,logStartTime,logEndTime, timeType, cameraIds, gender, minAge, maxAge,feature,null);
sqlList.add(“FROM CaptureLog”+conditionResult);
}
int taskSize=5;
ExecutorService pool = Executors.newFixedThreadPool(taskSize);
// 创建多个有返回值的任务
List<Future> list1= new ArrayList<Future>();
//将查询到的结果(集合)放到安全的集合中
Vector vector = new Vector();
//创建线程
for(String sql : sqlList) {
Callable c = new ThreadQuery(sql, vector);
Future f = pool.submit(c);
list1.add(f);
}
return vector;
}
@SuppressWarnings(“rawtypes”)
class ThreadQuery implements Callable {
private Vector vector;
private String sql;
ThreadQuery(String sql, Vector vector)
{
this.vector = vector;
this.sql = sql;
}
@Override
public Object call() throws Exception {
Query query = entityManager.createQuery(sql);
List<CaptureLog> list=query.getResultList();
System.out.println(list);
vector.addAll(list);
return vector;
}
}
此线程的执行顺序:
main方法或者service方法–》从上到下开始执行当执行到submit方法时执行call方法的内容,再去执行main方法或sevice方法的内容。
带逗号的字符串从逗号分隔的情况:
String[] split = ids.split(“,”);
List<String> list = new ArrayList<String>();
for (String str1 : split) {
list.add(str1);
}
List<String> result = new ArrayList<String>();
String temp=null;
for (int i = 0; i < list.size(); i++) {
if(i%2==0){
temp=list.get(i)+”,”;
}
if (i%2!=0) {
temp+=list.get(i);
result.add(temp);
temp=null;
}