Java基础第九天:狂神版本

该文档是:Luo学习Java笔记...

博客连接:https://www.loveuluo.cn

日期:2020-11-8

因为该教程讲的没有动力节点详细,所以6.2之后的学习全部转入动力节点第九天重新学习

1. 多线程概念

image-20201108095415440

image-20201108095556974

image-20201108095804640

2. 线程的创建

image-20201108100040029

2.1 方式一:继承Thread类

image-20201108102150189

image-20201108101902564

例子 多线程下载图片:

public class 下载图片多线程Test extends Thread { //继承多线程
    private String url; //网络图片地址
    private String name; //文件保存的名字
    public 下载图片多线程Test(String url, String name) {//有参构造方法
        this.url = url;
        this.name = name;
    }
    @Override
    public void run() { //下载图片线程的执行体(里边是start后多线程要执行的方法)
        gongJuLei gongJuLei=new gongJuLei();
        gongJuLei.donwloadPic(url,name);
        System.out.println("下载了文件名为:"+name);
    }
    public static void main(String[] args) { //主线程
        //创建一个线程对象
        下载图片多线程Test test1=new 下载图片多线程Test("https://tuchuang-luo.oss-cn-shanghai.aliyuncs.com/image-20201106104812521.png","pic1.jpg");
        下载图片多线程Test test2=new 下载图片多线程Test("https://tuchuang-luo.oss-cn-shanghai.aliyuncs.com/image-20201106104812521.png","pic2.jpg");
        下载图片多线程Test test3=new 下载图片多线程Test("https://tuchuang-luo.oss-cn-shanghai.aliyuncs.com/image-20201106104812521.png","pic3.jpg");
        test1.start();
        test2.start();
        test3.start();
    }
}
class gongJuLei{ //下载图片工具类
    //下载的方法
    public void donwloadPic(String url,String file){ //传入url和file方便方法复用
        try {
            FileUtils.copyURLToFile(new URL(url),new File(file));//下载图片的静态方法 需要一个url地址和 file文件名字
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("下载图片时发生了错误。");
        }
    }
}

执行结果为(因为是多线程执行 所以不会按照 1,2,3的顺序,CPU安排调度):

image-20201108105809364

2.2 方式二:实现Runnable接口(建议使用)

image-20201108110131339

image-20201108110849920

小结:

image-20201108111106187

例子一 买票案例:

public class MaiPiaoTest02 implements Runnable{//实现Runnable接口
    private int ticket = 10; //一共有十张票
    @Override
    public void run() { //重写run方法(多线程执行方法体)
        while (true) {
            if (ticket <= 0 ){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket+"张票");
            ticket--;//每循环一次票数-1
        }
    }
    public static void main(String[] args) { //主线程
    //创建Runnable接口的实现类对象
    MaiPiaoTest02 mai=new MaiPiaoTest02();
    //创建线程对象,通过线程对象来开启我们的线程,代理
    Thread thread = new Thread(mai,"小贝"); //线程对象可以取名
    Thread thread1 = new Thread(mai,"小钱");
    thread.start();
    thread1.start();
    new Thread(mai,"小蒸").start(); //直接调用start方法
    }
}

输出结果为(发现问题:多线程操作同一个资源的情况下,线程不安全,数据紊乱):

image-20201108113223236

例子二 龟兔赛跑案例:

