多线程简单介绍

线程:是操作系统能够进行运算调度的最小单位。它被包含在进程中,是进程中实际运作单位
进程:进程是程序的基本执行实体,一个程序就是一个进程

简单理解线程:
应用软件中相互独立,可以同时运行的功能
有了多线程,就可以让程序同时做多件事

线程的生命周期
完整的线程状态
New(新建状态) ->创建线程对象
至今尚未启动的线程处于这种状态

RUNNABLE(就绪状态) ->start方法
在Java虚拟机中执行的线程处于此状态

BLOCKER(阻塞状态) ->无法获得锁对象
被阻塞等待监视器锁定的线程处于此状态

WAITING(等待状态) ->wait方法
无限期的等待另一个线程执行特定动作的线程处于此状态

TIMED_WAITING(计时状态) ->sleep方法
正在等待另一个线程执行动作达到指定等待时间的线程处

TERMINATED(结束状态) ->全部代码运行完毕
已退出的线程处于此状态

多线程的实现方式
1.继承Thread类的方式进行实现
2.实现Runnable接口的方式进行实现
3.利用Callable接口和Future接口方式实现

多线程的第一种启动方式
1.自己定义一个类继承Thread
2,重写run方法
3.创建子类的对象,并启动线程
例如

public class MyThread extends Thread{
  @Override
  public void run(){
    //书写线程要执行代码
    for(int i=0; i<100; i++){
      System.out.println(getName()+"HelloWord");
    }
  }
}

