栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

多线程学习

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

多线程学习

多线程学习 1.概念

程序·进程·线程

进程是指执行一个程序的执行过程,一个进程会有多个线程。

即一个任务就可以理解为一个线程,线程是CPU调试和执行的单位。

2.前期准备

配置commons io工具包,里面提供了大量与文件相关的类

    百度搜索commons io,选择下载最新版本的

    在项目src目录下创建lib包,把下载好的文件放进去

      然后右键lib包,点击add Build Path就可以使用了

    3.线程创建
      方法一:继承Thread类

      子类继承Thread类具备多线程能力

      启动线程:子类对象.start()

      不建议使用,避免OOP单继承局限性

      //创建线程方法一:继承Thread类,重写run方法,调用start方法开启线程
      //总结:线程开始不一定立即执行,由CPU调度执行
      public class TestThread01 extends Thread {
          @Override
          public void run() {
              //run方法线程体
              for (int i = 0; i < 20; i++) {
                  System.out.println("我在学习代码--" + i);
              }
          }
      
          public static void main(String[] args) {
              //main方法线程,主线程体
              TestThread01 textThread01 = new TestThread01();
              //调用start方法开始线程
              textThread01.start();
      
              for (int i = 0; i < 20; i++) {
                  System.out.println("我在学习多线程--" + i);
              }
          }
      }
      
      方法二:实现Runnable接口

      实现Runnable具备多线程能力

      启动线程:传入目标对象+Thread对象.start()

      推荐使用,避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

      //创建线程方法2:实现runnable接口,重新run方法,执行线程需要丢人runnable接口实现类,调用start方法启动.
      public class TestThread03 implements Runnable{
          @Override
          public void run() {
              //run方法线程体
              for (int i = 0; i < 100; i++) {
                  System.out.println("我在学习代码--" + i);
              }
          }
      
          public static void main(String[] args) {
              //创建runnable接口实现类对象
              TestThread03 testThread03 = new TestThread03();
      
              //创建线程对象,通过线程对象来开启我们的线程,代理
              Thread thread = new Thread(testThread03);
              thread.start();
      
              for (int i = 0; i < 1000; i++) {
                  System.out.println("我在学习多线程--" + i);
              }
          }
      }
      

    !多个线程操作同一个资源的情况下,线程不安完全,出现紊乱。

      方法三:实现Callable接口(补充的,了解即可!)

    实现Callable接口,需要返回值类型

    重写call方法,需要抛出异常

    创建目标对象

    创建执行服务:ExecutorService ser =Executors.newFixedThreadPool(1);

    提交执行:Future result1 = ser.submit(t1);

    获取结果:boolean r1 = result1.get()

    关闭服务:ser.shutdownNow();

    //创建执行服务
    ExecutorService ser = Executors.newFixedThreadPool(3);
    
    //提交执行
    Future r1 = ser.submit(t1);
    Future r2 = ser.submit(t2);
    Future r3 = ser.submit(t3);
    
    //获取结果
    boolean rs1 = r1.get();
    boolean rs2 = r2.get();
    boolean rs3 = r3.get();
    
    //关闭服务
    ser.shutdownNow();
    
    //打印
    System.out.println(rs1);
    System.out.println(rs2);
    System.out.println(rs3);
    

    4.线程状态
      线程停止
      1.建议线程正常停止-->利用次数,不建议死循环
          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){
                      //调用stop方法切换标志位,让线程停止
                      testStop.stop();
                      System.out.println("线程该停止了");
                  }
              }
          }
      
      2.建议使用标志位-->设置一个标志位
          //设置一个标志位
          private boolean flag = true;
      
          @Override
          public void run() {
              int i = 0;
              while (flag){
                  System.out.println("run...Thread"+i);
              }
          }
          //设置一个公开的方法停止线程,转换标志位
          public void stop(){
              this.flag = false;
          }
      
      3.不要使用stop、destroy等过时的方法或者JDK不建议使用的方法
      
      线程休眠_sleep

      模拟网络延时

       //模拟延时
                  try {
                      Thread.sleep(200);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
      

      模拟倒计时

       //10秒倒计时
              try {
                  tenDown();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
      
          //模拟倒计时,,
          public static  void tenDown() throws InterruptedException {
              int num = 10;
              do {
                  Thread.sleep(1000);
                  System.out.println(num--);
              } while (num > 0);
          }
      }
      
      线程礼让_yield
      //测试线程礼让
      //线程礼让,是让当前正在执行的线程停止,但不阻塞
      //礼让不一定成功,看CPU心情
      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()+"线程停止执行");
          }
      }
      
      线程强制执行_join
      //测试join方法
      //想象为插队
      public class TestJoin implements Runnable{
          @Override
          public void run() {
              for (int i = 0; i < 200; i++) {
                  System.out.println("线程vip来了..."+i);
              }
          }
      
          public static void main(String[] args) throws InterruptedException {
              TestJoin testJoin = new TestJoin();
              Thread thread = new Thread(testJoin);
              thread.start();
      
              //主线程
              for (int i = 0; i < 500; i++) {
                  if (i == 100){
                      thread.join();//插队
                  }
                  System.out.println("线程main"+i);
              }
          }
      }
      
      线程的优先级

      优先级低只是意味着获得调度的概率低,并不是优先级低就不被调用,还嘚看CPU的调度使用getPriority()方法获取优先值,setPriority()方法设置优先值 守护线程

      //测试守护线程
      
      public class TestDaemon {
          public static void main(String[] args) {
              God god = new God();
             
              Thread thread = new Thread(god);
              thread.setDaemon(true);//默认是false表示用户线程,正常的线程都是用户线程
              thread.start();
    
    5.线程同步

      同步块:synchronized(Obj){ },Obj称之为同步监视器

      Obj可以是任何对象,但是推荐使用共享资源作为同步监视器

      同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身

      //synchronized 同步方法,锁的是this
      private synchronized void buy() throws InterruptedException {
          if (ticketNums<=0){
              flag = false;
              return;
          }
          //模拟延时
          Thread.sleep(100);
      
          //买票
          System.out.println(Thread.currentThread().getName()+"拿到了"+ticketNums--);
      }
      

      同步监视器的执行过程

      第一个线程访问,锁定同步监视器,执行其中代码第二个线程访问,发现同步监视器被锁定,无法访问第一个线程访问完毕,解锁同步监视器第二个线程访问,发现同步监视器没有锁,然后锁定并访问

      //synchronized 锁的对象应该是变化的量,需要增删改的量
      synchronized (account){//判断有没有钱
          if (account.money-drawingMoney<0){
              System.out.println(Thread.currentThread().getName()+"钱不够了,取不了");
              return;
          }
      
          //模拟延时
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      
          //卡内余额=余额-你取的钱
          account.money = account.money-drawingMoney;
          //你现在手里的钱=手头的钱+你取的钱
          nowMoney = nowMoney+drawingMoney;
      
          //打印
          System.out.println(account.name+"余额为:"+account.money);
          System.out.println(this.getName()+"手里的钱:"+nowMoney);//这里this.getName() = Thread.currentThread().getName()
          }
      
    6.死锁
      必要条件

    (1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求该资源,则请求者只能等待,直至占有该资源的进程用毕释放。
    (2)请求和保持条件:指进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其它进程占有,此时请求进程阻塞,但又对自己获得的其它资源保持不放。
    (3)不剥夺条件:指进程已获得资源,在使用完之前,不能被剥夺,只能在使用完时由自己释放。
    (4)环路等待条件:指在发生死锁时,必然存在一个进程—资源的环形链,即进程集合(P0,P1,P2,…,Pn)中的P0正在等待一个P1占用的资源;P1正在等待一个P2占用的资源,……,Pn正在等待已被P0占用的资源。

      死锁例子及解决办法

      //多个线程互相抱着对方需要的资源,然后形成僵持
      //化妆:互相持有对方的锁,就是需要拿到对方的钱
      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(2000);
                  synchronized (lipstick){
                      System.out.println(this.girlName+"获得口红的锁");
                  }
              }
      
              //改为下面情况就可以破解死锁,放在外面,不能同时拥有两个锁
              
          }
      }
      

      Lock锁

      1)Lock锁是个Java类,用ReentrantLock()方法定义,用法如下

      //定义lock锁
      private final ReentrantLock lock = new ReentrantLock();
      
      try {
          lock.lock();//加锁
          while (ticketNums>0) {
              try {
                  Thread.sleep(100);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              System.out.println(ticketNums--);
          }
      }finally {
          lock.unlock();//解锁
      }
      

      2)lock锁和synchronized区别

      首先synchronized是java内置关键字,在jvm层面,Lock是个java类;synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

    7.线程协作

      生产者—消费者模型1–>管程法,借助缓冲区

      理解:

      生产者将生产好的数据放入缓冲区 , 消费者从缓冲区拿出数据。通过判断缓冲区大小来决定生产者何时生产,消费者何时消费。

      模型:

      生产者 : 负责生产数据的模块 (可能是方法 , 对象 , 线程 , 进程) ;消费者 : 负责处理数据的模块 (可能是方法 , 对象 , 线程 , 进程) ;缓冲区 : 消费者不能直接使用生产者的数据 , 而是通过缓冲区拿出数据。

    ​ 生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据

    //测试:生产者消费者模型-->利用缓冲区解决:管程法
    
    //需要对象:生产者,消费者,缓冲区,产品
    public class TestPC {
        public static void main(String[] args) {
            SynContainer synContainer = new SynContainer();
            new Productor(synContainer).start();
            new Consumer(synContainer).start();
        }
    }
    //生产者
    class Productor extends Thread{
        SynContainer container;
        public Productor(SynContainer container){
            this.container = container;
        }
        //生产
        @Override
        public void run(){
            for (int i = 0; i < 100; i++) {
                container.push(new Hamburger(i));
                System.out.println("生产了"+i+"只鸡");
            }
        }
    }
    //消费者
    class Consumer extends Thread{
        SynContainer container;
        public Consumer(SynContainer container){
            this.container = container;
        }
        //消费
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("消费了-->"+container.pop().id+"只鸡");
            }
        }
    }
    //产品
    class Hamburger{
        int id;//产品编号
        public Hamburger(int id){
            this.id = id;
        }
    }
    //缓冲区
    class SynContainer{
        //容器大小
        Hamburger[] hamburgers = new Hamburger[10];
        //计数器
        int counter;
        //生产者放入产品,此处嘚用上同步
        public synchronized void push(Hamburger hamburger){
            //如果容器满了,则等待消费
            if (counter == hamburgers.length){
                //通知消费者消费
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //如果没有满,则生产产品
            hamburgers[counter] = hamburger;
            counter++;
            //可以通知消费者消费了
            this.notifyAll();
        }
        //消费者取出产品,此处嘚用上同步
        public synchronized Hamburger pop (){
            //判断能否消费
            if (counter==0){
                //等待生产者生产
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //如果可以消费了
            counter--;
            Hamburger hamburger = hamburgers[counter];
            //通知生产者生产
            this.notifyAll();
            return hamburger;
        }
    }
    

      生产者—消费者模型2–>信号灯法,借助标志位

      理解:

      当标志位为真时,生产者生产,接着把标志位变为假,等待消费者消费,消费完后接着把标志位变为真,如此一直循环下去。

      模型:

      生产者:负责生产数据的模块(这里的模块可能是:方法、对象、线程、进程)消费者:负责处理数据的模块(这里的模块可能是:方法、对象、线程、进程)标志位:生产者消费者使用同一资源,他们之间有个标志位,类似于信号灯的作用,通过信号灯控制生产者和消费者的循环使用

    //测试:生产者消费者模型-->利用标志位解决:信号灯法
    public class TestPC2 {
        public static void main(String[] args) {
            TV tv = new TV();
            new Player(tv).start();
            new Watch(tv).start();
        }
    }
    //生产者-->演员
    class Player extends Thread{
        TV tv;
        public Player(TV tv){
            this.tv = tv;
        }
        @Override
        public void run() {
            for (int i = 0; i < 50; i++) {
                if (i%2==0){
                    this.tv.play("小品节目表演中");
                }else {
                    this.tv.play("歌唱大赛进行中");
                }
            }
        }
    }
    //消费者-->观众
    class Watch extends Thread{
        TV tv;
        public Watch(TV tv){
            this.tv = tv;
        }
        @Override
        public void run() {
            for (int i = 0; i < 50; i++) {
                this.tv.watch();
            }
        }
    }
    //产品-->节目
    class TV{
        //表演的节目
        String program;
        //设置标志位
        boolean flag = true;//T,演员表演,观众等待;F,观众观看,演员等待
        //表演
        public synchronized void play(String program){
            //如果标志位为F,演员等待
            if (!flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("演员表演了-->"+program);
            //通知观众观看
            this.notifyAll();
            this.program = program;
            this.flag = !flag;
        }
        //观看
        public synchronized void watch(){
            //如果标志位为T,观众等待
            if (flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("观众观看了:"+program);
            //通知演员表演
            this.notifyAll();
            this.flag = !flag;
        }
    }
    

    学习资源路径

    转载请注明:文章转载自 www.mshxw.com
    本文地址:https://www.mshxw.com/it/723186.html
    我们一直用心在做
    关于我们 文章归档 网站地图 联系我们

    版权所有 (c)2021-2022 MSHXW.COM

    ICP备案号:晋ICP备2021003244-6号