提醒:下文内容仅就总结多线程的创建方式,不考虑线程安全问题,且为了直观,在异常处理方面大量省略
一.程序,进程,线程的基本概念
这里只要做简单的了解即可,红字部分有印象就行,详细内容在虚拟机部分做具体解释
二.创建多线程的四种方式(以多线程抢票为背景)
①在JDK5.0之前:
//1.创建一个子类继承Thread
class Mythread extends Thread{
private static int ticket=100;
//2.重写run方法
@Override
public void run() {
while (true){
if(ticket>=1){
System.out.println(getName()+"抢到票"+ticket);
ticket--;
}
else{
System.out.println(getName()+"票已售罄");
break;
}
}
}
}
public class Test1 {
public static void main(String[] args) {
// 3.实例化子类对象
Mythread t1= new Mythread();
Mythread t2= new Mythread();
t1.setName("线程一");
t2.setName("线程二");
// 4.start开启线程
t1.start();
t2.start();
}
}
总结:继承Thread创建多线程的步骤:
1.创建一个子类继承Thread
2.重写run方法
3.实例化子类对象
4.start开启线程
//1.创建一个实现Runnable接口的类
class Mythread implements Runnable{
private int ticket=100;
//2.重写run方法
@Override
public void run() {
while (true){
if(ticket>=1){
System.out.println(Thread.currentThread().getName()+"抢到票"+ticket);
ticket--;
}
else {
System.out.println(Thread.currentThread().getName()+"票已售罄");
break;
}
}
}
}
public class Test1 {
public static void main(String[] args) {
//3.创建一个实现类的对象
Mythread t= new Mythread();
/*4.每个线程各自创建一个以实现类对象为参数的Thread
类的对象*/
Thread t1=new Thread(t);
Thread t2=new Thread(t);
t1.setName("线程一");
t2.setName("线程二");
//5.start开启进程
t1.start();
t2.start();
}
}
总结:继承Thread创建多线程的步骤:
1.创建一个实现Runnable接口的类
2.重写run方法
3.创建一个实现类的对象
4.每个线程各自创建一个以实现类对象为参数的Thread类的对象
5.start开启进程
关于Thead带参构造器的源码:
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
不难发现,其参数必须是Runnable接口实现类的对象
注:相比于继承Thread类,方式二可以直接实现数据共享,方式一需要用static修饰成员变量(这一点在讲同步的时候尤其重要)
②在JDK5.0新增:
//1.创建一个实现Callable接口的类
class MyThread implements Callable {
//2.重写call函数(由于多态,Obj的返回值类型可以自动向下转化)
@Override
public Object call() throws Exception {
int sum=0;
for (int i = 0; i <100 ; i++) {
if (i % 2 == 0) {
sum += i;
System.out.println(i);
}
}
return sum;
}
}
public class Test1 {
public static void main(String[] args) throws InterruptedException, UnknownHostException {
//3.创建一个实现类的对象
MyThread myThread=new MyThread();
//4.创建一个FutureTask的对象,传入Callable实现类的对象
FutureTask futureTask=new FutureTask(myThread);
//5.创建一个Thread类的对象传入对象futureTask并start
new Thread(futureTask).start();
//6.如果需要返回值,再调用futureTask.get()来取返回值
try {
Object sum=futureTask.get();
System.out.println("总和为"+sum);
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
总结:
1.创建一个实现Callable接口的类
2.重写call方法
3.创建一个实现类的对象
4.创建一个FutureTask的对象,传入Callable实现类的对象
5.创建一个Thread类的对象传入对象futureTask并start
6.如果需要call函数的返回值,需要调用future.get()的方法
注:Callable可以简单认为是Runnable的升级版,Callable允许有返回值功能更强大
关于FutureTask构造器的源码解析
不难发现,FutrueTask其实继承了Runnable,所以在Tread实例化的时候可以直接带FutureTask的对象,并且上图中的构造器中对callable进行了回调
//1.创建一个实现Runnable接口的类
class Mythread implements Runnable {
private int ticket=100;
//2.重写run方法
@Override
public void run() {
while (true){
if(ticket>=1){
System.out.println(Thread.currentThread().getName()+"票"+ticket);
ticket--;
}
else{
System.out.println(Thread.currentThread().getName()+"售罄了");
break;
}
}
}
}
public class Test1 {
public static void main(String[] args) {
//3.提供指定线程数量的线程池
ExecutorService service=Executors.newFixedThreadPool(10);
//service.submit(new Mythread());//适用于Callable
//4.执行指定的线程操作需要提供实现Runnable接口或Callable接口的类
service.execute(new Mythread());//适用于Runnable
service.execute(new Mythread());
//关闭线程池
service.shutdown();
}
}
线程池的好处:
注:用survice.getClass()可以找到survice的实现类,可以发现是ThreadPoolExxcutor,然后上属的方法要通过强转才能用:
ThreadPoolExxcutor survice1=(ThreadPoolExxcutor) survice;