JAVA-16.1-线程常用方法、线程生命周期、线程组、线程池、计时器

一:线程中的一些方法(线程中存在的现象)
  1.1 线程加入
    public final void join()
    等待该线程运行完毕,然后剩下的线程再一起抢占CPU执行权

package com.edu.exercise_01;

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(getName()+" "+i);
        }
    }
}
package com.edu.exercise_01;
/**
 * 1.1 线程加入
        public final void join()
        等待该线程中止,其他线程才能继续抢着执行
 */
public class Test {
    public static void main(String[] args) throws Exception {
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();
        MyThread mt3 = new MyThread();

        mt1.setName("张三");
        mt2.setName("李四");
        mt3.setName("王五");

        mt1.start();
        mt1.join();

        mt2.start();
        mt3.start();
    }
}
//运行结果:
//张三 0
//张三 1
//张三 2
//张三 3
//张三 4
//李四 0
//李四 1
//李四 2
//王五 0
//李四 3
//王五 1
//李四 4
//王五 2
//王五 3
//王五 4

  1.2 线程礼让
    public static void yield():暂停当前正在执行的线程对象,并执行其他线程。
    作用:让线程间的执行更和谐一些,但是实际上做不到。可以通过后面讲解的知识来实现。

package com.edu.exercise_02;

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

            //实现线程礼让
            //public static void yield():暂停当前正在执行的线程对象,并执行其他线程。 
            //作用:让线程间的执行更和谐一些,但是实际上做不到
            Thread.yield();
        }
    }
}
package com.edu.exercise_02;

public class Test {
    public static void main(String[] args) {
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();
        MyThread mt3 = new MyThread();

        mt1.setName("小张");
        mt2.setName("小李");
        mt3.setName("小王");

        mt1.start();
        mt2.start();
        mt3.start();    
    }
}

  1.3 线程死亡
    public final void stop():直接杀死被清理掉
    public void interrupt():直接杀死,被清理之前前,会将剩下的代码执行完毕。

package com.edu.exercise_03;

import java.text.SimpleDateFormat;
import java.util.Date;

public class MyThread extends Thread{
    @Override
    public void run() {
        //获取开始时间
        System.out.println("StartTime:"+new SimpleDateFormat("HH:mm:ss").format(new Date()));

        //休眠10秒
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            System.out.println("被终止了");
        }

        //获取结束时间
        System.out.println("EndTime:"+new SimpleDateFormat("HH:mm:ss").format(new Date()));
    }
}
package com.edu.exercise_03;
/**
 * 1.3 线程死亡
   public final void stop():直接杀死(被清理)
   public void interrupt():直接杀死,在被清理之前会将剩下的代码执行完毕,然后才被清理
 *
 */
public class Test {
    public static void main(String[] args) {
        //创建线程对象
        MyThread mt = new MyThread();
        //开启线程对象
        mt.start();

        //主线程休眠3秒,睡眠的过程中将mt线程杀死
        try {
            Thread.sleep(3000);

            mt.interrupt();//首先输出StartTime:08:54:40
                            //3秒后输出:
                            /**      被终止了
                                     EndTime:08:54:43
                            */
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }



/*      
        //主线程休眠3秒,睡眠的过程中将mt线程杀死
        try {
            Thread.sleep(3000);

            mt.stop();//首先输出StartTime:08:57:13
                      //3秒后输出结果只有StartTime:08:48:50,并且终止线程
                      //stop划了一条横线表示该方法已经过时,但是还可以使用

        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
*/      

    }
}

  1.4 线程休眠
    static void sleep(long millis) 线程睡眠
二:线程的生命周期(画图讲解),面试题
  1.新建
  2.就绪
  3.运行
  4.有可能阻塞
  5.死亡
这里写图片描述

三:线程组
  3.1线程组:Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
  注意:默认情况下,所有的线程都属于主线程组。
  3.2线程组使用的一般步骤:
  ① a. 如果是给线程分组、命名,则首先创建MyRunnable类实现Runnable接口,重写run()方法;
    b. 如果是仅仅获取线程名称等,可以创建MyThread类继承Thread类,重写run()方法。
  ②创建线程组,并给线程组取名字
    ThreadGroup tg = new ThreadGroup(“我的线程组”);
  ③给线程设置分组
    a. MyRunnable mr = new MyRunnable();
     Thread t1 = new Thread(tg, mr);
    或者:
    b. Thread t2 = new Thread(tg, new MyRunnable());
  ④获取线程组对象
    ThreadGroup tg1 = t1.getThreadGroup();
  ⑤获取线程组对象的名称
    String name = tg1.getName()
