java多线程

微笑、不失礼 提交于 2020-02-07 04:06:50

多线程简介

  • 进程

    进程是系统进行资源分配和调用的独立单位

  • 线程

    在同一个进程中又可以同时执行多个任务,每一个任务可以看做是一个线程
    线程是程序执行的单元、执行路径,也是程序使用 CPU 的基本单元

  • 多线程及其意义

    多线程不能提高程序的执行速度,但是可以使应用程序在抢占 CPU 资源时占得上风,但无法保证,线程的执行具有随机性

  • 并行和并发

    并行:逻辑上同时发生,指在某一个时间内同时运行多个程序
    并发:物理上同时发生,值在某一个时间点同时运行多个程序

  • Java 的运行原理

    由 java 命令启动 JVM,相当于启动了一个进程,该进程创建一个主线程去调用 main 方法,垃圾回收线程也从开始就已经启动,否则很容易造成内存溢出

创建多线程方法

  • 继承 Thread

    自定义类继承 Thread,并且重写 Thread 类的 run 方法(其中放入想要被线程执行的方法),创建对象,启动线程

  • 实现 Runnable 接口

    自定义类实现 Runnable 接口,重写 run 方法,创建该类对象,并把该对象作为构造 Thread 时的参数传入,用于解决单继承的局限性,也可以实现多个线程操作同一份资源,一般使用该方法进行多线程操作

  • 实现 Callable 接口

    该方案的优点是可以抛出异常,也有返回值,该返回值由 Future<?> f 接收,并通过其 get 方法获得
    与实现 Runnable 接口的方案类似,不同的是需要重写 call 方法
    此外 Callable 是带泛型的接口,泛型的类型绝定了 call 方法的返回值类型
    该方法不可搭配 Thread 使用,只能用在线程池中

构造方法

  • public Thread()
  • public Thread(Runnable target)
  • public Thread(Runnable target, String name)
  • public Thread(String name)
  • public Thread(ThreadGroup group, Runnable target)
  • public Thread(ThreadGroup group, Runnable target, String name)
  • public Thread(ThreadGroup group, String name)

成员方法

  • public String getName()

    获取线程名称

  • public void setName(String name)

    设置线程名称

  • public static Thread currentThread()

    返回正在执行的线程对象

  • public void setPriority(int newPriority)

    设置线程优先级

  • public static void sleep(long millis)

    设置线程休眠

  • public static void sleep(long millis, int nanos)
  • public void join()

    等待这个线程死亡,需要抛出异常 InterruptedException

  • public void join(long millis)

    等待这个线程死亡最多…毫秒

  • public void join(long millis, int nanos)
  • public static void yield()

    表示当前线程愿意让出当前使用的处理器,但仍不是绝对的

  • public void setDaemon(boolean on)

    将线程标记为守护线程或者用户线程,当运行的线程都是守护线程时,Java 虚拟机退出,该方法必须在启动线程之前调用

  • public void interrupt()

    中断线程

多线程实现案例 1

创建 Thread 子类,重写其 run 方法,创建多个线程对象,调用对象的 start 方法

package javaPackage;

class MyThread extends Thread {
    @Override
    public void run() {
        //一般来说,被线程执行的方法肯定是比较耗时的
        for (int i = 0; i < 200; i++) {
            System.out.println(i + this.getName());
        }
    }
}

public class JavaClass {
    public static void main(String[] args) {
        //Java不能直接调用系统功能,必须通过调用C/C++实现的方法来实现多线程程序
        //创建线程对象
        MyThread mt = new MyThread();
        //设置线程名
        mt.setName("hello");

        //启动线程
        //mt.run();
        //mt.run();

        //直接调用run方法是单线程
        //start方法首先启动了线程,然后再由jvm去调用该线程的run方法
        mt.start();
        //mt.start();
        //单个线程对象调用两次start方法会出现IllegalThreadStateException
        //如果要多线程执行,则需要创建多个线程对象
        MyThread mts = new MyThread();
        mts.start();
    }
}
多线程实现案例 2

实现 Runnable 接口(重写 run 方法),创建 Thread 对象(传入实现类对象),调用 start 启动线程

package javaPackage;

class RunnableDemo implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            //当前类不是继承自Thread,无法使用Thread方法,但是可以通过Thread.currentThread()间接使用
            System.out.println(Thread.currentThread().getName() + "   " + i);
        }
    }
}