public class GuiTuSaiPaoTest03 implements Runnable{
    private static String winner;//定义一个胜者 只能有一个所以用static
    @Override
    public void run() { //线程执行体
        for (int i = 0; i <= 100; i++) { //设置跑道长度 (这里是局部变量,所以不会产生两个线程抢夺对象资源的情况)
            if (Thread.currentThread().getName() == "兔子" && i%10==0){//如果进来的线程名字叫兔子 每10米让它沉睡一次
                try {
                    Thread.sleep(1); //沉睡1毫秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //谁先到达100米谁获胜
            if (end(i)){ //如果胜者出现了 就结束循环
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"米。");//获取线程名字
        }
    }
    public boolean end(int step){ //判断谁是胜者的方法
        if (winner != null){ //已经存在胜利者了
            return true;
        }if (step >= 100){ //如果到达了100
            winner = Thread.currentThread().getName();  //将到达100的线程的名字赋值给winner
            System.out.println("winner is "+winner);
            return true;
        } return false;
    }
    public static void main(String[] args) {
        GuiTuSaiPaoTest03 guiTu=new GuiTuSaiPaoTest03();
        new Thread(guiTu,"兔子").start();
        new Thread(guiTu,"乌龟").start();
    }
}

2.3 方式三:实现Callable接口(了解即可)

image-20201108155845473

3. Lamda表达式

image-20201108170044605

image-20201108170104661

image-20201108170608078

3.1 从外部类一步步优化成lambda表达式例子

第一步: 正常外部类 然后new类并调用方法

public class lambda表达式 {
    public static void main(String[] args) {
        Ilove ilove=new Ilove();
        ilove.love("你");
    }
}
//定义一个函数式接口(只包含唯一一个抽象方法)
interface lambda {
    void love(String who); //public abstract可以省略
}

public class Ilove implements lambda{ //重写了接口方法的实现类
    @Override
    public void love(String who) {
        System.out.println("我爱"+who);
    }
}

第二步: 使用静态内部类方式简化代码

public class lambda表达式 {
    static class Ilove implements lambda{ //静态内部类
        @Override
        public void love(String who) {
            System.out.println("我爱"+who);
        }
    }
    public static void main(String[] args) {
        Ilove ilove=new Ilove();
        ilove.love("你");
    }
}
//定义一个函数式接口(只包含唯一一个抽象方法)
interface lambda {
    void love(String who); //public abstract可以省略
}

第三步: 使用局部内部类简化代码

public class lambda表达式 {
    public static void main(String[] args) {
        class Ilove implements lambda{ //局部内部类
            @Override
            public void love(String who) {
                System.out.println("我爱"+who);
            }
        }
        Ilove ilove=new Ilove();
        ilove.love("你");
    }
}
//定义一个函数式接口(只包含唯一一个抽象方法)
interface lambda {
    void love(String who); //public abstract可以省略
}

第三步: 使用匿名内部类简化代码

public class lambda表达式 {
    public static void main(String[] args) {
      lambda lambda=new lambda() {//匿名内部类(类没有名字)
          @Override
          public void love(String who) {
              System.out.println("我爱"+who);
          }
      };
        lambda.love("你");
    }
}

//定义一个函数式接口(只包含唯一一个抽象方法)
interface lambda {
    void love(String who); //public abstract可以省略

第四步:lambda表达式简化代码

public class lambda表达式 {
    public static void main(String[] args) {
      lambda lambda= (String who)->{
              System.out.println("我爱"+who);
          };
        lambda.love("你");
    }
}
//定义一个函数式接口(只包含唯一一个抽象方法)
interface lambda {
    void love(String who); //public abstract可以省略
}

第五步: lambda表达式最终简化

public class lambda表达式 {
    public static void main(String[] args) {
        //简化1:去掉参数类型学
         lambda lambda= (who)->{
             System.out.println("我爱"+who);
        };
         //简化2:去掉小括号
        lambda lambda1= who->{
            System.out.println("我爱"+who);
        };
        //简化3:去掉大括号 (当只有一行代码的时候才能省去大括号)(不建议,因为很少代码只有一行)
        lambda lambda2= who-> System.out.println("我爱"+who);
        
        /*总结:lambda表达式只有一行代码的时候才能去掉大括号并简化成一行,如果有多行,
        如果有多行,那么就要用代码块(大括号)包裹,前提是接口为函数式接口。
        多个参数也可以去掉参数类型,但是必须加上小括号:lambda lambda= (a,b)-> ...*/
        lambda.love("你");
        lambda1.love("你");
        lambda2.love("你");
    }
}
//定义一个函数式接口(只包含唯一一个抽象方法)
interface lambda {
    void love(String who); //public abstract可以省略
}

4. 静态代理

image-20201108202543404

image-20201108205021430

public class 静态代理模式Test {
    public static void main(String[] args) {
        You you=new You();//你要结婚
        婚庆公司 hunQing=new 婚庆公司(you);
        hunQing.happyMary();
    }
}
interface Marry{ //结婚接口
    void happyMary();
}
//真实角色,你去结婚
class You implements Marry{
    @Override
    public void happyMary() {
        System.out.println("我要结婚了");
    }
}
//代理角色,帮助你结婚
class 婚庆公司 implements Marry{
    //代理谁-->真实目标角色
    private Marry who; //需要传一个实现了Marry接口的实现类
    public 婚庆公司(Marry who) {
        this.who = who;
    }
    @Override
    public void happyMary() {
        before();
        this.who.happyMary();//这就是真实对象,调用了结婚。
        after();
    }
    private void before() {
        System.out.println("布置婚礼现场");
    }
    private void after() {
        System.out.println("洞房花烛夜");
    }
}

线程的底部实现原理就是静态代理:

5. 线程状态

image-20201109100608455

image-20201109100814400

5.1 停止线程

image-20201109100922493

public class XianChengStopTest04 implements Runnable{
    private boolean flag = true;//标识位
    @Override
    public void run() {
        int i = 0 ;
        while (flag) {
            System.out.println("我开始运行了,次数:" + i++);
        }
    }
    public void stop(){ //公开的停止线程的方法,转换标志位
        this.flag = false;
        System.out.println("停止线程了。");
    }
    public static void main(String[] args) {
        XianChengStopTest04 xianCheng = new XianChengStopTest04();
        new Thread(xianCheng).start();
        for (int i = 0; i < 100; i++) {
            System.out.println("main方法运行了:"+i);
            if (i == 90){
                xianCheng.stop(); //当i=90时调用stop方法停止线程
            }
        }
    }
}

5.2 线程睡眠

image-20201109104612238

//倒计时案例
public class XianChengSleepTest05 implements Runnable{
    @Override
    public void run() {
        for (int i = 10; i >=0 ; i--) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("倒计时10秒:"+i);
        }
    }
    public static void main(String[] args) {
    XianChengSleepTest05 xianCheng=new XianChengSleepTest05();
    new Thread(xianCheng).start();
    }
}

5.3 线程礼让

image-20201109110234646

public class XianChengLiRangTest06 {
    public static void main(String[] args) {
        LiRang liRang=new LiRang();
        new Thread(liRang,"小a").start();
        new Thread(liRang,"小b").start();
    }
}
class LiRang implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield(); //线程礼让
        System.out.println(Thread.currentThread().getName()+"线程结束");
    }
}
/* 礼让成功输出:           
小a线程开始执行
小b线程开始执行
小a线程结束
小b线程结束
    
礼让失败输出:
小a线程开始执行
小a线程结束
小b线程开始执行
小b线程结束 */

