【Java多线程与并发】——多线程基础知识

目录

一、什么是多线程

二、多线程创建的几种方式

1、继承Thread

2、实现Runnable接口

3、实现Callable接口

4、线程池

三、线程的生命周期

四、Thread中的一些方法

五、如何停止一个线程

1、使用return停止线程:

2、使用interrupt方法中断线程:

3、抛异常法(推荐用法):

4、在沉睡中停止:

5、为什么不使用stop()方法停止线程

六、暂停和恢复线程

七、线程的优先级

 八、什么是守护线程?


一、什么是多线程

点击——》进程与线程的联系区别

二、多线程创建的几种方式

1、继承Thread

thread是程序执行的一个线程,Java虚拟机允许一个应用程序多线程同时执行,每个线程都有优先级,优先级高的比优先级低的线程优先执行,每个线程有可能会被标记为守护线程。

//继承Thread方式,重写run()方法
public class MyThread extends Thread {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");

    }

    public static void main( String[] args )
    {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

        thread1.start();//启动线程thread1
        thread2.start();//启动线程thread2
    }
}

执行结果

Thread-1 is running
Thread-0 is running

 

2、实现Runnable接口

/**
 * 实现Runnable接口
 */
public class MyThread1 implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");
    }

    public static void main(String[] args) {
        MyThread1 myThread1 = new MyThread1();
        Thread thread = new Thread(myThread1);
        thread.start();
    }
}

3、实现Callable接口

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

//实现Callable<V>接口,V为方法返回类型
public class CallableImpl implements Callable<Double> {

    @Override
    public Double call() throws Exception {
        Thread.sleep(1000);
        double result = Math.random();
        System.out.println(Thread.currentThread().getName()+" is running !");
        return result;
    }
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Double> callable = new CallableImpl();
        FutureTask<Double> future = new FutureTask<>(callable);
        //创建并启动线程
        new Thread(future).start();
        //调用get()会阻塞主线程,否则不会阻塞
        Double result = future.get();
        System.out.println("result=" + result);
        System.out.println("main is running !");
    }
}

 

4、线程池

public class ExecutorThread {
    public static void main(String[] args) {
        
        //使用 guava 开源框架的 ThreadFactoryBuilder 给线程池的线程设置名字
        ThreadFactory nameThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-pool-%d").build();

        ExecutorService service = new ThreadPoolExecutor(4,10,0L,
                                                         TimeUnit.MILLISECONDS,
                                                         new LinkedBlockingDeque<>(1024),
                                                         nameThreadFactory,
                                                         new ThreadPoolExecutor.AbortPolicy());
        service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !"));
        service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !"));
        service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !"));
        service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !"));
        service.execute(()-> System.out.println(Thread.currentThread().getName() + " is running !"));
        service.shutdown();
    }
}

三、线程的生命周期

线程的五种基本状态:

1)新建状态(New):当线程对象创建后,线程进入新建状态,如:Thread  t  = new Thread();

2)就绪状态(Runnable):当调用线程对象的start()方法,即t.start(),线程就从新建状态变为就绪状态。处于就绪状态的线程只是说线程已经做好了准备,随时等待CPU调度执行,因此调用start()方法并不是说线程立即去执行,只是线程进入了一个可运行的状态。

3)运行状态(Running):当处于就绪状态的线程获得了CPU的调度执行后,此时线程进入运行状态。

4)阻塞状态(Blocked):处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才 有机会再次被CPU调用以进入到运行状态。根据阻塞产生的原因不同,阻塞状态又可以分为三种:

1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;

2.同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;

3.其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5)死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

四、Thread中的一些方法

  • currentThread():返回正在执行此方法代码的线程信息
  • isAlive():判断当前的线程是否处于活动状态,所谓活动就是指线程已经启动但是尚未终止。线程处于正在运行或者准备开始运行的状态。
  • sleep():让正在执行的线程暂停执行
  • getId():返回当前线程的唯一标识
    /*
     * Thread ID
     * 线程id,在调用init初始化方法的时候,调用nextThreadID()方法生成ID
     */
    private long tid;
    public long getId() {
        return tid;
    }
    /**
     *同步生成线程ID方法
     */
    private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }
  • getName():获得线程名称

