多线程基础(创建、常用方法以及线程的生命周期和风险)

线程的创建

  1. 写一个类去继承Thread类,重写run()方法
  2. 写一个类去实现Runable接口
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i=1;i<=100;i++){
            System.out.println(i+"子线程启动。。。。。。。。。子线程");
        }
    }
}
public class TestCreateThread {
    public static void main(String[] args) {
        System.out.println("main方法启动。。。。。。。。。。主线程");
        //创建子线程
        Thread thread=new MyThread();
        Thread thread1=new Thread(()->{
            System.out.println("接口创建线程");
        });
        thread1.run();
        //启动线程
        thread.start();
        for (int i=0;i<100;i++){
            System.out.println(i+"main方法启动。。。。。。。。。。主线程");
        }
        System.out.println("主线程的逻辑。。。。。");
    }
}

Thread类的常用方法

currentThread()方法

Thread.currentThread()方法可以获得当前线程 ,Java 中的任何一段代码都是执行在某个线程当中的. 执行当前代码的线程就是当前线程.同一段代码可能被不同的线程执行 , 因此当前线程是相对的,Thread.currentThread()方法的返回值是在代码实际运行时候的线程对象

setName()/getName()

thread.setName(线程名称), 设置线程名称,thread.getName()返回线程名称,通过设置线程名称,有助于程序调试,提高程序的可读性, 建议为每个线程都设置一个能够体现线程功能的名称

isAlive()

thread.isAlive()判断当前线程是否处于活动状态,活动状态就是线程已启动并且尚未终止

sleep()

Thread.sleep(millis); 让当前线程休眠指定的毫秒数

getId()

hread.getId()可以获得线程的唯一标识
注意:
某个编号的线程运行结束后,该编号可能被后续创建的线程使用,重启的 JVM 后,同一个线程的编号可能不一样

yield()

Thread.yield()方法的作用是放弃当前的 CPU 资源

setPriority()

thread.setPriority( num ); 设置线程的优先级,java 线程的优先级取值范围是 1 ~ 10 , 如果超出这个范围会抛出异常 IllegalArgumentException.
在操作系统中,优先级较高的线程获得 CPU 的资源越多,线程优先级本质上是只是给线程调度器一个提示信息,以便于调度器决定先调度哪些线程. 注意不能保证优先级高的线程先运行.
Java 优先级设置不当或者滥用可能会导致某些线程永远无法得到运行,即产生了线程饥饿.线程的优先级并不是设置的越高越好,一般情况下使用普通的优先级即可,即在开发时不必设置线程的优先级
线程的优先级具有继承性, 在 A 线程中创建了 B 线程,则 B 线程的优先级与 A 线程是一样的

interrupt()

中断线程.注意调用 interrupt()方法仅仅是在当前线程打一个停止标志,并不是真正的停止线程,搭配isInterrupt()使用

setDaemon()

Java 中的线程分为用户线程与守护线程
守护线程是为其他线程提供服务的线程,如垃圾回收器(GC)就是一个典型的守护线程
守护线程不能单独运行, 当 JVM 中没有其他用户线程,只有守护线程时,守护线程会自动销毁, JVM 会退出

 示例:

package com.edu.creatThread;

/**
 * @作者 five-five
 * @创建时间 2020/9/13
 */
public class DemonThread extends Thread {
    @Override
    public void run() {
        super.run();
        while(true){
            System.out.println("守护线程启动。。。。。。");
        }
    }
}
-------------------------------------------------------------------------
package com.edu.creatThread;

/**
 * @作者 five-five
 * @创建时间 2020/9/13
 */
public class TestDemon {
    public static void main(String[] args) {
        DemonThread demonThread = new DemonThread();
        demonThread.setDaemon(true);//设置线程为守护线程
        for (int i=0;i<1;++i){
            System.out.println("main线程在跑。。。。。");
        }
        demonThread.start();

    }
}

多线程的生命周期

线程的生命周期是线程对象的生老病死,即线程的状态
线程生命周期可以通过 getState()方法获得 , 线程的状态是Thread.State 枚举类型定义的

NEW,新建状态.

创建了线程对象,在调用 start()启动之前的状态

RUNNABLE,可运行 状态

它 是一 个复 合状 态 , 包 含 :READY 和RUNNING 两个状态. READY 状态该线程可以被线程调度器进行调度使它 处 于 RUNNING 状 态 , RUNING 状 态 表 示 该 线 程 正 在 执 行 .
Thread.yield()方法可以把线程由 RUNNING 状态转换为 READY 状态

BLOCKED 阻塞状态

线程发起阻塞的 I/O 操作,或者申请由其他线程占用的独占资源,线程会转换为 BLOCKED 阻塞状态. 处于阻塞状态的线程不会占用 CPU 资源. 当阻塞 I/O 操作执行完,或者线程获得了其申
请的资源,线程可以转换为 RUNNABLE.

WAITING 等待状态

线程执行了 object.wait(), thread.join()方法会把线程转换为 WAITING 等待状态, 执行 object.notify()方法,或者加入的线程执行完毕,当前线程会转换为 RUNNABLE 状态

TIMED_WAITING 状态

与 WAITING 状态类似,都是等待状态.区别在于处于该状态的线程不会无限的等待,如果线程没有在指定的时间范围内完成期望的操作,该线程自动转换为 RUNNABLE

TERMINATED 终止状态

线程结束处于终止状态

线程生命示例图:

 多线程编程存在的问题与风险:

线程安全(Thread safe)问题.

多线程共享数据时,如果没有采取正确的并发访问控制措施,就可能会产生数据一致性问题,如读取脏数据(过期的数据), 如丢失数据更新

线程活性(thread liveness)问题.

由于程序自身的缺陷或者由资源稀缺性导致线程一直处于非 RUNNABLE 状态,这就是线程活性问题

常见的活性故障有以下几种:

  1.   死锁(Deadlock). 类似鹬蚌相争.
  2.   锁死(Lockout), 类似于睡美人故事中王子挂了
  3.   活锁(Livelock). 类似于小猫咬自己尾巴
  4.   饥饿(Starvation).类似于健壮的雏鸟总是从母鸟嘴中抢到食物.

上下文切换(Context Switch).

处理器从执行一个线程切换到执行另外一个线程

可靠性

可能会由一个线程导致 JVM 意外终止,其他的线程也无法执行

原文地址:https://www.cnblogs.com/five-five/p/13660776.html