Java 多线程

发布于 2021-07-01 12:45:43 字数 8247 浏览 1617 评论 0

一、线程概述

进程直译:正在进行中的程序

线程

线程就是进程中一个负责程序执行的控制单元(执行路径) 一个进程中可以有多个执行路径,称为多线程。开启多个线程是为了同时运行多部分代码。 一个进程中至少有一个线程。每一个线程都有自己运行的内容,这个内容可以称为线程要执行的任务。

二、多线程好处与弊端

好处:解决了多部分同时运行的问题

弊端:线程太多会导致效率降低

其实应用程序的执行都是 cpu 在做着快速切换完成的。这个切换是随机的。

三、JVM 中多线程的解析

JVM启动时就启动了多个线程,至少有两个线程可以分析的出来。 1、执行main函数的线程 该线程的任务代码都定义在main函数中。 2、负责垃圾回收的线程

四、线程的创建方式-Thread类

创建新执行线程有两种方法:

第一种方法是将类声明为 Thread 的子类。该子类重写 Thread 的 run 方法

class Demo extend Thread{
  private String name;
  Demo(name){
    this.name = name;
  }

  public void run(){
    this.show();
  }
  public void show(){
    for(int x = 0;x<10;x++){
      System.out.println(name+"...x="+x);
    }
  }
}

class ThreadDemo{
  public static void main(){
    //创建线程的目的是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行
    //而运行的指定代码就是这个执行路径的任务

    //jvm创建的主线程的任务都定义在了主函数中。
    //而自定义的线程的任务在哪呢?
    //Thread类用于描述线程,线程是需要任务的,所以Thread类也是对任务的描述
    //这恶搞任务就是通过Tnread的run方法体现,也就是说run方法就是封装自定义线程运行任务的函数。
    //run方法中定义的就是线程要运行的任务代码

    //开启线程是为了运行指定代码,所以只有继承Thread类,并覆写run方法
    //将运行的代码定义在run方法中即可。

    Demo d1 = new Demo("旺财");
    Demo d2 = new Demo("xiaoqiang");
    d1.start();//开启线程后,jvm会自动调用该线程的run方法
    d2.start();//不用自己手动调用run方法
  }
}

第二种方法是实现 Runnable 接口

class Demo implements Runnable{//准备扩展Deno类的功能,让其中的内容可以作为线程的任务执行,通过接口的形式完成
  private String name;
  Demo(name){
    this.name = name;
  }

  public void run(){
    this.show();
  }
  public void show(){
    for(int x = 0;x<10;x++){
      System.out.println(name+"...x="+x);
    }
  }
}


class ThreadDemo{
  public static void main(){
    Demo d1 = new Demo();//这时候不是线程对象,不能调用start


    Thread t1 = new Thread(d1);//线程对象
    Thread t2 = new Thread(d1);

    t1.start();
    t2.start();
  }
}

第二种方法的好处:

  1. 将线程的任务从线程的子类中分离出来,进行了单独的封装(按照面向对象的思想将任务封装成对象)
  2. 避免了 java 单继承的局限性

所以常见线程的第二种方式比较常用。

五、Thread 类中的方法 - 线程名称

可以通过 Thread 的 getName 获取现场的名称 Thread-编号(从0开始)

class Demo extend Thread{
  private String name;
  Demo(name){
    this.name = name;
  }

  public void run(){
    this.show();
  }
  public void show(){
    for(int x = 0;x<10;x++){
      //线程以创建,名字就有了
      System.out.println(name+"...x="+x+"ThreadName="+this.getName());

      //获取当前运行线程的名字
      System.out.println(name+"...x="+x+"ThreadName="+Thread.currentThread().getName());
    }
  }
}

六、多线程运行内存图解

七、线程的四种状态

cpu 的执行资格:可以被 cpu 处理,在处理队列中排队。

cpu 的执行权:正在被 cpu 处理。

运行状态的线程具备 cpu 的执行资格和 cpu 的执行权 ,临时阻塞状态的线程具备 cpu 的执行资格,但不具备 cpu 的执行权 冻结状态的线程,不具备 cpu 的执行资格和 cpu 的执行权。

八、卖票示例

class Ticket implements Runnable{
  private int num = 100;

  public void run(){
    while(true){
      if(num>0){
        System.out.println(Thread.currentThread().getName()+"...sale..."+num--)
      }
    }
  }
}

class TicketDemo{
  public static void main(String[] args){
    Ticket t = new Ticket();

    Thread t1 = new Thread(t);
    Thread t2 = new Thread(t);
    Thread t3 = new Thread(t);
    Thread t4 = new Thread(t);

    t1.start();
    t2.start();
    t3.start();
    t4.start();
  }
}

九、多线程安全问题

class Ticket implements Runnable{
  private int num = 100;