public class JavaClass {
    public static void main(String[] args) {
        RunnableDemo rd = new RunnableDemo();
        Thread t1 = new Thread(rd, "张三");
        Thread t2 = new Thread(rd, "李四");
        t1.start();
        t2.start();
    }
}

线程调度

  • 分时调度

    轮流获得 CPU 使用权,平均分配每个线程占用 CPU 的时间

  • 抢占式调度

    优先让优先级高的线程使用 CPU,优先级相同的情况下随机选择

线程优先级

  • 线程优先级介于 1~10 之间,默认为 5
  • 通过 setPriority 方法设置线程优先级
  • 线程优先级高不一定优先执行,仍具有偶然性,在运行次数较多时优先效果比较明显
package javaPackage;

class ThreadPriority extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println(i + this.getName());
        }
    }
}

public class JavaClass {
    public static void main(String[] args) {
        ThreadPriority tp1=new ThreadPriority();
        ThreadPriority tp2=new ThreadPriority();
        ThreadPriority tp3=new ThreadPriority();
        tp1.setName("张三");
        tp2.setName("李四");
        tp3.setName("王五");
        tp1.setPriority(8);
        tp1.start();
        tp2.start();
        tp3.start();
    }
}

线程控制

休眠线程

使用 sleep 函数实现线程休眠
传入一个参数为毫秒,传入两个参数为毫秒和纳秒

package javaPackage;

import java.util.Date;

class ThreadSleep extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(this.getName() + new Date());
            try{
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class JavaClass {
    public static void main(String[] args) {
        ThreadSleep ts1 = new ThreadSleep();
        ThreadSleep ts2 = new ThreadSleep();
        ThreadSleep ts3 = new ThreadSleep();
        ts1.start();
        ts2.start();
        ts3.start();
    }
}

加入线程

当且仅当某个线程执行完成之后,其余线程才可以执行
使用 join 方法实现

package javaPackage;

import java.util.Date;

class ThreadJion extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(this.getName() + new Date());
        }
    }
}

public class JavaClass {
    public static void main(String[] args) throws InterruptedException {
        ThreadJion ts1 = new ThreadJion();
        ThreadJion ts2 = new ThreadJion();
        ThreadJion ts3 = new ThreadJion();
        ts1.start();
        ts1.join();
        ts2.start();
        ts3.start();
    }
}

礼让线程

使用 yield 方法实现线程的让出
可以让多个线程的执行更加和谐,但是不能靠它保证一个线程一次

package javaPackage;

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

public class JavaClass {
    public static void main(String[] args) {
        ThreadDemo td1 = new ThreadDemo();
        ThreadDemo td2 = new ThreadDemo();
        ThreadDemo td3 = new ThreadDemo();
        td1.start();
        td2.start();
        td3.start();
    }
}

守护线程

设置当某线程结束执行时,该线程也停止执行(有延迟)
使用 setDaemon 方法实现

package javaPackage;

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

public class JavaClass {
    public static void main(String[] args) {
        ThreadDemo td1 = new ThreadDemo();
        ThreadDemo td2 = new ThreadDemo();
        td1.setDaemon(true);
        td2.setDaemon(true);
        td1.start();
        td2.start();
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + "      " + i);
        }
    }
}

中断线程

stop 方法已弃用,终止线程后不会抛出异常
interrupt 方法,终止线程会抛出异常 InterruptedException

package javaPackage;

class ThreadDemo extends Thread {
    @Override
    public void run() {
        try{
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            System.out.println("线程被终止了");
        }
    }
}

public class JavaClass {
    public static void main(String[] args) {
        ThreadDemo td = new ThreadDemo();
        td.start();
        try{
            Thread.sleep(3000);
            //interrupt()方法终止线程并抛出InterruptedException异常,该异常被捕获后可继续执行catch中的内容--"线程被终止了"
            td.interrupt();
            //stop()方法终止线程不会继续执行catch中的内容--"线程被终止了"
            //td.stop();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程安全

  • 当多线程操作一块共享数据时,可能因为线程抢占资源发生错误操作数据的情况,进而导致判断语句失效、顺序错乱等问题
  • 解决该问题的方案为锁定多条语句操作共享数据的代码,使得任意时刻只能有一个线程执行

判断线程安全问题存在可能性的标准

  • 是否是多线程环境
  • 是否有共享数据
  • 是否有多条语句操作共享数据

线程安全问题的解决方案:同步机制

格式为 synchronized(对象){需要同步的代码}
该方案的关键在于传入的对象上,该对象就像是一把锁,使用的必须是同一把锁,除此之外没有要求
同步会降低代码的执行效率

package javaPackage;

class Runnablelmpl implements Runnable {
    private int num = 100;
    private Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                //注意判断必须放在循环里面,如果是放在while括号内,或者达到条件退出循环会出现num的值出现负数的情况
                if (num >= 0) {
                    System.out.println(num-- + "      " + Thread.currentThread().getName());
                }
                else {
                    break;
                }
            }
        }
    }
}