public class MyThreadTest{
  public static void main(String[] args){
    MyThread t1=new MyThread();
    MyThread t2=new MyThread();

    t1.setName("线程1");
    t2.setName("线程2");

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

多线程的第二种启动方式
1.定义一个类实现Runnable接口
2.重写里面的run方法
3.创建自己类的对象
4.创建一个Thread类的对象,并开启线程
例如

public class MyRun implements Runnable{
  @Override
  public void run(){
    for(int i=0; i<100; i++){
      //获取到当前的对象
      //Thread t=Thread.currentThread();
      //System.out.println(t.getName()+"HelloWorld")
      System.out.println(Tread.currentThread.getName()+"HelloWorld");
    }
  }
}

public class MyRunnableTest{
  public static void main(String[] args){
    //创建MyRun的对象
    //表示多线程要执行的任务
    MyRun mr=new MyRun();

    //创建线程对象
    Thread t1=new Thread(mr);
    Thread t2=new Thread(mr);

    //给线程设置名字
    t1.setName("线程1");
    t2.setName("线程2");

    //开启线程
    t1.start();
    t2.start();
  }
}

多线程的第三种实现方式
特点:可以获取到多线程运行的结果
1.创建一个类MyCallable实现Callable接口
2.重写call(是有返回值的,表示多线程运行的结果)
例如

public class MyCallable implements Callable<Integer>{
  @Override
  public Integer call() throws Exception{
    //1~100之间的和
    int sum=0;
    for(int i =0; i<100;i++){
      sum+=i;
    }
    return sum;
  }
}

3.创建MyCallable的对象(表示多线程要执行的任务)
4.创建FutureTask的对象(作用管理多线程运行的结果)
5.创建Thread类的对象,并启动(表示线程)

public class ThreadDemo{
  public static void main(String[] args) throws ExcutionException{
    //创建MyCallable的对象(表示多线程要执行的任务)
    MyCallable mc =new MyCallable();

    //创建FuturnTask的对象(作用管理多线程运行的结果)
    FutureTask<Integer> ft =new FutureTask<>(mc);

    //创建线程的对象
    Thread t1 =new Thread(ft);
    //启动线程
    t1.start();

    //获取多线程运行的结果
    Integer result =ft.get();
    System.out.println(result);
  }
}

多线程三种实现方式对比
继承Thread类:
优点:编程比较简单,可以直接使用Thread类中的方法
缺点:扩展性较差,不能再继承其他的类

实现Runnable接口
优点:扩展性强,实现该接口的同时还可以继承其他的类
缺点:编程相对复杂,不能直接使用Thread类中方法

实现Callable接口
优点:
1.可以获取到多线程的运行的结果
2.扩展性强,实现该接口的同时还可以继承其他的类
确定:编程相对复杂,不能直接使用Thread类中的方法

Thread常见的成员方法

方法名称 说明
String getName() 返回此线程的名称
void setName(String name) 设置线程的名字(构造方法也可以设置名字)
static Thread currentThread() 获取当前线程的对象
static void sleep(long time) 让线程休眠指定的时间,单位为毫秒
setPriority(int new Priority) 设置线程的优先级
final int getPriority() 获取线程的优先级
final void setDaemon(boolean on) 设置为守护线程
public static void yield() 出让线程/礼让线程
public static void join() 插入线程/插队线程
方法名称 说明
String getName() 返回此线程的名称
void setName(String name) 设置线程的名字(构造方法也可以设置名字)

细节:
1.如果我们没有给线程设置名字,线程也是有默认的名字的
格式:Thread-X(X为序号,从0开始)
2.如果我们要给线程设置名字,可以用set方法进行设置,也可以用构造方法进行设置
注意:子类要使用父类的有参构造器,要先生成有参构造器

方法名称 说明
More Actions static Thread currentThread() 获取当前线程的对象

细节:
当JVM虚拟机启动之后,会自动的启动多条线程
其中有一天线程就叫做main线程,作用就是调用main方法并执行里面的代码
在以前,我们写的所有代码,其实都运行在main线程当中

方法名称 说明
static void sleep(long time) 让线程休眠指定的时间,单位为毫秒

细节:
1.哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间
2.方法的参数:就表示睡眠的时间,单位毫秒 1秒=1000毫秒
3.当时间到了之后,线程会自动的醒来,继续执行下面的其他代码

方法名称 说明
setPriority(int new Priority) 设置线程的优先级
final int getPriority() 获取线程的优先级

细节:Java是抢占式调度,体现了随机性
多个线程抢夺cpu的执行权,cpu在什么时候执行哪条线程是不确定的,执行多长时间也是不确定的
没有设置线程的优先级,默认为5,最小权重为1,最大权重为10
权重越大随机到的概率越大,但不是100%

方法名称 说明
final void setDaemon(boolean on) 设置为守护线程

细节:当其他的非守护线程执行完毕之后,守护线程会陆续结束
简单来说:当女神线程结束了,那么备胎也就没有存在的必要了

public class MyThread1 extends Thread{
  @Override
  public void run(){
    for(int i=1;i<=10;i++){
      System.out.println(getName()+"@"+i);
    }
  }
}

public class MyThread2 extends Thread{
  @Override
  public void run(){
    for(int i=1; i<=100; i++){
      System.out.println(getName()+"@"+i);
    }
  }
}

public class ThreadDemo{
  public static void main(String[] args){
    MyThread t1 =new MyThread1();
    MyThread t2 =new MyThread2();

    t1.setName("女神");
    t2.setName("备胎");

    //把第二个线程设置为守护线程(备胎线程)
    t2.setDaemon(true);

    t1.start();
    t2.start();
    //女神运行10次时,备胎就会停止运行,不会执行到100次
  }
}

守护线程应用场景
例如:聊天和传输文件,聊天是一个线程,传输文件也是一个线程
当聊天关闭,那么传输文件也应该关闭,可以把传输文件设置为守护线程

方法名称 说明
public static void yield() 出让线程/礼让线程

作用:为了让结果分布更加均匀,但不是绝对
举例

public class MyThread extends Thread{
  @Override
  public void run(){
    for(int i=1; i<=100;i++){
      System.out.println(getName()+"@"+i);
      //表示出让当前CPU的执行权
      //线程会重新抢夺CPU的执行权
      Thread.yield();
    }
  }
}

public class ThreadDemo{
  public static void main(String[] args){
    MyThread t1 = new Thread();
    MyThread t2 = new Thread();

    t1.setName("飞机");
    t2.setName("坦克");

    t1.start();
    t2.start();
  }
}
方法名称 说明
public static void join() 插入线程/插队线程

举例

public class MyThread extends Thread{
  @Override
  public void run(){
    for(int i = 1; i<=100; i++){
      System.out.println(getName()+"@"+i);
    }
  }
}

public class ThreadDemo{
  public static void main(String[] args){
    MyThread t = new MyThread();
    t.setName("土豆");
    t.start();

    //表示把t这个线程插入当前线程之前
    //t:土豆
    //当前线程:main线程
    //土豆线程执行完才执行main线程
    t.join();

    //执行在main线程当中的
    for(int i = 0; i<10; i++){
      System.out.println("main线程"+i);
    }
  }
}
posted @ 2024-01-27 10:22  狗狗没有坏心眼  阅读(4)  评论(0编辑  收藏  举报