案例1:创建线程获取对应的线程组对象,并获取名称

package com.edu.exercise_04;

public class MyExtends extends Thread{
    @Override
    public void run() { 
    }
}
package com.edu.exercise_04;
/** 案例1:创建线程获取对应的线程组对象,并获取名称
 * 
 *    1.默认情况下,所有的线程都属于主线程组。
      2. public final ThreadGroup getThreadGroup():获取线程对应的线程组对象
      3. 线程组对象掉用getName(),获取线程组对象的名称,返回值是String
 *
 */
public class TestExtends {
    public static void main(String[] args) {
        //创建2个线程
        MyExtends mt1 = new MyExtends();
        MyExtends mt2 = new MyExtends();
        //获取线程的线程组对象
        ThreadGroup tg1 = mt1.getThreadGroup();
        ThreadGroup tg2 = mt2.getThreadGroup();
        //获取线程组对象的名称
        System.out.println(tg1.getName());//main
        System.out.println(tg2.getName());//main
    }
}

案例2:创建线程组对象,给线程分配线程组

package com.edu.exercise_04;
public class MyRunnable implements Runnable{
    @Override
    public void run() { 
    }
}
package com.edu.exercise_04;
/** 案例2:创建线程组对象,给线程分配线程组
 * 
 * 构造一个新线程组 public ThreadGroup(String name)
 * 给线程设置分组 Thread(ThreadGroup group, Runnable target),注意需要的是实现Runnable接口的实现类对象
 */
public class TestRunnable {
    public static void main(String[] args) {
        //创建实现类对象
        MyRunnable mr = new MyRunnable();
        //构造一个新线程组 public ThreadGroup(String name)
        ThreadGroup tg = new ThreadGroup("我的线程组");

        //给线程设置分组 Thread(ThreadGroup group, Runnable target)
        Thread t1 = new Thread(tg, mr);
        Thread t2 = new Thread(tg, new MyRunnable());

        //获取线程名称
        System.out.println(t1.getThreadGroup().getName());//我的线程组
        System.out.println(t2.getThreadGroup().getName());//我的线程组
    }
}

四:线程池
4.1为什么要使用线程池?
  程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
  线程池的特点:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
  线程池如何创建?JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法public static ExecutorService newFixedThreadPool(int nThreads)
  
4.2线程池的使用步骤:
  ①创建线程池对象
    ExecutorService pool = Executors.newFixedThreadPool(2);
  ②创建Runnable实例或者Callable实例
  (创建Runnable实例,重写run()方法,没有返回值;或者创建Callable<>实例重写call()方法,有返回值,返回值类型必须是引用类型)
    MyRunnable my = new MyRunnable();
  ③提交Runnable实例
    pool.submit(my);
  ④关闭线程池
    pool.shutdown();
案例1:实现Runnable接口实现线程池的使用

package com.edu.exercise_05;

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
    }
}
package com.edu.exercise_05;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MrThreadPool {
    public static void main(String[] args) {
        //案例1:实现Runnable接口实现线程池的使用

        //1.创建线程池对象
        //ExecutorService pool = Executors.newFixedThreadPool(3);
        ExecutorService mrpool = Executors.newFixedThreadPool(2);

        //2.提交给线程池两个任务,都是打印0-99

        //创建Runnable实例,
        //MyRunnable my = new MyRunnable();
        MyRunnable mr1 = new MyRunnable();
        MyRunnable mr2 = new MyRunnable();

        //3.提交任务
        mrpool.submit(mr1);//一旦提交任务,就会自动开启线程,并执行run()方法中的代码,
                           //每个线程任务执行完毕,线程会自动关闭,但是并没有被终止,
                           //而是再次再次回到线程池中成为空闲状态,等待下一个对象来使用。
        mrpool.submit(mr2);

        //关闭线程池
        //void shutdown()
        mrpool.shutdown();

    }
}

案例2:实现Callable接口实现线程池的使用