public class JavaClass {
    public static void main(String[] args) {
        Runnablelmpl rl = new Runnablelmpl();
        Thread t1 = new Thread(rl, "一号");
        Thread t2 = new Thread(rl, "二号");
        Thread t3 = new Thread(rl, "三号");
        t1.start();
        t2.start();
        t3.start();
    }
}

同步方法的格式及锁对象问题

  • 同步方法只需将 synchronized 关键字用于修饰方法即可
  • 同步方法无法自行设置锁对象,其锁对象就是 this
  • 静态方法的锁对象是当前的类的字节码文件对象(Object.class)

Lock

Lock 接口提供了比 synchronized 方法更广泛的锁定操作

实现类
  • ReentrantLock
  • ReentrantReadWriteLock.ReadLock
  • ReentrantReadWriteLock.WriteLock
成员方法
  • public void lock()

    获取锁,加锁

  • public void unlock()

    释放锁

package javaPackage;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Runnablelmpl implements Runnable {
    private int tickets = 100;

    //定义锁对象
    private Lock lock =new ReentrantLock();

    public int getTickets() {
        return tickets;
    }

    @Override
    public void run() {
        while (true) {
            //加锁
            lock.lock();
            if (tickets > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "号窗口正在出售第" + (100 - (--tickets)) + "张票");
            } else {
                break;
            }
            //释放锁
            lock.unlock();
        }
    }
}

public class JavaClass {
    public static void main(String[] args) {
        Runnablelmpl rl = new Runnablelmpl();
        Thread t1 = new Thread(rl, "一");
        Thread t2 = new Thread(rl, "二");
        Thread t3 = new Thread(rl, "三");
        t1.start();
        t2.start();
        t3.start();
    }
}

死锁问题

两个或两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待的现象
如果出现了同步嵌套,就容易产生死锁问题

javapackage javaPackage;

class MyLock {
    //创建两把锁对象
    private static final Object objA = new Object();
    private static final Object objB = new Object();

    public static Object getObjA() {
        return objA;
    }

    public static Object getObjB() {
        return objB;
    }
}

class DeadLock extends Thread {
    private boolean flag;

    public DeadLock(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if (this.flag) {
            synchronized (MyLock.getObjA()) {
                System.out.println("if objA");
                synchronized (MyLock.getObjB()) {
                    System.out.println("if objB");
                }
            }
        } else {
            synchronized (MyLock.getObjB()) {
                System.out.println("else objB");
                synchronized (MyLock.getObjA()) {
                    System.out.println("else objA");
                }
            }
        }
    }
}

public class JavaClass {
    public static void main(String[] args) {
        DeadLock dl1 = new DeadLock(true);
        DeadLock dl2 = new DeadLock(false);
        dl1.start();
        dl2.start();
        //最后的输出为if objA和else objB
        //或者是else objB和if objA
    }
}

部分线程安全与不安全类

  • StringBuffer、Vector、Hashtable 都是线程安全的类
  • 即使需要线程安全也不需要用 Vector,而是用 Collections 中设置的线程安全的类
package javaPackage;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class JavaClass {
    public static void main(String[] args) {
        //线程不安全
        List<String> list1 = new ArrayList<String>();
        //线程安全
        List<String> list2 = Collections.synchronizedList(new ArrayList<String>());
    }
}

线程通信

以生产者-商品-消费者为例,生产和消费均为线程操作,如果消费者先抢到执行权,则无商品可消费,程序出错,因此必须让生产者先执行生产操作,再通知消费者进行消费,消费者消费完了商品再通知生产者生产商品

未通信导致错误案例
package javaPackage;

class Person {
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

class SetThread implements Runnable {
    private Person person;

    public SetThread(Person p) {
        this.person = p;
    }

    public Person getPerson() {
        return person;
    }

    @Override
    public void run() {
        person.setAge(10);
        person.setName("张三");
    }
}

class GetThread implements Runnable {
    private Person person;

