JAVA多线程(入门)

JAVA多线程

什么是进程(Process)

  • 正在运行的程序,是系统进行资源分配的基本单位
  • 目前操作系统都是支持多进程的,可以执行多个进程,通过进程ID区分
  • 单核CPU在同一个时刻,只能运行一个进程;宏观并行,微观串行

什么是线程

  • 线程,又称轻量级进程。线程中的一条执行路径,也是CPU的基本调度单位。
  • 一个进程由一个或多个线程组成,彼此间完成不同的工作,同时执行,称为多线程

进程与线程的区别

  • 进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位
  • 一个程序运行后至少有一个进程
  • 一个进程可以包含多个线程,但是至少需要一个线程,否则这个进程是没有意义的
  • 进程间不能共享数据段地址,但同进程的线程之间可以

线程的组成

  • 任何一个线程都具有基本的组成部分
  • CPU时间片:操作系统(OS)会为每个线程分配执行时间
  • 运行数据:
    • 堆空间:存储线程需要使用的对象,多个线程可以共享堆中的对象
    • 栈空间:存储线程需使用的局部变量,每个线程都拥有独立的栈
  • 线程的逻辑代码

线程的特点

  • 线程抢占式执行

    • 效率高

    • 防止单一线程长时间独占CPU

  • 在单核CPU中,宏观上同时执行,微观上顺序执行

创建线程

继承Thread类

  • 继承Thread方法
  • 重写run()方法
  • 创建子类对象
  • 调用start()启动子线程
获取和修改线程名称
  • 获取线程ID和线程名称

    • 在Thread的子类中调用this.getId()或this.getName()
    • 使用Thread.currentThread().getId()和Thread.currentThread().getName()
  • 修改线程名称

    • 调用线程对象的setName()方法
    • 使用线程子类的构造方法赋值
  • package com.wly.Dome;
    
    //创建线程的方式一:继承Thread类,重新run()方法,调用start开启线程
    //线程开启不一定立即执行,由cpu调度
    public class TestDome extends Thread{
        public TestDome() {
        }
    
        public TestDome( String name) {
            super(name);
        }
    
        public void run(){
            for (int i = 0; i < 20; i++) {
                //继承Thread类使用该类的方法
    //            System.out.println("线程ID:"+this.getId()+"线程名:"+this.getName()+"子线程----------"+i);
                //获取当前线程.get方法获取ID
                System.out.println("线程ID:"+Thread.currentThread().getId()+"线程名:"+Thread.currentThread().getName());
            }
    
        }
        public static void main(String[] args){
            //main主线程
            //创建一个线程对象
            TestDome testDome1=new TestDome("子线程1");
            //调用start()方法开启线程,cpu调度执行
            //修改线程名称
    //        testDome1.setName("子线程1");
            testDome1.start();
            TestDome testDome2=new TestDome();
            testDome2.setName("子线程2");
            testDome2.start();
            for (int i = 0; i < 500; i++) {
                System.out.println("天天向上+++++++++++++"+i);
            }
        }
    }
    
卖票案例
package com.wly.Dome;

public class TicketWin extends Thread{
    private int ticket=100;
    private String name;

    public TicketWin(String name) {
        super(name);
    }

    @Override
    public void run() {
        while (true){
            if (ticket<=0){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"买了第"+ticket+"张票");
            ticket--;
        }
    }
}

package com.wly.Dome;

public class TestWin {
    public static void main(String[] args) {
        //创建4个窗口
        TicketWin w1=new TicketWin("窗口1");
        TicketWin w2=new TicketWin("窗口2");
        TicketWin w3=new TicketWin("窗口3");
        TicketWin w4=new TicketWin("窗口4");
        w1.start();
        w2.start();
        w3.start();
        w4.start();
    }
}