线程名称类似“Thread-0”名称的由来:由线程的构造方法构造而来,如下图源码:

    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }

    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }


    /* For autonumbering anonymous threads. 
     * 自动编号的匿名线程
     */
    private static int threadInitNumber;


    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }
  • yield():放弃当前的CPU资源,让给其他的线程去占用CPU执行时间。

因为放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。

  • isDaemon():判断线程是否是守护线程

五、如何停止一个线程

1、使用return停止线程:

public class Main extends Thread {
    @Override
    public void run() {
        while (true){
            if (this.isInterrupted()){
                System.out.println("线程停止了!");
                //使用return来停止线程
                return;
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Main main = new Main();
        main.start();
        Thread.sleep(2000);
        main.interrupt();
    }
}

 

2、使用interrupt方法中断线程:

在Thread.java类里提供了两种方法判断线程是否为停止的。

this.interrupted():测试当前线程是否已经中断(静态方法)。如果连续调用该方法,则第二次调用将返回false。在api文档中说明interrupted()方法具有清除状态的功能。执行后具有将状态标识清除为false的功能。

this.isInterrupted():测试线程是否已经中断,但是不能清除状态标识。

3、抛异常法(推荐用法):

public class MyThread4 extends Thread {
    @Override
    public void run() {
        super.run();
        try {
            for (int i = 0; i < 50000; i++) {
                if (this.isInterrupted()) {
                    System.out.println( "线程已经结束,我要退出" );
//                    return;
                    throw new InterruptedException();
                }
                System.out.println( "i=" + (i + 1) );
            }
            System.out.println( "我是for下面的语句,我被执行说明线程没有真正结束" );
        } catch (InterruptedException e) {
            System.out.println( "进入MyThread.java类中run方法的catch异常了" );
            e.printStackTrace();
        }
    }
}

 

4、在沉睡中停止:

  @Override
  public void run() {
      super.run();
      try {
          System.out.println( "begin run" );
          Thread.sleep( 500 );
          System.out.println( "begin end" );
      } catch (InterruptedException e) {
          System.out.println("在沉睡中终止");
          e.printStackTrace();
      }
  }
  public static void main(String[] args) {
        try {
            MyThread5 thread5 = new MyThread5();
            thread5.start();
            Thread.sleep( 20 );
            thread5.interrupt();
        } catch (InterruptedException e) {
            System.out.println( "main catch" );
            e.printStackTrace();
        }
    }

运行结果:

从打印结果看,sleep状态下停止某一个线程,会进入catch语句,并清除状态值,变成false

5、为什么不使用stop()方法停止线程

方法stop()已经弃用,因为如果强制让线程终止则有可能使一些请理性的工作得不到完成。另外,对锁的对象“解锁”,会导致数据得不到同步的处理,出现了数据不一致的情况。

六、暂停和恢复线程

suspend()——暂停线程

resume()——恢复线程

suspend()和resume()方法缺点——独占、不同步

七、线程的优先级

在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务。

在Java中线程的优先级划分为1~10这10个等级。如果小于1或者大于10,则JDK抛出异常throw new IllegalArgumentException()。

JDK中使用了3个常量来预置定义优先级:

public final static int MIN_PRIORITY = 1;

public final static int MIN_PRIORITY = 5;

public final static int MIN_PRIORITY = 10;

线程优先级的特性:

1)线程的优先级具有继承特性,比如A线程启动B线程,则A线程与B线程的优先级是一样的

2)线程优先级具有规则性,CPU尽量将资源让给优先级比较高的线程

3)线程的优先级具有随机性,换句话说优先级较高的线程不一定每一次都先执行完

 八、什么是守护线程?

在Java中线程分为两种:用户线程和守护线程

什么是守护线程?守护线程是一种特殊的线程。它的作用是为其他线程的运行提供便利服务,最典型的应用就是GC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值