    public GetThread(Person p) {
        this.person = p;
    }

    public Person getPerson() {
        return person;
    }

    @Override
    public void run() {
        System.out.println("此人名为" + person.getName() + ",今年" + person.getAge() + "岁了");
    }
}

public class JavaClass {
    public static void main(String[] args) {
        Person p = new Person();
        SetThread st = new SetThread(p);
        GetThread gt = new GetThread(p);
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(gt);
        t1.start();
        t2.start();
        //如果t2抢到执行优先权,则打印出的名字和年龄都有可能变成默认值
    }
}

等待唤醒机制

Object 类中提供了等待唤醒机制的方法

  • public void notify()

    唤醒在锁上等待的单个线程

  • public void notifyAll()

    唤醒在锁上等待的所有线程

  • public void wait()

    在其他线程调用此对象的 notify 或者 notifyAll 方法前,导致该线程等待

  • public void wait(long timeout)

    在未传形参的基础上添加最长等待时间

  • public void wait(long timeout, int nanos)
等待唤醒机制实现案例
package javaPackage;

class Person {
    private String name;
    private int age;
    //flag用于标记对象是否被初始化
    private boolean flag = false;

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

class SetThread implements Runnable {
    private Person person;
    private int select = 0;

    public SetThread(Person p) {
        this.person = p;
    }

    public Person getPerson() {
        return person;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (person) {
                if (person.isFlag()) {
                    try {
                        person.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (select % 2 == 0) {
                    person.setAge(10);
                    person.setName("张三");
                } else {
                    person.setAge(34);
                    person.setName("李四");
                }
                select++;
                //修改标记
                person.setFlag(true);
                //唤醒休眠线程
                person.notify();
            }
        }
    }
}

class GetThread implements Runnable {
    private Person person;

    public GetThread(Person p) {
        this.person = p;
    }

    public Person getPerson() {
        return person;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (person) {
                if (!person.isFlag()) {
                    try {
                        person.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("此人名为" + person.getName() + ",今年" + person.getAge() + "岁了");
                //修改标记
                person.setFlag(false);
                //唤醒休眠线程
                person.notify();
            }
        }
    }
}

public class JavaClass {
    public static void main(String[] args) {
        Person p = new Person();
        SetThread st = new SetThread(p);
        GetThread gt = new GetThread(p);
        Thread t1 = new Thread(st);
        Thread t2 = new Thread(gt);
        t1.start();
        t2.start();
        //如果t2抢到执行优先权,则打印出的名字和年龄都有可能变成默认值
    }
}

ThreadGroup 线程组

线程组可以对一批线程进行分类管理
默认情况下所有线程都属于主线程组
可以通过 Thread 的 public Thread(ThreadGroup tg, Runnable r, String name)构造方法来将线程加入到指定的线程组中

package javaPackage;

class Runnablelmpl implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getThreadGroup().getName());
    }
}

public class JavaClass {
    public static void main(String[] args) {
        method2();
    }

    private static void method1() {
        Runnablelmpl rl = new Runnablelmpl();
        Thread t1 = new Thread(rl, "一号");
        Thread t2 = new Thread(rl, "二号");
        ThreadGroup tg1 = t1.getThreadGroup();
        ThreadGroup tg2 = t2.getThreadGroup();
        System.out.println(tg1.getName() + "      " + tg2.getName());
    }

    private static void method2() {
        ThreadGroup tg = new ThreadGroup("新的线程组");
        Runnablelmpl rl = new Runnablelmpl();
        Thread t1 = new Thread(tg, rl, "线程一");
        Thread t2 = new Thread(tg, rl, "线程二");
        t1.start();
        t2.start();
    }
}

构造方法

  • public ThreadGroup(String name)
  • public ThreadGroup(ThreadGroup parent, String name)

成员方法

  • public ThreadGroup getThreadGroup()

    属于 Thread 的方法,返回线程所属的线程组

  • public String getName()
  • public void setMaxPriority(int pri)

    设置最大优先级

  • public void setDaemon(boolean daemon)

    更改此线程组的守护程序状态

  • public void interrupt()

线程池

一开始就产生一些线程,需要使用时取出线程,使用完毕后线程返回到池中,从而减少开启线程的资源消耗
使用 Executors 工厂类来产生线程池,其方法返回 ExecutorService 对象,该对象表示线程池,可以执行 Runnable 对象或者 Callable 对象代表的线程

Executors 成员方法