实现Runnable

  • 实现Runnable接口

  • 覆盖run()方法

  • 创建实现类对象

  • 创建线程对象

  • 调用start()方法

  • package com.wly.Dome;
    
    public class TestDome implements Runnable{
        public TestDome() {
        }
    
        public void run(){
            for (int i = 0; i < 20; i++) {
                System.out.println("线程ID:"+Thread.currentThread().getId()+"线程名:"+Thread.currentThread().getName());
            }
    
        }
        public static void main(String[] args){
         //创建对象,表示线程要执行的功能
            TestDome testDome1=new TestDome();
            //创建线程对象
            Thread thread=new Thread(testDome1,"线程1");
            thread.start();
            for (int i = 0; i < 500; i++) {
                System.out.println("main+++++++++++++"+i);
            }
        }
    }
    
匿名内部类实现
package com.wly.Dome;

public class TestDome{

    public static void main(String[] args){
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println("线程ID:" + Thread.currentThread().getId() 
                            + "线程名:" + Thread.currentThread().getName()+i);
                }
            }
        };
        //创建线程对象
        Thread thread=new Thread(runnable,"线程1");
        //启动线程
        thread.start();
        for (int i = 0; i < 500; i++) {
            System.out.println("main"+"=========="+i);
        }
    }
}
资源共享
  • 卖票案例

    • package com.wly.Dome;
      
      public class Ticket implements Runnable{
          private int ticket=100;
          private String name;
          @Override
          public void run() {
              while (true){
                  if (ticket<=0){
                      break;
                  }
             System.out.println(Thread.currentThread().getName()+"卖了"+ticket+"张票");
                  ticket--;
              }
          }
      }
      
    • package com.wly.Dome;
      
      public class TestTicket {
          public static void main(String[] args) {
              //创建票对象
              Ticket ticket=new Ticket();
              //创建线程对象
              Thread w1=new Thread(ticket,"窗口1");
              Thread w2=new Thread(ticket,"窗口2");
              Thread w3=new Thread(ticket,"窗口3");
              Thread w4=new Thread(ticket,"窗口4");
              //启动线程
              w1.start();
              w2.start();
              w3.start();
              w4.start();
          }
      }
      
  • 存取钱问题

    • package com.wly.Dome;
      
      public class BankCard {
          private double money;
      
          public double getMoney() {
              return money;
          }
      
          public void setMoney(double money) {
              this.money = money;
          }
      }
      
      
    • package com.wly.Dome;
      
      public class AddMoney implements Runnable{
          private  BankCard card;
          public AddMoney(BankCard card){
              this.card=card;
          }
          @Override
          public void run() {
              for (int i = 0; i <10 ; i++) {
                  card.setMoney(card.getMoney()+1000);
                  System.out.println(Thread.currentThread().getName()+"存了1000,余额是"+card.getMoney());
              }
          }
      }
      
      
    • package com.wly.Dome;
      
      public class SunMoney implements Runnable{
      
          private BankCard card;
          public SunMoney(BankCard card){
              this.card=card;
          }
          @Override
          public void run() {
              for (int i = 0; i <10 ; i++) {
                  if(card.getMoney()>=1000){
                      card.setMoney(card.getMoney()-1000);
                      System.out.println(Thread.currentThread().getName()
                              +"取了1000,余额是"+card.getMoney());
                  }else{
                      System.out.println("余额不足");
                      i--;
                  }
              }
          }
      }
      
      
    • package com.wly.Dome;
      
      public class TsetBankCard {
          public static void main(String[] args) {
              //创建一张卡
              BankCard card=new BankCard();
              //创建存钱,取钱
              AddMoney add=new AddMoney(card);
              SunMoney sub=new SunMoney(card);
              //创建两个线程
              Thread cq=new Thread(add,"cq");
              Thread qq=new Thread(sub,"qq");
              //启动线程
              cq.start();
              qq.start();
      
          }
      }
      
      
    • package com.wly.Dome;
      
      public class TestBankCard2 {
          private BankCard card;
          public static void main(String[] args) {
              //创建卡对象
              BankCard card=new BankCard();
              //存钱线程
              Runnable add=new Runnable() {
                  @Override
                  public void run() {
                      for (int i = 0; i <10 ; i++) {
                          card.setMoney(card.getMoney()+1000);
                          System.out.println(Thread.currentThread().getName()+"存了1000,余额是"+card.getMoney());
                      }
                  }
              };
              //取钱线程
              Runnable sub=new Runnable() {
                  @Override
                  public void run() {
                      for (int i = 0; i <10 ; i++) {
                          if(card.getMoney()>=1000){
                              card.setMoney(card.getMoney()-1000);
                              System.out.println(Thread.currentThread().getName()
                                      +"取了1000,余额是"+card.getMoney());
                          }else{
                              System.out.println("余额不足");
                              i--;
                          }
                      }
                  }
              };
              //启动线程
              new Thread(add).start();
              new Thread(sub).start();
          }
      }
      
      