5.4 线程插队

image-20201109143202754

public class XianChengJoin07Test implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 300; i++) {
            System.out.println("我是vip"+i);
        }
    }
    public static void main(String[] args) {
        XianChengJoin07Test xian=new XianChengJoin07Test();
        Thread thread = new Thread(xian);
        thread.start();//和主线程一起跑
        for (int i = 0; i < 200; i++) {
            System.out.println("main执行了"+i);
            if (i == 100 ){
                try {
                    thread.join();//当主线程循环100次的时候,thread就行插队,直到他执行完
                } catch (InterruptedException e) {
                    e.printStackTrace();}}}}
}

5.5 线程状态观测

image-20201109144559696

5.5.1回顾一下lambda表达式

第一步优化:匿名内部类优化。

public class XianChengStateTest08 {
    public static void main(String[] args) throws Exception{
        Runnable runnable=new Runnable() { //匿名内部类
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {Thread.sleep(300); //睡300毫秒
                    } catch (InterruptedException e) {e.printStackTrace(); }
                }
                System.out.println("循环结束了,我是该线程最后一段代码,执行完后该进程结束");
            }
        };
        Thread thread=new Thread(runnable);
        System.out.println(thread.getState()); //新生状态-NEW
        thread.start();
        System.out.println(thread.getState()); //正在执行-RUNNABLE
        while (thread.getState()!= Thread.State.TERMINATED){//如果程序没有退出 就一直侦测状态
            Thread.sleep(100); //主线程输出状态慢一点 防止一次性输出一大堆状态
            //因为上边睡了300毫秒,所以状态会是TIMED_WAITING 然后变成TERMINATED结束线程
            System.out.println(thread.getState());
        }
    }
}

