JAVA-多线程

1、 线程和进程

进程和线程的区别?

什么是进程

​ 任务管理器中看到的各种,如:QQ、网易云音乐、Steam、dffice办公软件、IDEA、任务管理器自己等等

​ 这一些我们称之为进程。

什么是线程

​ 线程是每一个进程中可能存在多个线程多并执行,

​ 我们生活中的例子:田径比赛,整体看作进程,每位参赛选手看作线程,只有当所有参赛选手都跑完,这一个进程才算完全结束。

​ 一个进程是由一个或多个线程组成的。

​ 魔兽世界中可能存在多个线程,每个线程都可以回去到相同的魔兽世界的资源,比如用用户登录信息。但是他不可能获取别的进程,如网易云音乐。魔兽世界中的线程不能区播放网易云音乐进程中的歌曲。

2、 多线程

 

比如,一个进程开始运作,同时开辟四条线程:做登录、打广告、放视频、最后加载页面。

CPU存在单核单线程CPU,多核多线程CPU。

首先如果你的CPU是支持多线程,俺么我们开盘多线程肯定很好理解。

但是如果你的CPU是单线程CPU此时你就只能单线程么?

答案是否定的

 

3、 Thread类

  public static void main(String[] args) {
      //通过Thread.currentThread()方法获取当前的线程
      Thread t=Thread.currentThread();
      //通过线程对象t调用他的getName()方法,过去当前线程的名称
      System.out.println(t.getname());
      //通过setName可以修改线程的名称
      t.setName("这个main方法的名字被我改了");
      System.out.println(t.getName());
  }

手动创建自己的线程有两种方法:

第一个继承Thread类

第二个实现Runnable接口

4、继承Thread类实现多线程


  
  //第一步继承Thread类
  public class MyThread1 extends Thread{
      public MyThread1(String name){
          super(name);
      }
      
      //第二步,我们通过重写run()方法来实现线程的自定义
      @override
      public void run(){
          for(int i=0;i<1000;i++){
              System.out.println(Thread.currentthread().getName()+i);
          }
      }
      
      public static void main(String[] args){
          //第三步,实例化我们继承的Thread类的类
          MyThread1 myThread1=new MyThread1("我的第一个线程");
          MyThread1 myThread2=new MyThread1("我的第二个线程");
          MyThread1 myThread3=new MyThread1("我的第三个线程");
          //第四步,通过start()方法去调用run()方法的内容
          //虽然我们没有去重写start()方法,但是Thresd类会通过start方法调用run方法
          //只有通过start()方法调用才是真正起到了开辟一个新线程
          //如果是用run()调用,那么依旧是在主线中跑的
          myThread1.start();
          myThread2.start();
          myThread3.start();
          
          for(int i=0;i<1000;i++;){
              System.out.println(Thread.currentThread().getName()+i);
          }
      }
  }
 

5、 实现Runnable接口实现多线程


  
  //第一步,新增一个类实现Runnable接口
  public class MyThread2 implements Runnable{
      //第二步,重写run()方法
      @Override
      public void run(){
          for(int i=0;i<10000;i++){
              System.out.printlm(Thread.currentThread().gatName()+i)
          }
      }
      
      public static void main(String[] args){
          //第三步,将实现Runnable接口的类实例化
          MyThread2 myThread1=new MyThread2();
          MyThread2 myThread2=new MyThread();
          MyThread2 myThread3=new MyThread();
          
          //第四步,将实例化的对象参数传入Thread类实例化的对象
          Thread t1=new Thread(myThread1,"1线程");
          Thread t2=new Thread(myThread2,"2线程");
          Thread t3=new Thread(myThread3,"3线程");
          
          //第三步和第四步可一步实现,一步到位
          //Thread t1=new Thread(new MyThread(),"1线程");
          //Thread t2=new Thread(new MyThread(),"2线程");
          //Thread t3=new Thread(new MyThread(),"3线程");
          
          //第五步,通过Thread类的实例化对象调用他的start()方法
          t1.start();
          t2.start();
          t3.start();
          
          for(int i=1;i<10000;i++){
              System.out.println(Thread.currentThread().getName()+i);
          }
      }
  }