  • public static ExecutorService newCachedThreadPool()

    创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程

  • public static ExecutorService newFixedThreadPool(int nThreads)

    创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程

  • public static ExecutorService newSingleThreadExecutor()

    创建一个使用从无界队列运行的单个工作线程的执行程序

  • public void shutdown()

    启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务

ExecutorService 成员方法

  • public Future<?> submit(Runnable task)

    提交一个可运行的任务执行,并返回一个表示该任务的未来

  • public Future submit(Callable task)

    提交值返回任务以执行,并返回代表任务待处理结果的 Future

线程池实现案例一:Runnable
package javaPackage;

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

class Runnablelmpl implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + "          " + i);
        }
    }
}

public class JavaClass {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(2);
        pool.submit(new Runnablelmpl());
        pool.submit(new Runnablelmpl());
        //代码执行结束后不会退出程序,需要关闭线程池才会退出
        pool.shutdown();
    }
}
线程池实现案例二:Callable
package javaPackage;

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

class Callablelmpl implements Callable {
    @Override
    public Object call() throws Exception {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "       " + i);
        }
        return null;
    }
}

public class JavaClass {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newFixedThreadPool(2);
        pool.submit(new Callablelmpl());
        pool.submit(new Callablelmpl());
        pool.shutdown();
    }
}

线程池实现案例三:Callable 带泛型
package javaPackage;

import java.util.concurrent.*;

class Callablelmpl implements Callable<Integer> {
    private int num;

    public Callablelmpl(int num) {
        this.num = num;
    }

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < this.num; i++) {
            sum += i;
        }
        return sum;
    }
}

public class JavaClass {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(2);
        Future<Integer> f1 = pool.submit(new Callablelmpl(13));
        Future<Integer> f2 = pool.submit(new Callablelmpl(20));
        System.out.println(f1.get());
        System.out.println(f2.get());
        pool.shutdown();
    }
}
匿名内部类方式实现多线程案例
package javaPackage;

import java.util.concurrent.*;

public class JavaClass {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        method1();
        method2();
        methoc3();
    }

    private static void method1() {
        //继承thread类实现多线程
        new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + "       " + i);
                }
            }
        }.start();
    }

    private static void method2() {
        //实现Runnable接口实现多线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + "       " + i);
                }
            }
        }).start();
    }

    private static void methoc3() throws ExecutionException, InterruptedException {
        //实现Callable接口实现多线程
        ExecutorService pool = Executors.newFixedThreadPool(1);
        Future<Integer> f = pool.submit(new Callable<Integer>() {
            private int num = 10;

            @Override
            public Integer call() throws Exception {
                return num;
            }
        });
        System.out.println(f.get());
    }
}

Timer 定时器

定时器可以安排线程在后台线程中执行的任务,可以安排任务执行一次或者定期重复执行

构造方法

  • public Timer()
  • public Timer(boolean isDaemon)

    其相关线程可以指定为 run as a daemon

  • public Timer(String name)
  • public Timer(String name, boolean isDaemon)

成员方法

  • public void cancel()

    终止此计时器,丢弃任何当前计划的任务

  • public int purge()

    从该计时器的任务队列中删除所有取消的任务

  • public void schedule(TimerTask task, Date time)

    在指定的时间安排指定的任务执行

  • public void schedule(TimerTask task, Date firstTime, long period)

    从指定的时间开始 ,对指定的任务执行重复的固定延迟执行

  • public void schedule(TimerTask task, long delay)

    在指定的延迟之后安排指定的任务执行

  • public void schedule(TimerTask task, long delay, long period)

    在指定的延迟之后开始,重新执行固定延迟执行的指定任务

  • public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)

    从指定的时间开始,对指定的任务执行重复的固定速率执行

  • public void scheduleAtFixedRate(TimerTask task, long delay, long period)

    在指定的延迟之后开始 ,重新执行固定速率的指定任务

定时器的简单实现案例
package javaPackage;

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

class MyTask extends TimerTask {
    private Timer time;

    public MyTask() {
    }

    public MyTask(Timer t) {
        this.time = t;
    }

    @Override
    public void run() {
        System.out.println("hello world");
        //结束任务
        time.cancel();
    }
}

public class JavaClass {
    public static void main(String[] args) {
        Timer t = new Timer();
        t.schedule(new MyTask(t), 3000);
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!