线程的状态

状态说明
New初始状态线程对象被创建,即为初始状态,只在堆中开辟内存,与常规对象无异。
Ready就绪状态调用start()之后,进入就绪状态,等待OS选中并分配时间片
Running获得时间片之后,进入运行状态,如果时间片到期,则回到就绪状态
Terminated终止状态主线程main()或独立线程run()结束,进入终止状态,并释放持有的时间片
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rWqJSReA-1672213397412)(D:\笔记\JAVA多线程\IMG_1288(20220330-210836)].PNG)

常见方法

休眠
  • public ststic void sleep(long millis)

  • 当前线程主动休眠millis毫秒

  • package com.wly.Dome;
    
    public class SleepThread extends Thread{
        public void run(){
            for (int i = 0; i <10 ; i++) {
                System.out.println(Thread.currentThread().getName()+"........"+i);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public static void main(String[] args) {
            SleepThread sleep1=new SleepThread();
            sleep1.start();
            SleepThread sleep2=new SleepThread();
            sleep2.start();
        }
    
    }
    
    
放弃
  • public static void yield()

  • 当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片

  • package com.wly.Dome;
    
    public class YieldThread extends Thread {
        public void run(){
            for (int i = 0; i <10 ; i++) {
                System.out.println(Thread.currentThread().getName()+i);
                //主动放弃时间片
                Thread.yield();
            }
        }
    
        public static void main(String[] args) {
            YieldThread y1=new YieldThread();
            y1.start();
            YieldThread y2=new YieldThread();
            y2.start();
    
        }
    }
    
加入
  • public final void join()

  • package com.wly.Dome;
    
    public class JionThread extends Thread{
        public void run(){
            for (int i = 0; i <50 ; i++) {
                System.out.println(Thread.currentThread().getName()+"-----"+i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            JionThread jion1=new JionThread();
            jion1.start();
            //加入当前(main)线程,并阻塞当前线程,直到jion线程执行完毕
            jion1.join();
            //主线程
            for (int i = 0; i <20 ; i++) {
                System.out.println(Thread.currentThread().getName()+"======="+i);
                Thread.sleep(100);
            }
        }
    }
    
优先级
  • 线程对象.setPriority()

  • 优先级为1-10,默认为5,优先级越高,表示获取CPU的机会越多

  • package com.wly.Thread;
    
    public class PriorityThread extends Thread{
        @Override
        public void run() {
            for (int i = 0; i <50 ; i++) {
                System.out.println(Thread.currentThread().getName()+"-----"+i);
            }
        }
    
        public static void main(String[] args) {
            PriorityThread pt1=new PriorityThread();
            pt1.setName("p1");
            PriorityThread pt2=new PriorityThread();
            pt2.setName("p2");
            PriorityThread pt3=new PriorityThread();
            pt3.setName("p3");
    
            //设置优先级
            pt1.setPriority(10);
            pt3.setPriority(2);
    
            //启动线程
            pt1.start();
            pt2.start();
            pt3.start();
        }
    }
    
守护线程
  • 线程对象.setDaemon(true);设置为守护线程
  • 线程有两类:用户线程(前台线程),守护线程(后台线程)
  • 如果程序中所有前台线程都执行完毕了,后台线程会自动结束
  • 垃圾回收线程属于守护线程
线程的状态(等待)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FkexbSZm-1672213397413)(D:\笔记\JAVA多线程\IMG_1289(20220331-104523)].PNG)

安全问题

  • 当多个线程并发的访问临界资源(共享资源)时,如果破坏原子操作,可能造成数据不一致

  • 临界资源:共享资源(同一对象),一次仅允许一个线程使用,才可保证其正确性

  • 原子操作:不可分割的多步操作,被视为一个整体,其顺序和步骤不可打乱或缺省

  • package com.wly.Thread;
    
    import java.util.Arrays;
    
    public class ThreadSafe {
        private static int index=0;
        public static void main(String[] args) throws InterruptedException {
    
            //创建数组
            String[] s=new String[5];
            //创建两个线程
            Runnable runnable=new Runnable() {
                @Override
                public void run() {
                    s[index]="Hello";
                    index++;
                }
            };
            Runnable runnable1=new Runnable() {
                @Override
                public void run() {
                    s[index]="word";
                    index++;
                }
            };
            //创建线程对象
            Thread a=new Thread(runnable,"A");
            Thread b=new Thread(runnable1,"B");
            a.start();
            b.start();
            a.join();
            b.join();
            System.out.println(Arrays.toString(s));
        }
    }
    
    
线程安全
同步方式(一)
  • 同步代码块

  • synchronized(临界资源对象){//对临界资源对象加锁
    
    //代码(原子操作)
    
    }
    
  • package com.wly.Thread;
    
    public class Ticket implements Runnable{
        private int ticket=1000;
        //创建锁
        private Object obj=new Object();
    
    
        @Override
        public void run() {
    
                while (true){
                    synchronized (obj){
                    if (ticket<=0){
                        break;
                    }
                    System.out.println(Thread.currentThread().getName()+"卖了"+ticket+"张票");
                    ticket--;
                }
            }
    
        }
    }
    
  • package com.wly.Thread;
    
    public class TestTicket {
        public static void main(String[] args) {
            //创建票对象
            Ticket ticket=new Ticket();
            //创建线程对象
            Thread w1=new Thread(ticket,"窗口1");
            Thread w2=new Thread(ticket,"窗口2");
            Thread w3=new Thread(ticket,"窗口3");
            Thread w4=new Thread(ticket,"窗口4");
            //启动线程
            w1.start();
            w2.start();
            w3.start();
            w4.start();
        }
    }
    
  • 每个对象都有一个互斥锁标记,用来分配给线程,只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块,线程退出代码块时,会释放相应的互斥锁标记

  • package com.wly.Thread;
    
    public class BankCard {
        private double money;
    
        public double getMoney() {
            return money;
        }
    
        public void setMoney(double money) {
            this.money = money;
        }
    }
    
  • package com.wly.Thread;
    
    public class TestBankCard3 {
        public static void main(String[] args) {
            //创建银行卡
            BankCard card=new BankCard();
            //创建操作
            Runnable add=new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i <10 ; i++) {
                        synchronized (card){
                            card.setMoney(card.getMoney()+1000);
                            System.out.println(Thread.currentThread().getName()
                                    +"存了1000元,余额为:"+card.getMoney());
    
                        }
                    }
    //                try {
    //                    Thread.sleep(1);
    //                } catch (InterruptedException e) {
    //                    e.printStackTrace();
    //                }
    
                }
            };
            Runnable sub=new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i <10 ; i++) {
                        //加锁
                        synchronized (card){
                            if (card.getMoney()>=1000){
                                card.setMoney(card.getMoney()-1000);
                                System.out.println(Thread.currentThread().getName()+"取了1000元,余额为:"+card.getMoney());
                            }else {
                                System.out.println("余额不足");
                                i--;
                            }
                        }
    
                    }
                }
            };
            //创建两个线程对象
            Thread xiaoli=new Thread(add);
            Thread xiaoming=new Thread(sub);
    //        xiaoli.setPriority(1);
    //        xiaoming.setPriority(10);
            xiaoli.start();
            xiaoming.start();
        }
    }
    