​ 可以使用Landba表达式去实例化一个匿名内部类Runable接口,之后正常创建线程

  public static void main(String[] args){
      //匿名内部类实现
      Thread t=new Thread(new Runnable);{
      @Override
          public void run(){
              for(int i=0;i<10;i++){
                  System.out.println(Thread.currentThread().getName()+i);
              }
          }
      });
      //也可以通过Lamdba表达式
      Thread t1=new Thread(()->{
          for(int i=;i<0;i++){
              System.out.println(Thread.currentThread().getName()+i);
          }
      });
  ​
      t.setName("匿名内部类的方式调用");
      t1.setName("Lamdba表达式实现接口");
      t.start();
      t1.start();
  }
 

6、 继承Thread类和实现Runnable接口的比较

总结一下,无论你是Thread类的继承,还是Runnable接口的实现,你都需要通过Thread这个类的start();方法区实现多线程的使用.如果你是调用的 run();方法,那么你肯定是单线程。

第二点,Thread类和Runnable接口实现的不同,由于java类只存再单继承,多实现,所以我们优先推荐大家使用接口的方式调用多线程,因为你继承了thread类,你就无法继承别的类,可能这个方法对于后期的代码扩展维护存在部分的问题。

切记,实现Runnable接口是将他当作参数传入新的Thread类中!!!!

7、 start()和run()的区别

调用start()方法

 

调用run()方法

 

8、 生命周期

 

9、 线程调度方法

9.1、 stop(); 停止线程

​ 这个方法会让线程进入死亡状态。注意!这个方法是过时的方法,我们不应该这样做,这样去做有可能会造成你线程中的数据丢失,因为我们run方法后面可能存在保存数据的内容,但是由于某些奇奇怪怪的原因,在没有执行到保存数据的方法的时候,县城就被其余地方杀死了,进入死亡状态,期间的所有数据均出现丢失。

  public static void main(String[] args){
      public MyThread1(String name){
          super(name);
      }
      #Override
      public void run(){
          System.out.print("线程启动");
          for(int i=0;i<100000;i++){
              System.out.println(Thread.currentThread().getName()+i);
          }
          System.out.print("线程代码执行结束");
      }
      
      public static void main(String[] args){
          MyThread1 m=new MyThread1("test");
          m.start();
          try{
              //线程休眠500ms(毫秒)
              Thread.sleep(500);
          }catch(InterruotedException e){
              e.printStackTrace();
          }
          //类似于任务管理器结束进程,没保存的数据就丢失了
          m.stop();
      }
  }

科学停止线程方法:


  public class Mythread3 extends Thread{
      //我们有一个标记位置,这个线程的开关
      static boolean flag=true;
      @Override
      public void run(){
          while(true){
              if(!flag){
                  //进入这个方法块,说明我们的代码线程被关闭了,我们可以再这里执行保存功能
                  System.out,println("执行保存代码");
                  return;
              }
              System.out.println("------------------------------")
          }
      }
      public static void main(String[] args){
          MyThread3 thread3=new MyThread();
          thread3.strat();
          try{
              Thread.sleep(2000);
          }catch(InterruptedException e){
              e.printStackTrace();
          }
          //相当于关上我们线程的开关
          MyThread3.flag=flase;
      }
  }

9.2、 设置优先级 setPriority();


  public static void main(String[] args){
      MyThread1 thread1=new MyThread("1号");
      MyThread1 thread1=new MyThread("2号");
      MyThread1 thread1=new MyThread("3号");
      MyThread1 thread1=new MyThread("4号");
      //最大值不能大于MAX_PRIORITY(10),最小值不能小于MIN_PRIORITY(1)
      //默认是5
      //数字越大代表越容易抢到资源,但是不是说一定优先级高
      thread1.setPriority(10);
      thread2.setPriority(Thread.MAX_PRIORITY);//10
      thread3.setPrioriy(6);
      thread4.setPriority(Thread.MIN_PRIORITY);//1
      thread1.start();
      thread2.start();
      thread3.start();
      thread4.start();
  }

