package 线程创建.Thread; public class Thread_Test01 { public static void main(String[] args) { //main线程,主线程 //创建一个线程对象 MyThread t = new MyThread(); //调用start方法开启线程 t.start(); for (int i = 0; i < 2000; i++) { System.out.println("当前游戏场景运行中"+i); } } } //创建线程的方式一:继承Thread类,重写run方法,调用start开启线程 class MyThread extends Thread{ @Override public void run() { //run方法线程体 for (int i = 0; i < 2000; i++) { System.out.println("后台加载下一游戏场景"+i); } } }
主线程和子线程并行交替执行
练习:同时下载三张网络图片
需要用到工具类库:commons-io
下载方法参考博客:(54条消息) commons-io的下载和使用_唯有一片炙热的博客-CSDN博客_commons-io
导入到IDEA使用:
1.新建包lib
2.拷贝外部库到lib
3.右键lib,点击“添加为库...”选项
public class Thread_WebPictureDown_Test { //main,主线程 public static void main(String[] args) { //创建三个线程 //地址太长这里省略了 WebPictureDownLoad_Thread t1 = new WebPictureDownLoad_Thread("https:...", "1"); WebPictureDownLoad_Thread t2 = new WebPictureDownLoad_Thread("https:...", "2"); WebPictureDownLoad_Thread t3 = new WebPictureDownLoad_Thread("https:...", "3"); //开启线程 t1.start(); t2.start(); t3.start(); } } //网图下载线程 class WebPictureDownLoad_Thread extends Thread{ private String url;//网络图片地址 private String name;//保存的文件名 //构造器 public WebPictureDownLoad_Thread(String url, String name){ this.url = url; this.name = name; } //网图下载线程的执行体 @Override public void run() { DownLoader downLoader = new DownLoader(); downLoader.downLoad(this.url, this.name); System.out.println(name+"下载完毕"); } } //下载器 class DownLoader{ //下载方法 public void downLoad(String url, String name){ try { FileUtils.copyURLToFile(new URL(url), new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO异常,downLoad方法出现问题"); } } }
具体原理在静态代理部分介绍
public class Runnable_Test { public static void main(String[] args) { //创建Runnable接口的实现类对象 LoadScene Scene_city_of_tear = new LoadScene("City_Of_Tear"); //创建线程对象,通过线程对象来开启我们的线程 (代理) Thread thread = new Thread(Scene_city_of_tear); thread.start(); for (int i = 0; i < 2000; i++) { System.out.println("当前游戏场景运行情况:"+i); } } } //创建线程的方式二:实现Runnable接口,重写run方法,执行线程需要丢入Runnable接口实现类,调用start方法 class LoadScene implements Runnable{ private String levelName; public LoadScene(String levelName){ this.levelName = levelName; } @Override public void run() { //run方法线程体 for (int i = 0; i < 2000; i++) { System.out.println("后台加载场景:"+this.levelName+" 加载情况:"+i); } } }
小结:对比Thread类和Runnable接口
实现Runnable接口的好处
1.接口可以多继承,更灵活方便
2.方便同一个对象被多个线程使用
例子:买电影票
//《阿凡达2》就要上映了,电影院一共放出10张票,小明,小红,小玉都想去看(并且想看很多次!,所以他们都要抢很多票) public class BuyTicket { public static void main(String[] args) { Ticket_Selling_Service Buy_AFanDa = new Ticket_Selling_Service("阿凡达2"); //第二个参数是线程的name,可用.getName()获得 new Thread(Buy_AFanDa, "小明").start(); new Thread(Buy_AFanDa, "小红").start(); new Thread(Buy_AFanDa, "小玉").start(); } } //售票服务 class Ticket_Selling_Service implements Runnable{ //电影的名字 private String movieName; //阿凡达电影票总数 private int ticketNums_AFanDa = 10; public Ticket_Selling_Service(String movieName){ this.movieName = movieName; } @Override public void run() { while(true){ if(this.ticketNums_AFanDa <= 0){ System.out.println("尊敬的用户"+Thread.currentThread().getName()+"你好,《阿凡达2》电影票已售完"); break; } //模拟延时(数据量小,cpu跑太快了,不便于测试) try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+this.movieName+"的第"+this.ticketNums_AFanDa-- +"张票"); } } }
发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
在后面的线程同步部分解决这个问题
//龟兔赛跑,兔子比乌龟跑快,兔子每隔10米睡一次觉 public class Race_Test { public static void main(String[] args) { Race race = new Race(); new Thread(race, "兔子").start(); new Thread(race, "乌龟").start(); } } class Race implements Runnable{ //赛道总长100米 private static int TotalLength = 100; //获胜者 private static String winner = null; @Override public void run() { if(Thread.currentThread().getName().equals("兔子")){ //模拟兔子跑步,一轮循环加25米 for (int howFarHasRuned = 0; howFarHasRuned <= 100; howFarHasRuned = howFarHasRuned + 25){ //模拟兔子睡觉,每10米睡1纳秒 if(howFarHasRuned % 10 == 0){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("兔子-->已经跑了"+howFarHasRuned+"米"); //判断比赛是否已经结束了 if(GameOver(howFarHasRuned)) break; } } else{ //模拟乌龟跑步,一轮循环加1米 for (int howFarHasRuned = 0; howFarHasRuned <= 100; howFarHasRuned++) { System.out.println("乌龟-->已经跑了"+howFarHasRuned+"米"); //判断比赛是否已经结束了 if(GameOver(howFarHasRuned)) break; } } } //判断比赛是否结束 private boolean GameOver(int howFarHasRuned){ if(winner != null) return true;//已经有获胜者了 else{ if(howFarHasRuned >= 100){//获胜者产生 winner = Thread.currentThread().getName(); System.out.println("获胜者是-->"+winner); } } return false; } }
结婚的例子
//静态代理模式总节 //真实对象和代理对象都要实现同一个接口 //代理对象要代理真实角色 //好处 //代理对象可以做很多真实对象做不了的事情 //真实对象只需要专注于做自己的事情 //一下是我目前的理解: //举个现实生活中的例子 //个人游戏开发者A是程序高手可是不会其他的 //那么A可以把音乐代理给音乐创作者B,把美术代理给美术创作者C //A自己只需要专注于把程序部分做好就可以了 //简而言之:把专业的事情代理给专业的对象去做,每个对象专精于自己的事情 public class Marry_Test { public static void main(String[] args) { MarryPeople people_Alice = new MarryPeople("Alice"); WeddingCompany weddingCompany = new WeddingCompany(people_Alice); weddingCompany.HappyMarry(); } } //结婚接口 interface Marry{ void HappyMarry(); } //真实角色:结婚的人 class MarryPeople implements Marry{ private String name; //构造器 public MarryPeople(String name){ this.name = name; } @Override public void HappyMarry() { System.out.println(this.name+"-->结婚"); } } //代理角色:婚庆公司帮助别人结婚 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 before() { System.out.println("---结婚前---"); } private void after() { System.out.println("---结婚后---"); } }
静态代理模式总节:
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色
好处:
代理对象可以做很多真实对象做不了的事情
真实对象只需要专注于做自己的事情
我目前的理解:
举个现实生活中的例子
个人游戏开发者A是程序高手可是不会其他的
那么A可以把音乐代理给音乐创作者B,把美术代理给美术创作者C
A自己只需要专注于把程序部分做好就可以了
简而言之:把专业的事情代理给专业的对象去做,每个对象专精于自己的事情
多线程中的静态代理
第一行代码是多线程中的静态代理
第二行代码是之前结婚的例子
Thread(代理对象)和蓝色框里的东西(真实对象)都实现了Runnnable接口
(这里用到了Lamda表达式,之后会介绍)
猜测:
Thread调用start()方法后会调用自己的run()方法 (具体怎么调的不太清楚)
在run()方法中调用真实对象的run()方法
使用前提:接口为函数式接口(接口只有一个方法且是抽象方法)
场景:名叫Apple的类只使用一次
逐步优化,把代码变简单
1.外部类
public class Lamda_Test01 { public static void main(String[] args) { Food apple = new Apple(); apple.lamda(); } } //外部类 class Apple implements Food{ @Override public void lamda() { System.out.println("吃了苹果"); } } //定义函数式接口 interface Food{ //隐式声明为 public abstract void lamda(); }
2.静态内部类
public class Lamda_Test02 { //静态内部类 static class Apple implements Food{ @Override public void lamda() { System.out.println("吃了苹果"); } } public static void main(String[] args) { Food apple = new Apple(); apple.lamda(); } } //定义函数式接口 interface Food{ //隐式声明为 public abstract void lamda(); }
3.局部内部类
public class Lamda_Test03 { public static void main(String[] args) { //局部内部类 class Apple implements Food{ @Override public void lamda() { System.out.println("吃了苹果"); } } Food apple = new Apple(); apple.lamda(); } } //定义函数式接口 interface Food{ //隐式声明为 public abstract void lamda(); }
4.匿名内部类
public class Lamda_Test04 { public static void main(String[] args) { //匿名内部类 Food apple = new Food() {//new了一个继承Food接口但是没有名称的实现类 @Override public void lamda() { System.out.println("吃了苹果"); } }; apple.lamda(); } } //定义函数式接口 interface Food{ //隐式声明为 public abstract void lamda(); }
5.用lamda简化
public class Lamda_Test05 { public static void main(String[] args) { //lamda Food apple = ()->{ System.out.println("吃了苹果"); }; apple.lamda(); } } //定义函数式接口 interface Food{ //隐式声明为 public abstract void lamda(); }
6.简化大括号(当方法内只有一行语句时)
public class Lamda_Test05 { public static void main(String[] args) { //lamda Food apple = ()->System.out.println("吃了苹果"); apple.lamda(); } } //定义函数式接口 interface Food{ //隐式声明为 public abstract void lamda(); }
推导完毕
带参数的lamda表达式
public class Lamda_Test06 { public static void main(String[] args) { //lamda Food apple = (int i)->System.out.println("吃了"+i+"个苹果"); apple.eat(3); } } //定义函数式接口 interface Food{ //隐式声明为 public abstract void eat(int i); }
1.简化参数类型
public class Lamda_Test07 { public static void main(String[] args) { //lamda Food apple = (i)->System.out.println("吃了"+i+"个苹果"); apple.eat(3); } } //定义函数式接口 interface Food{ //隐式声明为 public abstract void eat(int i);
2.简化小括号(函数只有一个参数时)
public class Lamda_Test08 { public static void main(String[] args) { //lamda Food apple = i->System.out.println("吃了"+i+"个苹果"); apple.eat(3); } } //定义函数式接口 interface Food{ //隐式声明为 public abstract void eat(int i); }
两个参数,有多行语句的例子
public class Lamda_Test09 { public static void main(String[] args) { //lamda Food apple = (name,i)->{ System.out.println(name+"吃了"+i+"个苹果"); System.out.println(name+"吃饱了"); }; apple.eat("Alice", 7); } } //定义函数式接口 interface Food{ //隐式声明为 public abstract void eat(String name, int i); }
无参数
public class Lamda_Test { public static void main(String[] args) { //lamda Food apple = ()->System.out.println("吃了苹果"); apple.eat(); } } //定义函数式接口 interface Food{ //隐式声明为 public abstract void eat(); }
一个参数
public class Lamda_Test { public static void main(String[] args) { //lamda Food apple = i->System.out.println("吃了"+i+"个苹果"); apple.eat(7); } } //定义函数式接口 interface Food{ //隐式声明为 public abstract void eat(int i); }
多个参数,多行语句
public class Lamda_Test { public static void main(String[] args) { //lamda Food apple = (name,i)->{ System.out.println(name); System.out.println("吃了"+i+"个苹果"); }; apple.eat("Alice", 7); } } //定义函数式接口 interface Food{ //隐式声明为 public abstract void eat(String name, int i); }