java补完——多线程(线程状态、创建&停止、常用方法、等待唤醒机制、线程池)

文章说明:黑色字体-已知,蓝色字体-所见即所得,红色字体-还需要再找资料弄明白的,黄色背景-提炼关键信息
参考资料:很多参考资料,段落下有原文链接到原作者

简介

Process(程序)、Thread(线程)、进程。

线程状态

  1. 新建(new):当线程被创建时,分配了系统资源,执行了初始化。有资格获得CPU 时间,调度器会把这个线程转变为运行状态或阻塞状态。还没调用start方法。
  2. 就绪(Runnable):可执行,是否执行取决于调度器。不同于死亡和阻塞状态。 
  3. 阻塞(Blocked):线程试图获取一个对象锁,如果该对象锁被其他线程所持有,进入阻塞状态。线程处于阻塞状态时,调度器忽略此线程,不分配给线程CPU 时间。直到线程持有锁进入就绪状态。
  4. 无限等待(Waiting):一个线程在等待另一个线程执行(唤醒)动作时,该线程进入无限等待状态。不能自动唤醒,必须等待另一个线程调用notify或notifyAll方法唤醒。
  5. 计时等待(TimedWaiting):同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直等到超时期满或者接收到唤醒通知。带有超时参数的常用方法有:Thread.sleep()、Object.wait()
  6. 被终止(Teminated):因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。
  7. 死亡 (Dead):处于死亡或者终止状态的线程将不可调度,再也不会得到CPU 时间,它的任务已经结束,或不再可运行。线程死亡的通常方式是从run() 方法返回,但是线程任务还可以被中断。

创建线程

创建线程的三种方法

方法一:继承 Thread 类,重写 Thread 的 run 方法,在主线程类种创建自定义线程类对象,调用 start 方法开启线程。run 方法相当于其他线程的 main 方法。

//自定义线程类继承Thread。
public class myThread extends Thread {
    //重写run方法,完成该线程执行的逻辑。
    public void run()
    {
        for(int i = 0;i < 50;i++)
        {
            System.out.println("新线程正在执行" + i);
        }
    }
}
//主线程类。
public static void main(String[] args)
{
    //创建自定义线程对象。
    myThread mT = new myThread();
    //开启新线程,让新的线程执行程序,jvm调用线程中的run。
    mT.start();
    //在main方法中执行。
    for(int i = 0;i < 50;i++)
    {
        System.out.println("main线程正在执行" + i);
    }
}

方法二:实现 Runnable 接口,重写 run 方法,在主线程类种创建自定义实现类对象,将对象传入到Thread对象的构造方法中,Thread对象调用start方法开启线程。

//定义接口实现类
public class myRunnable implements Runnable {
    //重写run方法
    public void run()
    {
        for(int i = 0;i < 5;i++)
        {
            System.out.println("myRunnable线程正在执行!");
        }
    }
}
//主线程类。
public static void main(String[] args)
{
    //创建线程执行目标类对象
    myRunnable mR = new myRunnable();
    //将Runnable接口的子类对象作为参数传递给Thread类的构造函数
    Thread t1 = new Thread(mR);
    Thread t2 = new Thread(mR);
    //开启线程
    t1.start();
    t2.start();
    for(int i = 0;i < 5;i++)
    {
        System.out.println("main线程正在执行!");
    }
}

方法三:Callable 接口。

停止线程

stop和destroy方法已过时,不建议使用。
设置标示位boolean flag,循环判断flag状态,通过更改flag状态终止循环停止线程。
快捷键预警:写for循环的时候可以1000.for直接出现写好的判断代码。(IDEA)

线程名称的获取和设置

String getName():返回该线程的名称(同 Thread.currentThread().getName())
Thread.currentThread():获取当前线程对象
Thread.currentThread().getName():获取当前线程对象的名称
setName():设置线程名称

public static void main(String[] args)
{
    myThread mT = new myThread();
    mT.start();
    mT.setName("thread1");    //设置线程名称
    //System.out.println(getName());  //使用getName()方法获取线程名称 在新线程中可以这样使用
    System.out.println(Thread.currentThread().getName());   //使用Thread.currentThread().getName()获取线程名称
    System.out.println(Thread.currentThread()); //获取当前线程对象
}

一些方法

1、Thread.sleep(long millis):休眠,单位毫秒。

public static void main(String[] args) {
    myThread mT = new myThread();
    mT.start();
    //使用sleep方法延时打印for循环
    for(int i = 0;i < 5;i++)
    {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(i);
    }
}