9.3、 线程休眠 sleep();

相当于让线程进入阻塞状态


  public static void main(String[] args){
      MyThread1 m=new MyThread1("test");
      m.start();
      try{
          //线程休眠500ms(毫秒)
          //线程休眠属于静态方法,如果我们是使用对象去调用,他依旧会把他视同为类
          //如
          m.sleep(500);
          //此时是使用m对象调用这个方法,但是编译之后还是会变成如下代码;
          //Thread.sleep(500);
          //所以休眠的线程依旧是当前写在这里的线程,而不是m这个对象的线程
      }catch(InterruptedException e){
          e.printStackTrace();
      }
      //类似于任务管理器结束进程,没保存的数据丢失了
      m.stop();
      }
  }

9.4、 强制运行(阻塞所有其他人)join();

join();方法相当于阻塞当前的线程,至于哪个对象调用,说明需要等到对应对象跑完(进入死亡阶段),那他才能继续跑


  public static void main(String[] args){
      MyThread1 myThread1=new MyThread1("我的第一个线程");
      MyThread1 myThread2=new MyThread1("我的第二个线程");
      myThread1.start();
      myThread2.start();
      for(int i=0;i<10000;i++){
          System.out.println(Thread.currentThread().getName()+i);
          if(i==5000){
              try{
                  //相当于当主线程的循环执行到i==5000时,我们让当前线程阻塞
                  //让myThread1加入
                  //知道myThread1跑完了才会让
                  //相当于我们对mian方法发令“你别动,然他先加入”
                  myThread1.join();
                  
                  //这一行代码写不写的区别在于:
                  //看我们main线程何时从堵塞状态恢复到就绪状态
                  //如果不写,那就是myThread1跑完,他就回复就绪
                  //然后main和myThread2一起争夺资源
                  //如果写了,就是要等myThread2跑完
                  myThread2.join();
              }catch(InterruptedException e){
          e.printStackTrace();
              }
          }
      }
  }

9.5、 线程的礼让 yield ();

相当于让线程进入就绪状态

  public class Mythread4 extends Thread{
      @Override
      public void run(){
          for(int i;i<10;i++){
              if(i%3==0){
                  System.out.println("线程象征性执行了一次礼让");
                  Thread.yield();
              }
              System.out.println("thread4对象的"+i);
          }
      }
      public static void main(String[] args){
          MyThread4 myThread4=new MyThread4();
          thread4.setPriority(1);
          thread4.start();
          for(int i=0;i<100;i++){
              System.out.println("mian方法的"+i);
          }
      }
  }

由于是进入了就绪状态,所以其实还是有点看运气,不知道是哪个时点

9.6、 打断睡眠 interrup();

  public class Mythread5 extends Thread{
      @Override
      public void run(){
          for(int i=0;i<1000;i++){
              //写睡眠时间或者类似这种写法需要毫秒值时,建议使用如下写法
              try{
                  //会执行到i==500时休眠一年
                  Thread.sleep(1000*60*60*24*365);
              }catch(InterruptedExceptoin e){
                  System.out.println("这里的睡眠被中断了,需要做什么你就做什么。");
              }
          }
          System.out.println("thread5对象的"+i);
      } 
      public static void main(String[] args){
          MyThread5 myThread5=new MyThread4();
          thread5.start();
          try{
              Thread.sleep(5000);
          }catch(InterrupedException e){
              e.printStackTrace();
          }
          //执行把thread5叫醒
          //相当于让我们的thread5再执行睡眠时抛出一个InterruptedException
          //通过try-catch异常处理机制,实现睡眠被唤醒
          thread5.interrupt();
      }
  }

10、 需要解决线程并发是发生的数据冲突问题

​ 如果我们需要使用多线程,这些数据存在共同性,可能两个线程同时操作一个数据,比如银行取钱,2张卡是一个账户,两个线程对应一个账号,这个时候我们就要考虑会不会因为并发(多个线程异步执行)会造成数据影响问题。

同步执行

​ 我跑,你跟在我的后头跑,我跑到终点,你再跑到重点