进一步优化:将匿名内部类直接放入new Thread()中 因为Thread需要传入一个Runnable接口实现类。

public class XianChengStateTest08 {
    public static void main(String[] args) throws Exception{
        Thread thread=new Thread(new Runnable() { //因为Thread方法需要一个Runnable接口实现类,直接使用匿名内部类传入
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try { Thread.sleep(300); //睡300毫秒
                    } catch (InterruptedException e) { e.printStackTrace(); }
                }
                System.out.println("循环结束了,我是该线程最后一段代码,执行完后该进程结束");
            }
        });
        System.out.println(thread.getState()); //新生状态-NEW
        thread.start();
        System.out.println(thread.getState()); //正在执行-RUNNABLE
        while (thread.getState()!= Thread.State.TERMINATED){//如果程序没有退出 就一直侦测状态
            Thread.sleep(100); //主线程输出状态慢一点 防止一次性输出一大堆状态
            //因为上边睡了300毫秒,所以状态会是TIMED_WAITING 然后变成TERMINATED结束线程
            System.out.println(thread.getState());
        }
    }
}

进一步优化:使用lambda表达式

public class XianChengStateTest08 {
    public static void main(String[] args) throws Exception{
        Runnable runnable=()->{
            for (int i = 0; i < 5; i++) {
                try { Thread.sleep(300); //睡300毫秒
                } catch (InterruptedException e) { e.printStackTrace(); }
            }
            System.out.println("循环结束了,我是该线程最后一段代码,执行完后该进程结束");
        };
        Thread thread=new Thread(runnable); //传入Runnable接口实现类,使用了lambda表达式简化
        System.out.println(thread.getState()); //新生状态-NEW
        thread.start();
        System.out.println(thread.getState()); //正在执行-RUNNABLE
        while (thread.getState()!= Thread.State.TERMINATED){//如果程序没有退出 就一直侦测状态
            Thread.sleep(100); //主线程输出状态慢一点 防止一次性输出一大堆状态
            //因为上边睡了300毫秒,所以状态会是TIMED_WAITING 然后变成TERMINATED结束线程
            System.out.println(thread.getState());
        }
    }
}

最终优化:直接将lambda表达式放入new Thread()中

public class XianChengStateTest08 {
    public static void main(String[] args) throws Exception{
        Thread thread=new Thread( ()->{ //直接传入lambda表达式
            for (int i = 0; i < 5; i++) {
                try { Thread.sleep(300); //睡300毫秒
                } catch (InterruptedException e) { e.printStackTrace(); }
            }
            System.out.println("循环结束了,我是该线程最后一段代码,执行完后该进程结束");
        } );
        System.out.println(thread.getState()); //新生状态-NEW
        thread.start();
        System.out.println(thread.getState()); //正在执行-RUNNABLE
        while (thread.getState()!= Thread.State.TERMINATED){//如果程序没有退出 就一直侦测状态
            Thread.sleep(100); //主线程输出状态慢一点 防止一次性输出一大堆状态
            //因为上边睡了300毫秒,所以状态会是TIMED_WAITING 然后变成TERMINATED结束线程
            System.out.println(thread.getState());
        }
    }
}

5.6 线程的优先级

image-20201109161841795

public class XianChengYouXianTest09 {
    public static void main(String[] args) {
        Test test=new Test();
        Thread thread1 = new Thread(test,"a");
        Thread thread2 = new Thread(test,"b");
        Thread thread3 = new Thread(test,"c");
        Thread thread4 = new Thread(test,"d");
        thread1.start();//默认优先级 直接启动
        thread2.setPriority(3);//设置线程优先级为3
        thread2.start();//一定要先设置优先级再启动
        thread3.setPriority(Thread.MAX_PRIORITY);//设置线程优先级为最大优先级(10)
        thread3.start();
        thread4.setPriority(Thread.MIN_PRIORITY);//设置线程优先级为最小优先级(1)
        thread4.start();
        System.out.println("主线程默认优先级是"+Thread.currentThread().getName()+"->"+Thread.currentThread().getPriority());
    }
}
class Test implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+":我的优先值是->"+Thread.currentThread().getPriority());
    }
}

