三种创建方式
Thread class → 继承Thread类(重点)
Runnable 接口 → 实现 Runnable 接口(重点)
Callable 接口 → 实现 Callable 接口 (了解)
Thread
- 自定义线程类继承 Thread 类
- 重写 run() 方法,编写线程执行体
- 创建线程对象,调用 start() 方法启动线程
//继承 Thread 类
public class TestThread01 extends Thread {
//重写 run() 方法线程体
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("子线程TTT——"+i);
}
}
public static void main(String[] args) {
//main 主线程
//创建一个线程对象
TestThread01 testThread01 = new TestThread01();
// testThread01.run(); 按顺序运行,打印完run()内方法,再回主线程
testThread01.start();
//调用start() 方法启动线程时,主线程和子线程同时交替进行,子线程打印会出现主线程打印之间。
for (int i = 0; i < 500; i++) {
System.out.println("主线程MMM--"+i);
//执行的顺序由CPU控制,随机的每次出来的结构都不一样
}
}
}
注意:线程开启不一定立即执行,由CPU调度执行
eg:下载图片 (使用Commons IO包 针对IO流功能的工具类库)
(2条消息) commons-io.jar免费下载_承蒙时光不弃1769203735的博客-CSDN博客_commons-io-2.6.jar下载
下载图片步骤:
- 调用下载器方法
- 重写run方法
- 填入网址、名字并启动线程
import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; //练习Thread,实现多线程同步下载图片 public class TestThread2 extends Thread{ private String url; //网络图片地址 private String name; //保存的文件名 public TestThread2(String url,String name){ this.url = url; this.name = name; } @Override public void run() {//(2)使用多线程方法下载 WebDownloader WebDownloader = new WebDownloader(); WebDownloader.downloader(url,name); System.out.println("下载了文件名为:"+name); } public static void main(String[] args) { //(3)主方法内填入网址及名字,并运行 TestThread2 T1= new TestThread2("https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=4015125149,3656732410&fm=26&gp=0.jpg","1.jpg"); TestThread2 T2= new TestThread2("https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=176549861,2908328616&fm=26&gp=0.jpg","2.jpg"); TestThread2 T3= new TestThread2("https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2654924537,3043598140&fm=26&gp=0.jpg","3.jpg"); T1.start(); T2.start(); T3.start(); } } //下载器(1) class WebDownloader{ //下载方法 public void downloader(String url,String name){ try { FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO异常,downloader方法出现问题"); } } }
实现Runnable
- 定义 MyRunnable 类实现 Runnable 接口
- 实现 run() 方法,编写线程执行体
- 创建线程对象,调用 start() 方法启动线程
package com.ssxxz.TestThread;
//创建线程方式2:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
public class TestThread03 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("子线程TTT——"+i);
}
}
public static void main(String[] args) {
//创建runnbale接口的实现类对象
TestThread03 testThread03 = new TestThread03();
/*创建线程对象,通过线程对象来开启我们的线程,代理
Thread thread = new Thread(testThread03);
thread.start();*/
new Thread(testThread03).start();
for (int i = 0; i < 500; i++) {
System.out.println("主线程MMM--"+i);
}
}
}
推荐使用Runnable接口,因为Java单继承的局限性
小结
继承Thread类
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不建议使用:避免OOP单继承局限性
实现Runnable接口
- 实现接口Runnable具有多线程能力
- 启动线程:传入目标对象+Thread对象.start()
- 推荐使用:避免单继承局限性,灵活方便,方便同一对象被多个线程使用
并发问题 eg 抢票
//多个线程同时操作同一个对象
//抢票系统
public class TestThread04 extends Thread{
private int ticketNums = 10; //总票数
@Override
public void run() {
while (true){
if (ticketNums<=0){
//当票数为0时,跳出循环
break;
}
try {//降低速度,并且抛出异常
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//当while循环一次,票少一张;导入线程名字
System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"票");
}
}
public static void main(String[] args) {
TestThread04 t1 = new TestThread04();
new Thread(t1,"小明").start();
new Thread(t1,"老师").start();
new Thread(t1,"黄牛党").start();
}
}
最后会产生负数票 因为在多个线程操作同一个资源的情况下 线程不安全 系统紊乱
eg 龟兔赛跑
/*
1.定义一个赢家
2.设定赛道
3.判断输赢
4.确定比赛是否结束
*/
public class Race implements Runnable{
private static String winner; //设定一个赢家
@Override
public void run() {
for (int i = 0; i <=100 ; i++) {//赛道
//模拟兔子每10步睡一次觉,而乌龟本身速度就慢
if (Thread.currentThread().getName().equals("兔子") && i%10==0){
try {
Thread.sleep(65);
} catch (InterruptedException e) {
e.printStackTrace();
}
}if (Thread.currentThread().getName().equals("乌龟")){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
//比赛结束就停止程序
if (flag){
break;
}
System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步"); //打印步数
}
}
//判断输赢
//假如winner不为空就说明已经有冠军
//假如steps>=100,则产生冠军
//否则比赛还没结束
private boolean gameOver(int steps){
if (winner!=null){
return true;
}
{
if (steps >= 100) {
winner = Thread.currentThread().getName();
System.out.println("winner is"+winner);
return true;
}
}return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
实现Callable接口
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
- 提交执行:Future< Boolean >result1 = ser.submit(t1);
- 获取结果:boolean r1 = result1.get()
- 关闭服务:ser.shutdownNow();
-
package com.sxmz.ThreadClass.Thread02; import com.sxmz.ThreadClass.Thread01.TestThread2; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.concurrent.*; //线程创建方式三:实现calladle接口 /* 1. 实现Callable接口 2.实现call方法 3.创建执行服务 4.提交执行 5.获取结果 6.关闭服务 */ public class TestCallable implements Callable<Boolean> { private String url; private String name; public TestCallable(String url,String name){ this.url = url; this.name = name; } @Override public Boolean call() { WebDownloader WebDownloader = new WebDownloader(); WebDownloader.downloader(url,name); System.out.println("下载了文件名为:"+name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { TestCallable T1= new TestCallable("https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=4015125149,3656732410&fm=26&gp=0.jpg","1.jpg"); TestCallable T2= new TestCallable("https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=176549861,2908328616&fm=26&gp=0.jpg","2.jpg"); TestCallable T3= new TestCallable("https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2654924537,3043598140&fm=26&gp=0.jpg","3.jpg"); //创建执行服务,new一个池子里面有三个线程 ExecutorService ser = Executors.newFixedThreadPool(3); //提交执行,返回三个值 Future<Boolean> r1 = ser.submit(T1); Future<Boolean> r2 = ser.submit(T2); Future<Boolean> r3 = ser.submit(T3); //获取结果 boolean rs1 = r1.get(); boolean rs2 = r2.get(); boolean rs3 = r3.get(); //关闭 ser.shutdownNow(); } } //下载器 class WebDownloader{ //下载方法 public void downloader(String url,String name){ try { FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO异常,downloader方法出现问题"); } } }
静态代理模式
eg 婚庆公司
- 你You
- 婚庆公司WeddingCompany
- 婚礼Marry
//静态代理模式总结
//真实对象和代理对象都要实现同一个接口
//代理对象要代理真实角色,做了“结婚前”和“结婚后”的事,而真实对象只需要“结婚”
/*
好处:
1. 代理对象可以做很多真实对象做不了的事情
2. 真实对象专注做自己的事情
*/
public class StacticProxy {
public static void main(String[] args) {
You you = new You();
new Thread(()-> System.out.println("我爱你")).start();
new WeddingCompany(you).HappyMarry();
/*
多线程原理与静态代理模式很像
Thread和WeddingCompany都是代理,都代理了真实对象
*/
/* WeddingCompany wd = new WeddingCompany(you);
wd.HappyMarry();*/
}
}
interface Marry{ //婚礼
void HappyMarry();
}
//真是角色,你去结婚
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("我要结婚了!!!");
}
}
//代理角色,帮助你结婚
class WeddingCompany implements Marry{
private Marry target; //创建目标对象
public WeddingCompany(Marry target) {
this.target = target; //导入值等于目标对象
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry(); //目标对象进行结婚
after();
}
private void after() {
System.out.println("结婚之后,收尾款");
}
private void before() {
System.out.println("结婚之前,布置现场");
}
}
真实对象和代理对象都要实现同一个接口
好处:
- 代理对象可以做很多真实对象做不了的事情
- 实对象专注做自己的事情
多线程原理与静态代理模式很像
Thread和WeddingCompany都是代理,都代理了真实对象Runnable和Marry
Lambde表达式
- 希腊字母表中排序第十一位的字母,英文名称为Lambde
- 避免匿名内部类定义过多
- 其实质属于函数式编程的概念
为什么要用Lambde表达式
避免匿名内部类定义过多
可以让你的代码看起来很简洁
去掉了一堆没有意义的代码,只留下核心的逻辑
函数式接口的定义:
- 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口
public interface Runnable{
public abstract void run();
}
- 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。
外部类,静态内部类,局部内部类,匿名内部类,Lambda表达式练习
public class TestLambda2 {
/*静态内部类,static
static class Love implements ILove {
@Override
public void love(int a) {
System.out.println("I love you->" + a);
}
}*/
public static void main(String[] args) {
/*局部内部类,在main方法内
class Love implements ILove {
@Override
public void love(int a) {
System.out.println("I love you->" + a);
}
}
ILove love = new Love();
love.love(4);
*/
/*匿名内部类,没有类名,直接new接口
ILove love = new ILove() {
@Override
public void love(int a) {
System.out.println("I love you->" + a);
}
};
love.love(5);*/
/*1.Lambda表达式
ILove love =(int a)->System.out.println("I love you->" + a);
love.love(6);*/
/*2.简化参数类型
ILove love =(a)->System.out.println("I love you->" + a);*/
/*3.简化括号
ILove love =a->System.out.println("I love you->" + a);*/
/*4.简化花括号
ILove love =a->{System.out.println("I love you->" + a);};*/
ILove love =(a,b,c)->{System.out.println("I love you->" + a+b+c);};
love.love(520,1314,"love");
}
}
interface ILove{
void love(int a,int b,String c);
}
/*外部类
class Love implements ILove{
@Override
public void love(int a) {
System.out.println("I love you->"+a);
}
}*/
总结:
- Lambda表达式可以简化方法、简化参数类型、简化括号、简化花括号。
- 前提是接口为函数式接口
- Lambda表达式只能有一行代码(函数式接口)的情况下才能简化成为一条,如果有多行,那么就用代码块包裹{}
- 多个参数也可以去掉参数类型,但要去掉就都去掉,必须加上括号