异步执行

​ 我跑我的,你跑你的,我在错做数据的时候你也在操作

异步的效率更高,但是由于数据是异步的,无法同步,所以需要考虑数据是否会影响结果


  public class RobTicket implements Runnable{
      private int count=10;//总票数
      private int num=1;//当前票
      
      @Override
      public void run(){
          while(true){
              if(count>0){
                  System.out.println(Thread.currentThread().getName()+
                                     "抢到了第"+num+"张票,余票数还剩"+count+"张");
                  num++;
                  count--;
              }else{
                  System.out.println(Thread.currentTread().getName()+
                                     "没抢到票,因为没票了");
                      return;
              }
              try{
                  Tread.sleep(500);
              }catch(InterrupedException e){
                  e.printStckTracr();
              }
          }
      }
      
      public static void main(String[] args){
          RobTicket robTicket =new RobTicket();
          Thread t1=new Thread(robTicket);
          Thread t2=new Thread(robTicket);
          Thread t3=new Thread(robTicket);
          
          t1.setName("淘票票");
          t2.setName("12306");
          t3.setName("大麦网");
          
          t1.start();
          t2.start();
          t3.start();
      }
  }

11、synchronized关键字解决问题


  public class RobTicket1 implements Runnable{
      private int count=10;//总票数
      private int num=1;//当前票
      
      @Override
      public void run (){
          while(count>0){
              sale();
          }
      }
      
      public synchronized void sale(){
          if(count>0){
              System.out.println(Thread.currentThread().getName()+
                                 "抢到了第"+num+"张票,余票数还剩"+count+"张");
              num++;
              count--;
          }else{
              System.out.println(Thread.currentTread().getName()+
                                 "没抢到票,因为没票了");
                  return;
          }
          try{
              Tread.sleep(500);
          }catch(InterrupedException e){
              e.printStckTracr();
          }
      }
  ​
      
      public static void main(String[] args){
          RobTicket robTicket =new RobTicket();
          Thread t1=new Thread(robTicket);
          Thread t2=new Thread(robTicket);
          Thread t3=new Thread(robTicket);
          
          t1.setName("淘票票");
          t2.setName("12306");
          t3.setName("大麦网");
          
          t1.start();
          t2.start();
          t3.start();
      }
  }

第二种写法:

注意核心难点 :synchronized() 括号内的内容!!!

  
  public class RobTicket2 implements Runnable{
      private int count=10;//总票数
      private int num=1;//当前票
      
      @Override
      public void run(){
          while(true){
              //()括号中写的内容比较复杂
              //这里面需要写的是一个对象,我们会根据这个对象的内存地址去创建一把锁
              //如果你们的这个对象是一样的,那么,就需要使用这一把锁;如果是不一样的,就相当于不同的锁
              //是否相同对象,如果是相同的对象就要一起排队,如果是不同的对象就比用一起排队
              synchronized(this){
                  if(count>0){
                      System.out.println(Thread.currentThread().getName()+
                                         "抢到了第"+num+"张票,余票数还剩"+count+"张");
                      num++;
                      count--;
                  }else{
                      System.out.println(Thread.currentTread().getName()+
                                         "没抢到票,因为没票了");
                          return;
                  }
                  try{
                      Tread.sleep(500);
                  }catch(InterrupedException e){
                      e.printStckTracr();
                  }
              }
          }
  }
      
      public static void main(String[] args){
          RobTicket robTicket =new RobTicket();
          Thread t1=new Thread(robTicket);
          Thread t2=new Thread(robTicket);
          Thread t3=new Thread(robTicket);
          
          t1.setName("淘票票");
          t2.setName("12306");
          t3.setName("大麦网");
          
          t1.start();
          t2.start();
          t3.start();
      }
  }

12、 附加内容

java.util.concurrent提供了一系列方便并发编程的类

阻塞队列BolckingQueue

延迟队列DelayQueue

闭锁CountDownQueue

线程池ExcutorService

…………

第三种创建线程的方式

Callable:能够获取线程执行结果——该结构使用call调用,可以存在返回值,而Runnable时无返回值的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值