输出结果为:(不一定线程优先级高就百分百回先调用,只是概率高了)

image-20201109163351517

5.7 守护线程

image-20201109163747101

public class ShouHuXianChengTest10 {
    public static void main(String[] args) {
        You you =new You();
        God god =new God();
        Thread thread=new Thread(god);
        thread.setDaemon(true);//设置为true代表设置为守护线程,默认是false用户线程
        thread.start(); //当用户进程结束后,守护线程也会结束
        new Thread(you).start();
    }
}
class You implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i < 101; i++) {
            System.out.println("活了"+i+"岁");
        }
    }
}
class God implements Runnable{
    @Override
    public void run() {
        while (true){ // 死循环 (设置为守护线程后,当用户线程结束该线程也会结束)
            System.out.println("上帝在守护你");
        }
    }
}

6. 线程同步机制

image-20201109183327738

image-20201109183339024

锁就像上厕所,人进去了 锁上了做完了该做的事之后再出来。 所以效率低,但是安全。

image-20201109183630691

image-20201109183702665

6.1 三大不安全案例

例子一:买票案例

public class Test01 {
    public static void main(String[] args) {
        Mai mai=new Mai();
        new Thread(mai,"我").start();
        new Thread(mai,"你").start();
        new Thread(mai,"黄牛").start();
    }
}
class Mai implements Runnable{
    private boolean flag = true; //停止方式
    private int piao = 10;//票的数量一共为10
    @Override
    public void run() {
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace(); } } }
    private void buy() throws InterruptedException {//私有的买票方法 供这个类里使用
        if (piao <= 0){
            this.flag = false;
            return;
        }
        Thread.sleep(100);//模拟延时
        System.out.println(Thread.currentThread().getName()+"抢到了第"+ piao-- +"张票");
    }
}

例子二:取钱案例

image-20201110111746634

public class Test02 {
    public static void main(String[] args) {
    Account account=new Account(10000,"夫妻共同财产");
        //在这个账户拿一万块
    new Thread(new NaMoney(account,8000),"丈夫").start();
        //在这个账户拿一万块
    new Thread(new NaMoney(account,10000),"妻子").start();
    }
}
class Account {
   int money;//余额
    String name;//卡名
    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}
//模拟取款
class NaMoney implements Runnable{
    Account account;
    int quMoney;//取了多少钱
    int nowMoney;//现在有多少钱
    public NaMoney(Account account, int quMoney) {
        this.account = account;
        this.quMoney = quMoney;
    }
    @Override
    public void run() {
        //sleep可以放大问题的发生性
        try {
            Thread.sleep(100);//模拟延时 在这个地方等待然后同时执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (account.money - quMoney <0){//如果账户余额-要取的钱小于0
        System.out.println("你的余额不足");
        return;//直接不再执行下去
    }
    nowMoney = nowMoney+quMoney;//现在有的钱 = 有的钱加上取到的钱
    account.money = account.money-quMoney;//卡内余额=余额-取的钱
        System.out.println("恭喜"+Thread.currentThread().getName()+
         "取到了"+quMoney+"元钱"); //线程名加取到了多少钱
        System.out.println(Thread.currentThread().getName()+"现在拥有:"+nowMoney+"元钱");
        System.out.println(account.name+"该账户的内余额是:"+account.money);
    }
}

执行结果是:

image-20201110105854783

例子三:线程不安全的集合

集合的大小最终为9998,原因两个线程同一瞬间操作了同一个位置(没有对列),把两个数据添加到了同一个位置。

image-20201110111424744

6.2 同步方法 (接下来的学习转入动力节点那边)

image-20201110135758991

image-20201110140035929

image-20201110140056113

最后修改:2020 年 11 月 28 日 09 : 53 AM
如果觉得我的文章对你有用,请随意赞赏