多线程:
JVM:Java虚拟机识别main(主线程);特点:
1> jvm是多线程的;
2> 有主线程,main..执行这些代码,能够被Jvm识别;
3> 在执行一些程序的时候,一些对象Jvm释放掉;
原因:它开启了垃圾回收线程,里面GC垃圾回收器(回收一些没有更多引用的对象或者变量...);
几个思想:
线程:用户在听歌的同时,可以使用QQ等操作,这些活动完全可以同时进行,这种思想在Java中成为并发,而将并发完成的每一件事情称为线程;
多线程:Java语言中提供了并发机制,程序员可以在程序中执行多个线程,每一个线程完成一个功能,并与其他线程并发执行,这种机制称为多线程。在单线程中,程序代码按调用顺序依次往下,如果需要一个进程同时完成多段代码的操作,就需要产生多线程;
进程:一个线程则是进程中的执行流程,一个进程中可以同时包括多个线程,每个线程也可以得到一小段程序的执行时间,这样一个进程就可以具有多个并发执行的线程。
实现多线程的三种方式:
1> 继承Thread类;注意:
run():只是调用了一个普通方法,并没有启动另一个线程,程序还是会按照顺序执行相应的代码;
start():则表示,重新开启一个线程,执行相应的run()方法,start()不能重复调用;
举例:
package org.westos_04;
/**
*
*MyThread类就是一个执行线程类
*并且重写Thread类中的run 方法
* run()方法里面应该是一些耗时的操作,IO操作/循环语句..
*/
class MyThread extends Thread {
@Override
public void run() {
//System.out.println("helloworld");
for(int x = 0 ;x <5; x ++) { //两个线程都需要执行这个代码
System.out.println(x);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//创建MyThread类对象
MyThread my = new MyThread() ;
//当前Thread类有一个run public void run()
//执行线程不是run方法 ,run方法的调用相当于一个普通方法的调用
my.run();
/*public void start()使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
结果是两个线程并发地运行*/
MyThread t1 = new MyThread() ;
t1.start();
//IllegalThreadStateException:非法状态异常,同一个线程只能被执行一次
//t1.start();
MyThread t2 = new MyThread() ;
t2.start();
}
}
Thread类提供了一些方法:
public final void setName(String name):给线程起名称;
public final String getName():获取线程名称;
public static Thread currentThread():返回当前正在执行的线程对象的引用;
举例:
import java.security.AccessControlContext;
import java.security.AccessController;
class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
super(name) ;
}
@Override
public void run() {
for(int x = 0 ;x < 100 ; x++) {//t1,t2
System.out.println(getName()+":"+x); //thread-线程编号(从0开始)
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//public Thread(String name):有参构造的形式
MyThread t1 = new MyThread("张三") ;
MyThread t2 = new MyThread("李四") ;
t1.start();
t2.start();
//Thread类中提供另一个功能:
//public static Thread currentThread():返回当前正在执行的线程对象的引用 ==> ,返回:当前执行的线程
System.out.println(Thread.currentThread().getName()); //main
}
}
public final void join():等待该线程终止 interruputedException 中断异常;
分别创建三个子线程,让第一个子线程执行之后,调用join()等待该线程中,在执行t2,t3线程;
举例:
class ThreadJoin extends Thread {
@Override
public void run() {
for(int x = 0 ; x < 100 ; x ++) {
System.out.println(getName()+":"+x);
}
}
}
public class ThreadJoinDemo {
public static void main(String[] args) {
//创建三个子线程
ThreadJoin t1 = new ThreadJoin() ;
ThreadJoin t2 = new ThreadJoin() ;
ThreadJoin t3 = new ThreadJoin() ;
//分别给线程设置名称
t1.setName("李渊");
t2.setName("李世明");
t3.setName("李元霸");
t1.start();
//如果中断了这个线程,那么久抛出这个异常
//谁设置了join这个方法,那么谁就必须要执行完毕,再去执行别的线程
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//等到t1执行完后,t2和t3猜抢占CPU执行权
t2.start();
t3.start();
}
}
public static void yield():暂停当前正在执行的线程对象,并执行其他线程;
举例:
class ThreadYield extends Thread {
@Override
public void run() {//t1,t2子线程线程都要执行这个run()方法
for(int x = 0 ;x <100 ; x ++) {
System.out.println(getName()+":"+x);//
}
//每一个线程都要调用yield()方法;
//当ty1进来打印一个0,之后ty1等待,执行ty2,因为CPU的一点时间片段要执行很多次,
//之后ty2调用yield()方法,ty1在执行一个时间片段,在调用yield()方法,一直到程序结束;
Thread.yield();
}
}
public class ThreadyieldDemo {
public static void main(String[] args) {
//创建两个子线程对象
ThreadYield ty1 = new ThreadYield() ;
ThreadYield ty2 = new ThreadYield() ;
ty1.setName("张三");
ty2.setName("李四");
//分别启动线程
ty1.start();
ty2.start();
}
}
public final void setDaemon(boolean on):true时,表示为守护线程;
将该线程标记为守护线程或用户线程;
当正在运行的线程都是守护线程时,Java 虚拟机退出(守护线程不会立即结束掉,它会执行一段时间再结束掉);
条件:该方法必须在启动线程前调用;
//可执行线程
class ThreadDaemon extends Thread {
@Override
public void run() {
for(int x = 0 ; x<100; x ++) {
System.out.println(getName()+":"+x);
}
}
}
public class ThreadDaemonDemo {
public static void main(String[] args) {
//创建两个子线程
ThreadDaemon td1 = new ThreadDaemon() ;
ThreadDaemon td2 = new ThreadDaemon() ;
td1.setName("张飞");
td2.setName("关羽");
//在启动之前,设置为守护线程
td1.setDaemon(true);
td2.setDaemon(true);
td1.start();
td2.start();
//因为td1和td2是守护线程,那么在名为"刘备"的这个线程执行完后,程序不会停止,还会将td1和td2执行一段时间
Thread.currentThread().setName("刘备");
for(int x =0 ; x < 5 ; x ++) {
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
跟线程优先级相关的方法:
public final int getPriority():返回线程的优先级;
public final void setPriority(int newPriority):更改线程的优先级;
线程存在一个默认优先级;
public static final int MAX_PRIORITY 10 最大优先级
public static final int MIN_PRIORITY 1 最小优先级
public static final int NORM_PRIORITY 5 默认优先级
举例:
class MyThread extends Thread {
@Override
public void run() {
for(int x = 0 ; x <100 ; x ++) {
System.out.println(getName()+":"+x);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//创建三个子线程
MyThread t1 = new MyThread() ;
MyThread t2 = new MyThread() ;
MyThread t3 = new MyThread() ;
System.out.println(t1.getPriority()); //5 默认优先级
System.out.println(t2.getPriority());
System.out.println(t3.getPriority());
t1.setName("林青霞");
t2.setName("林志颖");
t3.setName("林彪");
//设置线程优先级
t1.setPriority(10); //抢占CPU执行权的几率最大
t2.setPriority(1);
t3.setPriority(5);
t1.start();
t2.start();
t3.start();
}
}
public static void sleep(long millis):线程睡眠指定是时间毫秒值;
throws InterruptedException:抛出一个异常;
解释:t1和t2两个线程抢占CPU执行权,加入t1先抢到,开始执行,但是在这个过程中,t2来了,之后t1执行睡眠,之后t2执行,执行了一会t1醒了,t1和t2继续抢占CPU执行权,谁抢到谁执行,执行完睡眠,另一个接着执行;
举例:
import java.util.Date;
class ThreadSleep extends Thread {
@Override
public void run() {
for(int x = 0 ; x <100 ; x ++) {
//t1,t2
//t1, 0,1 //t2来了,之后t1执行睡眠,t2执行,3,4,5,6,7,8 //t2执行到8,t1醒了,接着执行2 ,,,
System.out.println(getName()+":"+x+",日期是:"+new Date());
//困了,想睡一秒钟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadSleepDemo {
public static void main(String[] args) {
//创建两个子线程
ThreadSleep ts1 = new ThreadSleep() ;
ThreadSleep ts2 = new ThreadSleep() ;
ts1.setName("张三");
ts2.setName("李四");
ts1.start();
ts2.start();
}
}
面试题:
stop()和interrupt()的区别:
public final void stop():强迫线程停止执行,不会执行了 (过时了),方法还能使用;
public void interrupt():中断线程,表示中断线程的一种状态,程序还能执行;
wait()和sleep()的区别:
wait(): wait()调用的,立即释放锁(同步锁/Lock锁);
sleep()::线程睡眠,调用不会释放锁;
public final void stop():强迫线程停止执行,不会执行了(过时了),方法能使用的;
解释:在ThreadStop类中线程设点的是睡眠十秒,但是在主线程中,如果三秒醒不来,那么就中断线程,这时候ThreadStop类中的线程就会报异常,输出一句话;
举例:
class ThreadStop extends Thread {
@Override
public void run() {
System.out.println("程序开始了....");
//睡秒10秒钟
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
//e.printStackTrace();
System.out.println("程序出现异常了...");
}
System.out.println("程序结束了....");
}
}
public class ThreadStopDemo {
public static void main(String[] args) {
//创建ThreadStop类对象
ThreadStop st = new ThreadStop() ;
st.start() ;
//如果3秒不醒来,我就干掉你
try {
st.sleep(3000);
//st.stop();
st.interrupt();// 中断
} catch (InterruptedException e) {
//e.printStackTrace();
System.out.println("程序出现了中断异常");
}
}
}
2> 实现多线程程序的第二种方式:
1) 自定义一个类,实现Runnable接口;
2) 实现接口中的run方法,对耗时的代码进行操作;
3) 然后在主线程中创建该了对象,将该类对象做为一个资源类,创建Threadd类的对象,将刚才的资源类作为参数进行传递;
举例:
//自定义类实现接口,实现run方法
class MyThread implements Runnable {
@Override
public void run() {
for(int x= 0; x <100 ; x ++) {
//System.out.println(getName()+":"+x);
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
//创建当前类对象
MyThread my =new MyThread() ;
//实现多线程
//public Thread(Runnable target,String name)
Thread t1 = new Thread(my, "高圆圆") ;
Thread t2 = new Thread(my, "赵又廷") ;
//启动线程
t1.start();
t2.start();
}
}
如何解决多线程的安全问题:
校验一个多线程程序是否有安全问题的隐患的前提条件:1> 当前程序是否为多线程环境;
2> 是否有共享数据;
3> 是否有多条语句对共享数据进行操作;
注意:一般我们只能从第三条解决程序的安全问题;
为了模拟更真实的场景,给程序加入延迟操作(线程睡眠100毫秒):
举例:
package org.westos_03;
class SellTicket implements Runnable {
//定义100张票
private int tickets = 100 ;
/*@Override
public void run() {
while(true) {
//t1先进来 t2
if(tickets>0) {
//为了模拟更真实的场景(网络售票有延迟的),稍作休息
try {
//t1睡 t2睡
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//睡醒之后不一定能抢到;
System.out.println(Thread.currentThread().getName()
+"正在出售第"+(tickets--)+"张票");
//100张票
//为什么会出现同一张票被卖多次?
*//**
* 出现同票的原因:CPU的执行有一个特点(具有原子性操作:最简单最基本的操作)
* t1线程进来,睡完了,100张票
* 原子性操作:记录以前的值 100
* 接着tickets-- :票变成99张票
* 在马上输出99张票之前,t2/t3进来睡一会,直接输出记录的以前那个tickets的值,这个值是100
* 出现:
* 窗口1正在出售第100张票
* 窗口3正在出售第99张票
* 窗口2正在出售第99张票
*
* 原子性操作
*
*//*
}
}
}*/
@Override
public void run() {
while(true) {
try {
//t1睡 t2睡
Thread.sleep(100); //t2睡
} catch (InterruptedException e) {
e.printStackTrace();
}
if(tickets>0) {
//t1,t2,t3 三个线程执行run里面代码
//为了模拟更真实的场景(网络售票有延迟的),稍作休息
System.out.println(Thread.currentThread().getName()
+"正在出售第"+(tickets--)+"张票");//0
/**
* 理想状态:
* t1正在出售第3张票
* t3正在出售第2张票
* t2正在出售第1张票
* ...
* 负票(存在有的线程没有睡醒,醒了之后不经过if判断,直接执行输出语句,这时候票变为0,同样的道理变为-1)
* t1出售第0张票 (延迟操作+线程的执行随机性)
* t3正在出售-1张票
*
*/
}
}
}
}
/**
* 为了模拟更真实的场景,加入延迟操作(让我们线程睡100毫秒)
*
*/
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();
}
}
上述代码存在安全问题:
1> 同一张票被卖了多次;
CPU的执行有一个特点(具有 原子性操作:最简单最基本的操作);
2> 出现了0或者负票;
( 延迟操作+线程的执行随机性);
解决方案1:同步代码块;
格式:
synchronized(锁对象){
针对多条语句对共享数据操作代码;
}
解释:
锁对象:肯定一个对象,随便创建一个对象(匿名对象) ,它可以使任意的一个Java类(引用类型);
注意:每一个线程使用的锁对象,只能是同一把锁,(可以将锁对象看成是门的开和关);
举例:
package org.westos_04;
class SellTicket implements Runnable {
//定义100张票
private int tickets = 100 ;
private Object obj = new Object() ;
@Override
public void run() {
while(true) {
//new Object():锁对象 (门和关)
//t1,t2,t3
synchronized(obj) {//t1进来,门一关,t2,t3进不来了
if(tickets>0) {
try {
//睡眠:延迟
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"正在出售第"+(tickets--)+"张票");//0,-1
}
}
}
}
}
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();
}
}
解决方案2:Lock锁(需要去释放锁);
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作;
举例:
package org.westos_07;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class SellTicket implements Runnable {
// 定义票
private int tickets = 100;
// Object obj = new Object();
// Jdk5.0以后,java提供了一个具体的锁: 接口:Lock
private Lock lock = new ReentrantLock(); // 显示获取锁的前提,一定要创建Lock接口对象
@Override
public void run() {
while (true) {
try { // try...finally
lock.lock(); // 获取锁 syncrhonized(obj)
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
} finally {// 释放锁
if (lock != null) {
lock.unlock();
}
}
}
}
}
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();
}
}
虽然解决了多线程的安全问题,但还是有些问题:
1> 执行效率低;
2> 会产生死锁;
死锁:两个或两个以上的线程,在执行的过程中出现互相等待的情况,就叫做死锁;
举例:
package org.westos_08;
class DieLock extends Thread {
//声明一个成语变量
private boolean flag ;
public DieLock(boolean flag) {
this.flag = flag ;
}
//重写run方法
@Override
public void run() {
if(flag) {
synchronized (MyLock.objA) {
System.out.println("if ObjA");
synchronized (MyLock.objB) {
System.out.println("if objB");
}
}
}else {
synchronized (MyLock.objB) {
System.out.println("else objB");
synchronized (MyLock.objA) {
System.out.println("else objA");
}
}
}
}
//只有当一个锁执行完毕才能释放这个锁;
//加入flag为true,那么闲执行锁对象MyLock.objA,接着继续执行MyLock.objB,但是这时候else进来了,执行MyLock.objB,执行完,
//就要接着执行MyLock.objA,但是这时候下边的锁对象在等待上边的锁释放,而上边的锁对象也在等待下边的锁释放,
//造成了互相等待的结果,这就是死锁;
/**
* 第一种情况:
* if ObjA
else objB
第二种情况
else objB
if ObjA
第三种情况:
理想状态
else objB
else objA
if ObjA
if objB
if ObjA
if objB
else objB
else objA
*/
}
//测试类
public class DieLockDemo {
public static void main(String[] args) {
//创建线程了对象
DieLock dl1 = new DieLock(true) ;
DieLock dl2 = new DieLock(false) ;
//启动线程
dl1.start();
dl2.start();
}
}
死锁的解决方案:
使用两个线程,消费者线程以及生产者线程;
举例分析:
Student类:资源类;
SetThread:设置学生的数据(生产者线程);
GetThread:获取(输出)学生数据(消费者线程);
StudentDemo:测试类;
完整的解决死锁问题:需要使用的是Java的等待唤醒机制;
notify():唤醒在此对象监视器上等待的单个线程,如果所有线程都在此对象上等待,则会选择唤醒其中一个线程;
举例:
消费者线程:
package org.westos_11;
//消费者线程
public class GetThread implements Runnable {
private Student s ;
public GetThread(Student s) {
this.s = s ;
}
@Override
public void run() {
//输出该学生数据
//Student s = new Student() ;
while(true) {
synchronized (s) {
//如果在flag为false的情况下,本身消费者有数据,那么需要等待线程输出数据;
if(!s.flag) {
try {
s.wait();//和网络编程中TCP编程里面的accept() 都属于阻塞式方法
//消费线程等待,等待该线程先输出这些数据(立即释放锁对象)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//线程当前没有数据,一等待,消费者线程将锁对象已经释放掉了,
//释放了之后,我们这个数据需要输出数据,而我现在没有数据,改值,唤醒
System.out.println(s.name +"----"+s.age);//高圆圆---27
//如果没有数据类,
s.flag = false ;
//通知t1线程,赶紧产生数据
s.notify(); //唤醒单个线程
}
//张杨---27
}
}
}
生产者线程:
package org.westos_11;
//生产者线程
public class SetThread implements Runnable {
private Student s ;
public SetThread(Student s) {
this.s = s ;
}
//定义一个变量
private int x = 0 ;
@Override
public void run() {
//设置学生数据
//Student s = new Student() ;
while(true) {
synchronized (s) {
//判断有没有数据的情况
//如果在flag为true的情况下,本身没有数据,那么等待产生数据;
if(s.flag) {
try {
s.wait(); //如果本身没有数据线程等待,产生数据;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(x%2 ==0) {
s.name = "高圆圆" ; //高圆圆---27
s.age = 27 ;
}else {
s.name = "张杨";
//张杨
s.age = 28 ;
}
x++ ;
//如果有数据了,更改flag值
s.flag = true ;//有数据了
//通知t2线程消费数据,唤醒
s.notify(); //唤醒t2线程,唤醒之后t1,t2都互相抢占
}
}
}
}
资源类:
ackage org.westos_11;
public class Student {
String name ;
int age ;
boolean flag; //默认没有数据,如果是true,说明有数据
}
测试类:
public class StudentDemo {
public static void main(String[] args) {
//针对同一个对象进行操作
Student s = new Student() ;
//创建线程类对象
SetThread st = new SetThread(s) ;
GetThread gt = new GetThread(s) ;
//创建线程了对象
Thread t1 = new Thread(st) ; //生产者
Thread t2 = new Thread(gt) ;//消费者
//启动线程
t1.start();
t2.start();
}
}
面试题:
wait(),notify(),notifyAll() 这些方法为什么会定义在Object类中呢?
wait():导致当前线程等待;
注意:特点 ==> 等待的过程中会立即释放所对象,这样才能在下面的代码上更改唤醒notify();
notify():唤醒在此对象监视器上等待的单个线程,如果所有线程都在此对象上等待,则会选择唤醒其中一个线程;
选择是任意性的,并在对实现做出决定时发生;
notifyAll():唤醒在此对象监视器上等待的所有线程;
定义在Object类中的原因:
这些方法好像是属于线程的方法,但是Thread类中并没有这些方法,因为多线程中同步锁对象是任意的Java类,而这些方法都和锁对象有关系,所以定义在Object类;
线程组:
程组表示一个线程的集合。此外,线程组也可以包含其他线程组(用完就没了);
举例:
package org.westos_12;
class MyThread implements Runnable {
@Override
public void run() {
for(int x = 0 ; x <100 ; x ++) {
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
//测试类
public class ThreadGroupDemo {
public static void main(String[] args) {
//获取线程组的名称
//method1();
//如何给多个线程设置一个线程组名称呢?
method2();
}
private static void method2() {
//public ThreadGroup(String name)构造一个新线程组
ThreadGroup tg = new ThreadGroup("main-新的线程组") ;
//共享资源类
MyThread my = new MyThread() ;
//Thread(ThreadGroup group, Runnable target, String name)
Thread t1 = new Thread(tg, my, "线程1") ;
Thread t2 = new Thread(tg, my, "线程2") ;
//直接获取线程组名称
System.out.println(t1.getThreadGroup().getName());
System.out.println(t2.getThreadGroup().getName());
System.out.println(t1.getName());
}
private static void method1() {
MyThread my = new MyThread() ;
//创建线程类对象
Thread t1 = new Thread(my, "线程1") ;
Thread t2 = new Thread(my, "线程2") ;
//public final ThreadGroup getThreadGroup()返回该线程所属的线程组
ThreadGroup tg1 = t1.getThreadGroup() ;
ThreadGroup tg2 = t2.getThreadGroup() ;
//public final String getName():返回线程组的名称
System.out.println(tg1.getName()); //main
System.out.println(tg2.getName());//main
//所有的线程它默认的线程组名称:main(主线程)
//currentThread:正在执行的线程;
System.out.println(Thread.currentThread().getThreadGroup().getName());//main
}
}
线程池:
多个线程执行完毕,它会重新回到线程池中,等待被利用,不会变成垃圾;特点:
1> 节约成本;
2> 线程执行完毕之后不会变成垃圾,重新回到线程池中,等待被利用;
举例:
ackage org.westos_13;
class MyRunnable implements Runnable {
@Override
public void run() {
for(int x = 0 ; x < 100 ; x ++) {
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
//测试类
public class ExceutorsDemo {
public static void main(String[] args) {
//创建一个线程池:返回一个接口;
ExecutorService pool = Executors.newFixedThreadPool(2) ;//创建一个线程池中包含了2条线程
//提交和Runnable接口的方法或者Callable(提交任务)
pool.submit( new MyRunnable()) ;
pool.submit( new MyRunnable()) ;
//1表示只有一个线程池,2表示线程池中的对象编号;
//pool-1-thread-2 :线程池-池数-线程类对象的描述-编号(从1开始)
//关闭线程池
pool.shutdown();
}
}
3>自定义类实现Callable接口;
举例:
MyCallable类:
package org.westos_14;
import java.util.concurrent.Callable;
//Callable的泛型的类型它是call方法的返回值类型
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 ExecutorsDemo {
public static void main(String[] args) {
//创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2) ;
//提交任务
pool.submit(new MyCallable()) ;
pool.submit(new MyCallable()) ;
//关闭线程池
pool.shutdown();
}
}
综合练习:分别计算每个线程的求和;
package org.westos_15;
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 org.westos_15;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
*分别计算每个线程的求和!
*
*/
public class ExecutorsTest {
public static void main(String[] args) throws InterruptedException,ExecutionException {
//创建线程池对象
ExecutorService pool = Executors.newFixedThreadPool(2) ;
//提交任务
Future<Integer> f1 = pool.submit(new MyCallable(100)) ;
Future<Integer> f2 = pool.submit(new MyCallable(200)) ;
//V get():获取结果(Future中的方法)
Integer i1 = f1.get() ;
Integer i2 = f2.get() ;
System.out.println(i1);
System.out.println(i2);
//关闭线程池
pool.shutdown();
}
}