package com.edu.exercise_05;

//Callable接口和Runnable接口很相似,不同的是前者有返回值,后者没有返回值

import java.util.concurrent.Callable;
public class MyCallable implements Callable{
    @Override
    public Object call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
        return null;
    }
}
package com.edu.exercise_05;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class McThreadPool {
    public static void main(String[] args) {
        //案例2:实现Callable接口实现线程池的使用
        //1.创建线程池
        ExecutorService mcpool = Executors.newFixedThreadPool(2);

        //2.创建任务
        MyCallable mc1 = new MyCallable();
        MyCallable mc2 = new MyCallable();

        //3.提交任务
        mcpool.submit(mc1);
        mcpool.submit(mc2);

        //4.关闭线程池
        mcpool.shutdown();
    }
}

案例3:实现Callable接口实现线程池的使用,实现多线程求和,1-10之和,1-100之和

package com.edu.exercise_06;
//实现Callable接口实现线程池的使用,实现多线程求和,1-10之和,1-100之和
import java.util.concurrent.Callable;
/**
 * 要想在主函数中直接给MyCallable类初始化,把开始值和结束值传进来,就应该想到利用构造方法传参
 *
 */
public class MyCallable implements Callable<Integer>{

    private int start;
    private int end;

    public MyCallable(int start , int end){
        this.start = start;
        this.end = end;
    }

    @Override
    public Integer call() throws Exception {
        //求和
        int sum = 0;
        for (int i = start; i < end + 1; i++) {
            sum+=i;
        }
        return sum;
    }
}

测试类:

package com.edu.exercise_06;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Test {
    public static void main(String[] args) throws Exception {
        //1.创建线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(2);

        //2.创建任务
        MyCallable mc1 = new MyCallable(1, 10);
        MyCallable mc2 = new MyCallable(1, 100);

        //3.添加任务
        Future<Integer> res1 = pool.submit(mc1);
        Future<Integer> res2 = pool.submit(mc2);

            //V get()如有必要,等待计算完成,然后获取其结果。
        System.out.println(res1.get());
        System.out.println(res2.get());

        //4.关闭线程池
        pool.shutdown();
    }
}

五:定时器
①Timer类
  public Timer()构造
  public void schedule(TimerTask task, long delay)延迟多久执行任务
  public void schedule(TimerTask task,long delay,long period)延迟多久执行任务,并以后每隔多久执行一次
  public void schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务
  public boolean cancel()取消这个任务

②TimerTask抽象类,用于创建一个计时任务
  public abstract void run()放的是所要执行的任务代码
案例1:演示以上方法的使用

package com.edu.exercise_07;

import java.util.Timer;
import java.util.TimerTask;

//需求:在5秒钟后,在控制台打印一句话,helloworld
public class TimerTest {
    public static void main(String[] args) {
        Timer t = new Timer();
        //public void schedule(TimerTask task, long delay)延迟多久执行任务
        //TimerTask是抽象类
        t.schedule(new MyTimerTask(t), 5000);


        //t.cancel();//如果在这里关闭的话,我们还没等任务执行完毕呢,计时器已经被关闭了

    }
}

class MyTimerTask extends TimerTask{

    private Timer t;
    public MyTimerTask(Timer t){
        this.t = t;
    }

    @Override
    public void run() {
        System.out.println("helloworld");

        t.cancel();//等待任务结束,将计时任务取消
    }
}

案例2:定时删除文件

package com.edu.exercise_08;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Test {
    public static void main(String[] args) throws Exception {
        /**
         * 案例:定时删除文件(需要在指定时间删除D://a.txt文件)
         * 所用方法:schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务
         * 时间点:16:30:00
         * 任务:删除D://a.txt文件
         */
        //1.创建定时器
        Timer t = new Timer();
        //2.指定的时间解析为日起对象
        String time = "2017-05-21 16:31:00";
        Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(time);

        //3.所用方法:schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务
        t.schedule(new MyTimerTask(t), date);
    }
}


class MyTimerTask extends TimerTask{

    //构造传参
    private Timer t;
    public MyTimerTask(Timer t) {
        this.t = t;
    }

    @Override
    public void run() {
        //任务:删除D://a.txt文件
        File file = new File("D://a.txt");
        file.delete();

        t.cancel();
    }   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值