线程的状态(阻塞)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VdTKYMIc-1672213397414)(D:\笔记\JAVA多线程\IMG_1290.PNG)]

同步方式(二)
  • 同步方法

  • synchronized 返回值类型 方法名称(形参列表){//对当前对象this加锁

    //代码,原子操作

    }

  • package com.wly.Thread;
    
    public class Ticket implements Runnable{
        private int ticket=1000;
        //创建锁
        private Object obj=new Object();
    
    
        @Override
        public void run() {
            while (true){
                if (!sale()){
                    break;
                }
    
            }
    
    
        }
        //同步方法
        public synchronized boolean sale(){//静态方法锁Ticket.class,非静态,this
            if (ticket<=0){
                return false;
            }
            System.out.println(Thread.currentThread().getName()+"卖了"+ticket+"张票");
            ticket--;
            return true;
               }
    }
    
    
  • package com.wly.Thread;
    
    public class TestTicket {
        public static void main(String[] args) {
            //创建票对象
            Ticket ticket=new Ticket();
            //创建线程对象
            Thread w1=new Thread(ticket,"窗口1");
            Thread w2=new Thread(ticket,"窗口2");
            Thread w3=new Thread(ticket,"窗口3");
            Thread w4=new Thread(ticket,"窗口4");
            //启动线程
            w1.start();
            w2.start();
            w3.start();
            w4.start();
        }
    }
    
    