2、wait():等待。
3、setPriority(int newPriority):更改线程优先级。
4、join():等待该线程终止。
5、yield():暂停当前正在执行的线程对象,并执行其它线程。
6、interrupt():中断线程,不要使用。
7、isAlive():测试线程是否处于活动状态。

等待和唤醒机制

等待唤醒机制是为了方便处理进程之间通信的手段,多个线程在处理同一个资源时,由于处理的动作(线程的任务)不行同,为了使各个线程能够有效的利用资源,便采取了等待唤醒机制。
等待唤醒机制涉及到的方法:

  • wait():等待。将正在执行的线程释放其执行资格和执行权,并存储到线程池中
  • notify():唤醒。唤醒线程池中被 wait() 的线程,一次唤醒一个,而且是任意的
  • notifyAll():唤醒全部。可以将线程池中的所有 wati() 线程都唤醒

注:

  • 这些方法都是在同步中才有效,在使用时必须注明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程
  • 因为这些方法在使用的时候要注明所属的锁,而锁又是任意对象,所以这些方法是定义在 Object 类中的

分析:

  • 输入 inPut 类:输入完成后,必须等待输出结果打印结束才能进行下一次赋值,赋值后,执行wait()方法永远等待,直到被唤醒,唤醒后重新对变量赋值,赋值后再唤醒输出线程 notify(),自己再wait()
  • 输出 outPut 类:输出完成后,必须等待输入的重新赋值后才能进行下一次输出,在输出等待前,唤醒输入的notify(),自己再 wait() 永远等待,直到被唤醒

线程池

是一个线程容器,实现了线程的复用,不必反复创建与销毁。

创建线程池的两种方法

方法一:Runnable接口
通过线程池工厂创建,调用线程池中的方法获取线程,通过线程执行任务方法。
1、线程池创建工厂类:public static ExecutorService newFixedThreadPool(int nThreads)返回线程池对象。
2、线程池类:Future<?> submit(Runnable task)获取线程池中的某一个线程对象并执行。Future 接口:用来记录线程任务执行完毕后产生的结果。
3、使用线程池中的线程对象:创建线程池对象、创建 Runnable 接口子类对象、提交 Runnable 接口子类对象、关闭线程池。

//Runnable接口实现类
public class myRunnable implements Runnable {
    @Override
    public void run() {
        for(int i = 0;i < 5;i++)
        {
            System.out.println("myRunnable正在执行!");
        }
    }
}
//主线程中调用
public static void main(String[] args)
{
    //创建线程池对象
    ExecutorService ex = Executors.newFixedThreadPool(2);
    //创建Runnable实例对象
    myRunnable mR = new myRunnable();
    //从线程池中获取线程对象,调用myRunnable中的run(),没有返回值
    ex.execute(mR);
    //再获取个线程对象,调用myRunnable中的run(),有返回值
    ex.submit(mR);
    //关闭线程池
    ex.shutdown();
}

注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中。
方法二:Callable接口
由于Runnable 接口中发 run 方法是 void 类型,无返回值,并且不能抛异常,而Callable 接口中的 call 方法弥补了这两点缺陷,有返回值,并能抛异常,用法和 Runnable 接口一样。
1、线程池创建工厂类:public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象。
2、线程池类:<T> Future<T> submit(Callable<T> task)获取线程池中的某一个线程对象,并执行线程中的call()方法。Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用。
3、使用线程池中线程对象的步骤:创建线程池对象、创建Callable接口子类对象、提交Callable接口子类对象、关闭线程池。

//Callable接口实现类,call方法可抛出异常、返回线程任务执行完毕后的结果
public class myCallable implements Callable {
    @Override
    public Object call() throws Exception {
        System.out.println("myCallable线程!");
        Thread.sleep(1000);
        System.out.println("正在执行");
        return null;
    }
}
public static void main(String[] args)
{
    //创建线程池对象
    ExecutorService ex = Executors.newFixedThreadPool(2);
    //创建Callable实例对象
    myCallable mR = new myCallable();
    //从线程池中获取线程对象,然后调用myCallable中的call()
    ex.submit(mR);
    //再获取个线程对象,调用myCallable中的call()
    ex.submit(mR);
    //关闭线程池
    ex.shutdown();
}

原文链接:https://blog.youkuaiyun.com/One_L_Star/article/details/95985907
原文链接:https://blog.youkuaiyun.com/One_L_Star/article/details/96171934

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值