创建方式一:继承Thread类创建多线程
public class 多线程_1创建 {
public static void main(String[] args) {
//创建方式一
Thread t=new MyThread();//创建MyThread线程对象
//直接调run()方法会被当成普通方法执行,此时相当于还是单线程执行,
// 所以需要调用start()方法启动多线程
t.start();//调用start()方法启动线程(实际执行的还是run()方法)
//注意:子线程一定要放在主线程之前,不然先跑主线程还是会被当做是单线程
for (int i = 0; i < 6; i++) {
System.out.println("主线程执行:"+i);
}
}
}
//继承Thread类创建多线程,优点:代码简单。
// 缺点:线程类已经继承Thread类,无法继承其他类,不利于扩展
class MyThread extends Thread{//继承Thread类,重写其中的方法即可实现多线程
//重写run()方法
public void run(){
for (int i = 0; i < 6; i++) {
System.out.println("子线程执行:"+i);
}
}
}
创建方式二:实现Runnable接口
class r{
//创建方式二
public static void main(String[] args) {
//创建一个任务对象
Runnable r=new MyRunnable();
//把任务对象交给Thread处理
Thread t=new Thread(r);
t.start();//调用start()方法启动线程(实际执行的还是run()方法)
//注意:子线程一定要放在主线程之前,不然先跑主线程还是会被当做是单线程
for (int i = 0; i < 5; i++) {
System.out.println("主线程执行:"+i);
}
}
}
//优点:只是实现接口,可以继续继承类和实现接口,扩展性强
// 缺点:多一层对象包装,如果线程有执行结果,是不可以直接返回的
class MyRunnable implements Runnable{//实现Runnable接口,重写其中的方法即可实现多线程
public void run(){
for (int i = 0; i < 5; i++) {
System.out.println("子线程执行:"+i);
}
}
}
创建方式二的另一种语法形式:
class r_2{
//实现Runnable接口,用匿名内部类的方式(另一种语法形式)
public static void main(String[] args) {
//原始形态:
// Runnable r=new Runnable() {
// @Override
// public void run() {
// for (int i = 0; i < 5; i++) {
// System.out.println("子线程执行:"+i);
// }
// }
// };
// Thread t=new Thread(r);
// t.start();
//简化:
new Thread(()->{
for (int i = 0; i < 5; i++) {
System.out.println("子线程执行:"+i);
}
}).start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程执行:"+i);
}
}
}
小案例:
class 案例_售票{
//四个窗口同时售买100张票
public static void main(String[] args) {
TicketWindow tw=new TicketWindow();//创建TicketWindow实例化对象
new Thread(tw,"窗口1").start();//创建线程对象并命名为窗口1,开启线程
new Thread(tw,"窗口2").start();
new Thread(tw,"窗口3").start();
new Thread(tw,"窗口4").start();
}
}
class TicketWindow implements Runnable{
private int tickets=100;
public void run(){
while(true){
if(tickets>0) {
Thread t = Thread.currentThread();//获取当前线程
String t_name = t.getName();//获取当前线程名字
System.out.println(t_name + "正在发售第\t" + tickets-- + "\t张票");//票售出后--,不然永远卖不完
}
if(tickets==0){
break;
}
}
}
}
前两种创建方式都有一个共同的缺点,那就是不能返回线程执行结果
于是便有了创建方式三:实现Callable接口
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class 创建_返回结果{
public static void main(String[] args)throws Exception{
//创建Callable任务对象
Callable<String> c=new MyCallable(100);
//把Callable任务对象交给FutrueTask对象
//FutrueTask对象作用1:是Runnable对象(实现了Runnable接口),可以交给Thread了
//FutrueTask对象作用2:可以在线程执行完毕后调用get方法得到线程执行完毕的结果
FutureTask<String>f1=new FutureTask<>(c);
//交给线程处理
Thread t1=new Thread(f1);
t1.start();//启动线程1
Callable<String> c2=new MyCallable(200);
FutureTask<String>f2=new FutureTask<>(c2);
Thread t2=new Thread(f2);
t2.start();//启动线程2
//如果f1任务没有执行完毕,那么这里的代码会等待,直到线程1跑完才提取结果
String str1=f1.get();
System.out.println("第一个线程执行结果为:"+str1);
//如果f2任务没有执行完毕,那么这里的代码会等待,直到线程2跑完才提取结果
String str2=f2.get();
System.out.println("第二个线程执行结果为:"+str2);
}
}
//定义一个任务类,实现Callable接口,应该声明线程任务结束后返回的结果的数据类型
class MyCallable implements Callable<String> {
// v(泛型)
private int n;
public MyCallable(int n) {
this.n = n;
}
//重写call方法
//案例:加法
public String call() throws Exception {
int sum = 0;
for (int i = 1; i <=n; i++) {
sum += i;
}
return "子线程执行结果为:" + sum;
}
}
三种创建多线程方式的优缺点 :