  public void run(){
    while(true){
      if(num>0){
        try{//由于线程的安全问题,可能会导致负数票的出现
          Thread.sleep(10);
          System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
        }catch{}
      }
    }
  }
}

class TicketDemo{
  public static void main(String[] args){
    Ticket t = new Ticket();

    Thread t1 = new Thread(t);
    Thread t2 = new Thread(t);
    Thread t3 = new Thread(t);
    Thread t4 = new Thread(t);

    t1.start();
    t2.start();
    t3.start();
    t4.start();
  }
}

线程安全问题产生的原因

  1. 多个线程在操作共享的数据。
  2. 操作共享数据的代码有多条。

当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线程安全问题的产生。

十、解决多线程问题-同步代码块

解决思路:就是将多条操作共享数据的线程封装起来,当有线程在执行这些代码的时候,其他线程是不可以参与运算的,必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。

在 java 中同步代码块就可以解决这个问题,同步代码块的格式:

synchronized(对象){
  需要被同步的代码;
}

修改代码如下:

class Ticket implements Runnable{
  private int num = 100;
  Object obj = new Object();//创建一个对象为同步代码块使用

  public void run(){
    while(true){
      synchronized(obj){//同步代码块
        if(num>0){
          try{
            System.out.println(Thread.currentThread().getName()+"...sale..."+num--);
          }catch{}
        }
      }
    }
  }
}

class TicketDemo{
  public static void main(String[] args){
    Ticket t = new Ticket();

    Thread t1 = new Thread(t);
    Thread t2 = new Thread(t);
    Thread t3 = new Thread(t);
    Thread t4 = new Thread(t);

    t1.start();
    t2.start();
    t3.start();
    t4.start();
  }
}

同步代码块的好处与弊端

  • 同步的好处:解决了线程安全问题
  • 同步的弊端:相对以前降低了效率,因为同步外的线程都会判断同步锁。
  • 同步的前提:必须有多个线程必须使用同一个锁。

十一、解决多线程问题-同步函数

class Back{
  private int sum;
  private Object obj = new Object();

  public void add(int num){
    synchronized(obj){//同步代码块
      sum = sum+num;
      try{Thread.sleep(10)}catch(InterruptedExpection e){}
      System.out.println("sum="+sum);
    }
  }


  //同步的第二种表现形式
  public void synchronized add(int num){//同步函数 (只需要用synchronized关键字修饰即可,不用使用锁)
      sum = sum+num;
      try{Thread.sleep(10)}catch(InterruptedExpection e){}
      System.out.println("sum="+sum);
  }
}

class Cus implements Runnable{
  private Bank b = new Bank();

  public void run(){
    for(int x=0;x<3;x++){
      b.add(100);
    }
  }
}

使用同步代码块时候的锁是自己 new 的一个对象,那么同步函数的锁是什么呢?(this)

建议使用同步代码块

静态同步函数用的锁就不是this了。当前的字节码对象 (this.getClass()/类名.class)

十二、单例模式涉及的多线程问题

//饿汉式(不存在线程安全问题)
class Single{
  private static final Single s = new Single();
  private Single(){}
  public static Single getInstance(){
    return this.s;
  }
}

//懒汉式(存在安全问题需要解决)
class Single{
  private static Single s = null;
  private Single(){}
  public static Single getInstance(){
    if(s==null){
      s = new Single();
    }
    return this.s;
  }

  //解决线程安全

  public static Single getInstance(){
    if(s==null){
      synchronized(Single.class/this.getClass()){
        if(s==null){
          s = new Single();
        }
      }
    }
    return this.s;
  }
}

十三、死锁

死锁的出现之一:同步的嵌套

class Test implements Runnable{
  private boolean flag;
  Test(boolean flag){
    this.flag = flag;
  }

  public void run(){
    if(flag){
      synchronized(MyLock.locka){
        System.out.println("if locka...");
        synchronized(MyLock.lockb){
          System.out.println("if lockb...");
        }
      }
    }else{
      synchronized(MyLock.lockb){
        System.out.println("else lockb...");
        synchronized(MyLock.locka){
          System.out.println("else locka...");
        }
      }
    }
  }
}

class MyLock{
  public static final Object locka = new Object();
  public static final Object lockb = new Object();
}

class DeadLockTest{
  public static void main(){
     Test a = new Test(true);
     Test b = new Test(false);

     Thread t1 = new Thread(a);
     Thread t2 = new Thread(b);

     t1.start();
     t2.start();
   }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

JSmiles

生命进入颠沛而奔忙的本质状态,并将以不断告别和相遇的陈旧方式继续下去。

0 文章
0 评论
84960 人气
更多

推荐作者

1CH1MKgiKxn9p

文章 0 评论 0

ゞ记忆︶ㄣ

文章 0 评论 0

JackDx

文章 0 评论 0

信远

文章 0 评论 0

yaoduoduo1995

文章 0 评论 0

霞映澄塘

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文