Java基础笔记(六)多线程

6.1.1 进程和线程

  1. ***进程***是执行中一段程序,即一旦程序被载入到内存中并准备执行,它就是一个进程。进程是表示资源分配的的基本概念,又是调度运行的基本单位,是系统中的并发执行的单位。
  2. 线程:单个进程中执行中每个任务就是一个线程。线程是进程中执行运算的最小单位。
  • Thread class
  • Runnable接口
  • Callable接口
  1. 创建线程的方式一:继承Thread类,重写run()方法,调用start并开启线程

  2. 线程不一定立即执行,由CPU调度

6.1.2 继承Thread类

  • package com.thread;
    
    //创建线程的方式一:继承Thread类,重写run()方法,调用start并开启线程
    public class Demo01 extends Thread{
        public static void main(String[] args) throws InterruptedException {
            Demo01 demo01 = new Demo01();
            demo01.start();
    
            for (int i = 0; i < 1000; i++) {
                System.out.println("我在学习多线程--"+i);
                demo01.sleep(100);
            }
        }
        @Override
        public void run() {
            for (int i = 0; i < 200; i++) {
                System.out.println("我在看代码--"+i);
                try {
                    this.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }
    
    }
    
    
    
  • 不建议使用:避免OOP单继承局限性

6.1.3 实现Runnable接口

  • package com.thread;
    
    //创建线程的方式二,实现Runnable接口,重写run()方法,调用start并开启线程
    public class Demo03 implements Runnable{
    
        @Override
        public void run() {
            for (int i = 0; i < 200; i++) {
                System.out.println("代码===="+i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            Demo03 demo03 = new Demo03();
            new Thread(demo03).start();
            for (int i = 0; i < 1000; i++) {
                System.out.println("多线程===="+i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    
  • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象呗多个线程使用

6.1.4 实例:龟兔赛跑

  • package com.thread;
    
    //模拟龟兔赛跑
    public class Race implements Runnable{
    
        private static String winner;
    
        @Override
        public void run() {
            for (int i = 0; i <= 100; i++) {
                //模拟兔子睡觉
                if (Thread.currentThread().getName()=="兔子" && i%10==0){
                    try {
                        Thread.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (Thread.currentThread().getName()=="乌龟" && i%10==0){
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
                boolean flag = gameOver(i);
                if (flag){
                    break;
                }
                System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
            }
        }
    
        //判断是否完成
        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();
        }
    }
    
    

6.1.5 实现Callable接口

  • 可以定义返回值
  • 了解即可

6.1.6 静态代理

  • package com.thread;
    
    public class StaticProxy {
        public static void main(String[] args) {
            You you = new You();
            WeddingCompany weddingCompany = new WeddingCompany(you);
            weddingCompany.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("结婚前");
        }
    }
    

6.2.1 Lamda表达式

  • new Thread(()->System.out.println("多线程学习")).start();
    
  • package com.thread;
    
    public class TestLambda {
        //3.静态内部类
        static class Like2 implements ILike{
            @Override
            public void lambda() {
                System.out.println("I Like Lambda2");
            }
        }
        public static void main(String[] args) {
            ILike like = new Like();
            like.lambda();
    
            like = new Like2();
            like.lambda();
    
            //4.局部内部类
            class Like3 implements ILike{
                @Override
                public void lambda() {
                    System.out.println("I Like Lambda3");
                }
            }
            like = new Like3();
            like.lambda();
            //5.匿名内部类,没有父类的名称,必须借助接口或者父类
            like = new ILike() {
                @Override
                public void lambda() {
                    System.out.println("I Like Lambda4");
                }
            };
            like.lambda();
    
            //6.用Lambda简化
            like = ()->{
                System.out.println("I Like Lambda5");
            };
            like.lambda();
        }
    }
    //1.定义一个函数式接口
    interface ILike{
        void lambda();
    }
    //2.实现类
    class Like implements ILike{
        @Override
        public void lambda() {
            System.out.println("I Like Lambda");
        }
    }
    
  • 函数式接口:一个接口只包含一个抽象方法

  • interface ILike{
        void lambda();
    }
    
  • public static void main(String[] args){
        ILove love = null;
        love = a->System.out.println("I Love You--->"+a);
        love.love(520);
    }
    interface ILove{
        void love(int a);
    }
    
  1. lambda表达式只能在有一行代码的情况下才能简化为一行
  2. 前提是接口为函数式接口
  3. 多个参数时也可以去掉参数类型,要去掉就都去掉

6.2.2 线程的五大状态

  1. 新生状态
  2. 就绪状态
  3. 阻塞状态
  4. 运行状态
  5. 死亡状态
  • 线程的停止:不建议使用官方的stop方法

    package com.thread;
    
    public class TestStop implements Runnable{
        //设置一个标志位
        private boolean flag = true;
        @Override
        public void run() {
            int i = 0;
            while (flag){
                System.out.println("running....Thread"+i++);
            }
        }
        //2.设置一个公开的方法停止线程,转换标志位
        public void stop(){
            this.flag = false;
        }
        public static void main(String[] args) {
            TestStop testStop = new TestStop();
            new Thread(testStop).start();
            for (int i = 0; i < 1000; i++) {
                System.out.println("main"+i);
                if (i==900){
                    //3.调用stop方法切换标志位,让线程停止
                    testStop.stop();
                    System.out.println("线程停止了");
                }
            }
        }
    }
    
    
  • Sleep

    • 打印当前系统时间

      package com.thread;
      
      import java.text.SimpleDateFormat;
      import java.util.Date;
      
      public class TestSleep2 {
          public static void CountDown() throws InterruptedException {
              int num = 10;
              while (true){
                  Thread.sleep(1000);
                  System.out.println(num--);
                  if (num<=0){
                      break;
                  }
              }
          }
      
          public static void main(String[] args) {
              Date startTime = new Date(System.currentTimeMillis());
              while (true){
                  try {
                      Thread.sleep(1000);
                      System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                      startTime = new Date(System.currentTimeMillis());
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
      
          }
      }
      
      
    • 倒计时

      package com.thread;
      
      public class TestSleep3 {
          public static void CountDown() throws InterruptedException {
              int num = 10;
              while (true){
                  Thread.sleep(1000);
                  System.out.println(num--);
                  if (num<=0){
                      break;
                  }
              }
          }
      
          public static void main(String[] args) {
              try {
                  CountDown();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
      

6.2.3 线程三大方法

  1. sleep(睡眠)

    • 不会释放锁
  2. yield(礼让)

    • 礼让不一定成功

    • package com.thread;
      
      public class Testyield {
          public static void main(String[] args) {
              Myyield myyield = new Myyield();
              new Thread(myyield,"a").start();
              new Thread(myyield,"b").start();
          }
      }
      class Myyield implements Runnable{
          @Override
          public void run() {
              System.out.println(Thread.currentThread().getName()+"线程开始执行");
              Thread.yield();//礼让
              System.out.println(Thread.currentThread().getName()+"线程停止执行");
          }
      }
      
      
  3. join(让线程等待)

    • package com.thread;
      
      public class TestJoin implements Runnable{
          @Override
          public void run() {
              for (int i = 0; i < 100; i++) {
                  System.out.println("线程VIP来了"+i);
              }
          }
      
          public static void main(String[] args) {
              TestJoin testJoin = new TestJoin();
              Thread thread = new Thread(testJoin);
              thread.start();
      
              for (int i = 0; i < 1000; i++) {
                  if (i==200){
                      try {
                          thread.join();
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
                  System.out.println("main"+i);
              }
          }
      }
      
    • 会释放锁

6.2.4 观察线程状态

  • package com.thread;
    
    //观察线程的状态
    public class TestState {
        public static void main(String[] args) {
            Thread thread = new Thread(()->{
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("/");
            });
    
            //观察状态
            Thread.State state = thread.getState();
            System.out.println(state);//新生态
    
            //管查启动后
            thread.start();
            state=thread.getState();
            System.out.println(state);//运行态
    
            while (state!=Thread.State.TERMINATED){//只要state不停止
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                state=thread.getState();//更新状态
                System.out.println(state);
    
            }
        }
    }
    
    

6.3.1 线程的优先级

  • 线程优先级高不一定先执行,但是先执行的可能性更高,执不执行看CPU

  • 优先级范围:1-10(默认为5)

  • 优先级数字越大优先级越高

  • package com.thread;
    
    public class TestPriority {
        public static void main(String[] args) {
            MyPriority myPriority = new MyPriority();
            Thread t1 = new Thread(myPriority);
            Thread t2 = new Thread(myPriority);
            Thread t3 = new Thread(myPriority);
            Thread t4 = new Thread(myPriority);
    
            System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
            t1.start();
            t2.setPriority(1);
            t2.start();
            t3.setPriority(4);
            t3.start();
            t4.setPriority(Thread.MAX_PRIORITY);
            t4.start();
        }
    
    }
    class MyPriority implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
        }
    }
    
    

6.3.2 守护线程

  • thread.setDaemon(true);//设置守护线程
    
  • package com.thread;
    
    //测试守护线程
    //上帝守护你
    public class TestDaemon {
        public static void main(String[] args) {
            God god = new God();
            you you = new you();
    
            Thread thread = new Thread(god);
            thread.setDaemon(true);//设置守护线程
    
            thread.start();
            Thread thread1 = new Thread(you);
            thread1.start();
    
        }
    
    }
    
    //上帝
    class God implements Runnable{
        @Override
        public void run() {
            while (true){
                System.out.println("上帝保护者你");
            }
        }
    }
    
    //你
    class you implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 36500; i++) {
                System.out.println("你一生都开心的活着");
            }
            System.out.println("Goodbye World!");
        }
    }
    

6.4.1 线程同步

  • 锁机制(synchronized)

  • 线程不安全两大案例:

    • 买票

    • package com.syn;
      
      //买票
      public class UnsafeBuyTicket {
      
          public static void main(String[] args) {
              BuyTicket station = new BuyTicket();
              new Thread(station,"1").start();
              new Thread(station,"2").start();
              new Thread(station,"黄牛").start();
          }
      }
      class BuyTicket implements Runnable{
          private int ticketNums = 10;
          boolean flag = true;
          @Override
          public void run() {
              while (flag){
                  buy();
              }
          }
          private void buy(){
              if (ticketNums<=0){
                  flag=false;
                  return;
              }
              try {
                  Thread.sleep(100);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println(Thread.currentThread().getName()+"拿到了"+ticketNums--);
          }
      }
      
    • 取钱

    • package com.syn;
      
      public class UnsafeBank {
          public static void main(String[] args) {
              Account account = new Account(100, "基金");
      
              Drawing me = new Drawing(account, 50, "你");
              Drawing wife = new Drawing(account, 100, "wife");
              me.start();
              wife.start();
          }
      }
      //账户
      class Account{
          int money;
          String name;
      
          public Account(int money, String name) {
              this.money = money;
              this.name = name;
          }
      }
      //银行:模拟取款
      class Drawing extends Thread{
          Account account;
          int drawingMoney;
          int currentMoney;
          public Drawing(Account account,int drawingMoney,String name){
              super(name);
              this.account=account;
              this.drawingMoney=drawingMoney;
          }
          @Override
          public void run() {
              if (account.money<drawingMoney) {
                  System.out.println(Thread.currentThread().getName() + "钱余额不足");
                  return;
              }
              try {
                  Thread.sleep(100);//放大问题的发生性
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              account.money -=drawingMoney;
              currentMoney+=drawingMoney;
              System.out.println(account.name+"目前余额为:"+account.money);
              System.out.println(this.getName()+"手里的钱为:"+this.currentMoney);
          }
      }
      
  • 同步:

    • //obj是指需要锁的资源,需要增加或者减少
      synchronized(obj){
          //这里面写线程的操作
      }
      

6.4.2 死锁

  • 产生死锁的四个必要条件:

    1. 互斥条件:一个资源每次只能被一个线程使用
    2. 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
    3. 不剥夺条件:线程已获得的资源,在未使用完之前,不能强行剥夺。
    4. 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。
  • package com.syn;
    
    public class DeadLock {
        public static void main(String[] args) {
            Makeup g1 = new Makeup(0, "灰姑娘");
            Makeup g2 = new Makeup(1, "白雪公主");
            g1.start();
            g2.start();
        }
    }
    //口红
    class Lipstick{
    }
    class Mirror{
    }
    class Makeup extends Thread{
        //用static来保证资源只有一份
        static Lipstick lipstick = new Lipstick();
        static Mirror mirror = new Mirror();
        int choice;
        String girlName;
        Makeup(int choice,String girlName){
            this.choice=choice;
            this.girlName=girlName;
        }
        @Override
        public void run() {
            try {
                makeup();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        private void makeup() throws InterruptedException {
            if (choice==0){
                synchronized (lipstick){
                    System.out.println(this.girlName+"获得了口红的锁");
                    Thread.sleep(1000);
                    synchronized (mirror){
                        System.out.println(this.girlName+"获得了镜子的锁");
                    }
                }
            }else {
                synchronized (mirror){
                    System.out.println(this.girlName+"获得了镜子的锁");
                    Thread.sleep(1000);
                    synchronized (lipstick){
                        System.out.println(this.girlName+"获得了口红的锁");
                    }
                }
            }
        }
    }
    
  • 在上面的例子中,g1和g2分别获得了口红和镜子的锁,还想获得对方的锁,导致了死锁的发生

  • 解决办法:

    • private void makeup() throws InterruptedException {
              if (choice==0){
                  synchronized (lipstick){
                      System.out.println(this.girlName+"获得了口红的锁");
                      Thread.sleep(1000);
                  }
                  synchronized (mirror){
                      System.out.println(this.girlName+"获得了镜子的锁");
                  }
              }else {
                  synchronized (mirror){
                      System.out.println(this.girlName+"获得了镜子的锁");
                      Thread.sleep(1000);
                  }
                  synchronized (lipstick){
                      System.out.println(this.girlName+"获得了口红的锁");
                  }
              }
          }
      
    • 将第二同步锁放在第一个同步锁之外,这样就不会出现拥有一个锁的同时还想获得另一个锁的情况,就能有效的解决死锁问题

6.4.3 Lock(锁)

  • ReentrantLock(可重入锁)

  • class A{
        private final ReentrantLock lock = new ReentrantLock();
        public void m(){
            lock.lock();
            try{
                //保证线程安全的代码;
            }finally{
                lock.unlock();
                //如果同步代码有异常,要将unlock()写入finally语句块
            }
        }
    }
    

6.4.4 wait()和notify()

  1. wait()表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
  2. notify()唤醒一个处于等待状态的线程
  3. wait(long timeout)
  4. notifyAll()唤醒同一个对象上所有调用wait()方法的线程,优先级高的线程有限调度
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值