同步规则
  • 只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的标记锁

  • 如调用不包含同步代码块的方法,或普通方法时,则不需要锁标记,可以直接调用

  • 已知JDK中线程安全的类

    • StringBuffer

    • Vector

    • Hashtable

    • 以上类中的公开方法,均为synchonized修饰的同步方法

    死锁问题
    • package com.wly.Thread;
      //创建俩个对象
      public class Mylock {
          //创建两个锁
          public static Object a=new Object();
          public static Object b=new Object();
      }
      
      
    • package com.wly.Thread;
      
      public class Boylock extends Thread{
          @Override
          public void run() {
              synchronized (Mylock.a){
                  System.out.println("男孩拿到了a");
                  synchronized (Mylock.b){
                      System.out.println("男孩拿到了b-------");
                      System.out.println("男孩可以吃东西了");
                  }
              }
          }
      }
      
      
    • package com.wly.Thread;
      
      public class GirlLock extends Thread{
          @Override
          public void run() {
              synchronized (Mylock.b){
                  System.out.println("女孩拿到了b");
                  synchronized (Mylock.a){
                      System.out.println("女孩拿到了a");
                      System.out.println("女孩可以吃东西了-------");
                  }
              }
          }
      }
      
    • package com.wly.Thread;
      
      public class TestDeadLock {
      
          public static void main(String[] args) throws InterruptedException {
              Boylock boylock=new Boylock();
              GirlLock girlLock=new GirlLock();
              boylock.start();
              Thread.sleep(100);
              girlLock.start();
          }
      }
      
      

    线程通信

    等待
    • public final void wait()
    • public final void Wait(long timeout)
    • 必须在对obj加锁的同步代码中。在一个线程,调用obj.wait()时,此线程会释放其拥有的所有锁标记。同时此线程阻塞在o的等待队列中。释放锁进入等待队列
    通知
    • pubilc final void notify()
    • public final void notifyAll()
    • 唤醒等待的线程

生产者,消费者问题

         synchronized (Mylock.a){
                System.out.println("女孩拿到了a");
                System.out.println("女孩可以吃东西了-------");
            }
        }
    }
}
```
  • package com.wly.Thread;
    
    public class TestDeadLock {
    
        public static void main(String[] args) throws InterruptedException {
            Boylock boylock=new Boylock();
            GirlLock girlLock=new GirlLock();
            boylock.start();
            Thread.sleep(100);
            girlLock.start();
        }
    }
    
    

线程通信

等待
  • public final void wait()
  • public final void Wait(long timeout)
  • 必须在对obj加锁的同步代码中。在一个线程,调用obj.wait()时,此线程会释放其拥有的所有锁标记。同时此线程阻塞在o的等待队列中。释放锁进入等待队列
通知
  • pubilc final void notify()
  • public final void notifyAll()
  • 唤醒等待的线程

生产者,消费者问题

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值