1、多线程入门
几个常见的概念:
程序:指令集,静态的概念;
进程:操作系统 调度程序, 动态的概念;
线程:在进程内多条执行路径。
实现多线程的三种方式:继承Thread、实现Runnable接口和实现Callable接口(了解)。
1.1 继承Thread
/**
* 模拟龟兔赛跑
1、创建多线程 继承 Thread +重写run(线程体)
2、使用线程: 创建子类对象 + 对象.start() 线程启动
*/
public class Rabbit extends Thread {
@Override
public void run() {
//线程体
for(int i=0;i<100;i++){
System.out.println("兔子跑了"+i+"步");
}
}
}
class Tortoise extends Thread {
@Override
public void run() {
//线程体
for(int i=0;i<100;i++){
System.out.println("乌龟跑了"+i+"步");
}
}
}
package com.bjsxt.thread.create;
public class RabbitApp {
public static void main(String[] args) {
//创建子类对象
Rabbit rab = new Rabbit();
Tortoise tor =new Tortoise();
//调用start 方法
rab.start();
//rab.run();//不要调用run方法,调用run方法只是简单的方法调用
tor.start();
//tor.run();
//这个循环是main线程里面的,也相当于一条路径
for(int i=0;i<1000;i++){
System.out.println("main==>"+i);
}
}
}
1.2 实现Runnable接口
实现多线程的第二种方式是创建一个类,实现Runnable接口。在这种方式中,JDK使用了一种设计模式:静态代理。所以下面先来认识认识静态代理设计模式。
1.2.1 静态代理
静态代理:就是在编译阶段,代理角色可以去增强真实角色的的功能。
静态代理要满足的两个条件:1、真实角色和代理角色要实现相同的接口;2、代理角色要持有真实角色的引用。
/**
* 静态代理 设计模式
* 1、代理角色: 持有真实角色的引用
* 2、二者 实现相同的接口
*/
public class StaticProxy {
public static void main(String[] args) {
//创建真实角色
Marry you =new You();
//创建代理角色 +真实角色的引用
WeddingCompany company =new WeddingCompany(you);
//执行任务
company.marry();
}
}
//接口
interface Marry{
public abstract void marry();
}
//真实角色
class You implements Marry{
@Override
public void marry() {
System.out.println("you and 嫦娥结婚了....");
}
}
//代理角色
class WeddingCompany implements Marry{
private Marry you;
public WeddingCompany() {
}
public WeddingCompany(Marry you) {
this.you = you;
}
private void before(){
System.out.println("布置猪窝....");
}
private void after(){
System.out.println("闹玉兔....");
}
@Override
public void marry() {
before();
you.marry();
after();
}
}
1.2.2 通过Runnable接口实现多线程
1、创建类 实现 Runnable接口 +重写 run() -->真实角色类
public class Programmer implements Runnable {
@Override
public void run() {
for(int i=0;i<1000;i++){
System.out.println("一边敲helloworld....");
}
}
}
2、启动多线程 使用静态代理
1)、创建真实角色
2)、创建代理角色 +真实角色引用
3)、调用 .start() 启动线程
public class ProgrammerApp {
public static void main(String[] args) {
//1)、创建真实角色
Programmer pro =new Programmer();
//2)、创建代理角色 +真实角色引用
Thread proxy =new Thread(pro);
//3)、调用 .start() 启动线程
proxy.start();
//这个循环也是一个线程,main线程
for(int i=0;i<1000;i++){
System.out.println("一边聊qq....");
}
}
}
推荐 Runnable 创建线程,它的优点: 1)、避免单继承的局限性 2)、便于共享资源
下面再以卖火车票的例子来加深理解:
public class Web12306 implements Runnable {
private int num =50; //共享的资源,总共的火车票
@Override
public void run() {
while(true){
if(num<=0){
break; //跳出循环
}
System.out.println(Thread.currentThread().getName()+"抢到了"+num--);
}
}
public static void main(String[] args) {
//真实角色
Web12306 web = new Web12306();
//创建三个代理角色
Thread t1 =new Thread(web,"路人甲");
Thread t2 =new Thread(web,"黄牛已");
Thread t3 =new Thread(web,"攻城师");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
1.2.3 通过实现Callable实现多线程
优点:可以获取线程的返回值和抛出异常。
Callable接口和Runnable接口有以下几点不同:
1、Callable接口的方法是call,Runnable接口的方法是run;
2、call方法可以抛出异常,而run方法不能抛出异常;
3、Callable的方法执行后可以返回值,运行Callable对象可以拿到一个Future对象,通过Future可以拿到返回值。而运行Runnable对象是没有返回值的。
Future接口:表示异步计算的结果,它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算得到的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可以获取任务执行的结果。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 使用Callable创建线程
步骤:
1、创建类实现Callable接口,重写call方法
2、借助调度服务ExecutorService 得到Future对象
ExecutorService ser=Executors.newFixedThreadPool(2);
Race tortoise = new Race("老不死",1000);
Race rabbit = new Race("小兔子",500);
3、获取call方法的返回值 result1.get();
4、停止服务 ser.shutdownNow();不停止服务会一直阻塞
*/
public class Call {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//创建线程
ExecutorService ser=Executors.newFixedThreadPool(2);
Race tortoise = new Race("老不死",1000);
Race rabbit = new Race("小兔子",500);
//获取值
Future<Integer> result1 =ser.submit(tortoise) ;
Future<Integer> result2 =ser.submit(rabbit) ;
Thread.sleep(4000); //2秒
tortoise.setFlag(false); //改变标识符,停止线程体循环
rabbit.setFlag(false);
int num1 =result1.get();
int num2 =result2.get();
System.out.println("乌龟跑了-->"+num1+"步");
System.out.println("小兔子跑了-->"+num2+"步");
//停止服务
ser.shutdownNow();
}
}
class Race implements Callable<Integer>{
private String name ; //名称
private long time; //延时时间
private boolean flag =true;
private int step =0; //步数
public Race() {
}
public Race(String name) {
super();
this.name = name;
}
public Race(String name,long time) {
super();
this.name = name;
this.time =time;
}
@Override
public Integer call() throws Exception {
while(flag){
Thread.sleep(time); //延时
step++;
}
return step;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public int getStep() {
return step;
}
public void setStep(int step) {
this.step = step;
}
}