栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java 多线程

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java 多线程

目录

1. 多线程介绍

1.并发与并行

2.进程与线程

3.多线程概述

3.1 运行原理

3.2 主线程(单线程)

3.3 单线程和多线程图例

2. Thread类

2.1 创建多线程

2.1.1 创建线程方式一:继承Thread类

2.1.2 创建线程方式二:实现Runnable接口

2.1.3 两种实现多线程方式的区别

2.1.4 创建线程的写法

2.2 Thread常用方法使用

 2.2.1 线程名称的获取与更改

 2.2.2 程序睡眠sleep方法

3 线程安全

3.1 线程安全演示

3.2 线程同步

3.2.1 同步代码块

 3.2.2 同步方法

3.3 Lock接口

3.4 StringBuffer与StringBuilder线程安全的区别

3.5 细节问题

3.6 死锁

4.案例

4.1 线程等待唤醒机制(通信)

4.2 定时器

1. 多线程介绍

1.并发与并行

        并发(concurrency):“同时存在”

​            并发指能够让多个任务在逻辑上交织执行的程序设计

        并行(parallelismparallelism):”同时执行“

​            并行指物理上同时执行多个任务

2.进程与线程

        1.进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。

        2.线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

        简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

3.多线程概述

日常生活中很多事都是可以同时进行的,例如:人可以同时进行呼吸、血液循环、思考问题等活动。

多线程是指一个应用程序中有多条并发执行的线索,每条线索都被称作为一个线程,它们会交替执行,彼此之间可以进行通信。​

3.1 运行原理

        CPU在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另外一个线程,这个叫做线程上下文切换(对于进程也是类似)

分时调度

        所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。

抢占式调度

        优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。

3.2 主线程(单线程)

JVM启动后,会有一个执行路径(线程)从main方法开始的,一直执行到main方法结束,这个线程在Java中称之为主线程。(非主线程为子线程)

(java中其实并无主线程与子线程之分,都为独立的线程,为了更能好区分我们将其这样划分)

当程序的主线程执行时,如果遇到了循环而导致程序在指定位置停留时间过长,则无法马上执行下面的程序,需要等待循环结束后能够执行。

那么,能否实现一个主线程负责执行其中一个循环,再由另一个线程负责其他代码的执行,最终实现多部分代码同时执行的效果?

能够实现同时执行,通过Java中的多线程技术来解决该问题。

代码示例:

public class Demo01 {
    public static void main(String[] args) {
        System.out.println("main 方法被调用了!");
        MyThread myThread = new MyThread();
        myThread.run();
        System.out.println("程序结束!");

    }
}
class MyThread{
    public void run(){
        while(true){
            System.out.println("MyThread类的run方法被运行了!");
        }
    }
}

程序一直打印的是“MyThread类的run()方法在执行”,这是因为该程序是一个单线程程序,当调用MyThread类的run()方法时,遇到死循环,循环会一直执行,因此MyThread类的打印语句讲永远执行,并不会执行 System.out.println("程序结束!");

3.3 单线程和多线程图例

2. Thread类

Thread是Java程序中开启多线程的类,Java 虚拟机允许应用程序并发地运行多个执行线程。

Thread的构造方法
Thread()
          分配新的 Thread 对象。
Thread(String name)
          分配新的 Thread 对象。
Thread(Runnable target)
          分配新的 Thread 对象。
Thread(Runnable target, String name)
          分配新的 Thread 对象。
Thread的常用方法
 voidrun()
          如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
static voidsleep(long millis)
          在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
 void

start()
          使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

voidsetDaemon(boolean on)
          将该线程标记为守护线程或用户线程。
 StringgetName()
          返回该线程的名称。
 voidsetName(String name)
          改变线程名称,使之与参数 name 相同。
static ThreadcurrentThread()
          返回对当前正在执行的线程对象的引用。

2.1 创建多线程

2.1.1 创建线程方式一:继承Thread类

创建线程的步骤:

  1. 定义一个类继承Thread类。
  2. 重写run方法。
  3. 创建子类对象
  4. 调用start方法,开启线程并让线程执行,同时还会告诉JVM去调用run方法。 

注:

调用run方法不开启线程。仅是对象调用方法。

调用start开启线程,并让jvm调用run方法在开启的线程中执行。

代码示例:

public class Demo02 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        //开始多线程(从这里才开始多线程,若上面有代码,这上面的代码先执行)
        myThread.start();   //1
        for (int i = 0; i < 5; i++) {   //2
            System.out.println("main正在运行!");
        }
        System.out.println("程序结束!");    //3
        //1 2 3的代码运行顺序会不同

    }
}
class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("MyThread类的run()方法被调用了!");
    }
}

运行结果1:

 运行结果2:

 运行结果3:

 ......

线程模型

主线程会等待子线程执行完毕吗?

​    需要分清Java的线程模型,JVM中不区分主线程和用户线程(我们通过Thread创建的线程),各个线程是独立的,但是JVM把线程分为前台线程和后台线程,后台线程官方术语叫守护线程,JVM结束的条件是所有的前台线程结束。正常构建的线程(子线程)都是前台线程,可以在线程未开始前调用Thread类的setDaemon(true)方法将线程改变为**后台守护线程**。

代码示例:

public class Text {
    public static void main(String[] args) {
        MyThread2 thread = new MyThread2();
        //将前台线程变为守护线程
        thread.setDaemon(true);//若该代码,程序将不会结束循环
        //开启多线程
        thread.start();
        System.out.println("程序结束!");

    }
}
class MyThread2 extends Thread{
    @Override
    public void run() {
        while (true) {
            System.out.println("MyThread2的run方法被调用了!");
        }
    }
}

运行结果:

2.1.2 创建线程方式二:实现Runnable接口

创建线程的另一种方式是实现Runnable接口。该接口有一个未实现的方法(run)。

创建Runnable的子类对象,通过构造方法传入到Thread对象中即可。

Thread的构造方法
Thread(Runnable target)
          分配新的 Thread 对象。
Thread(Runnable target, String name)
          分配新的 Thread 对象。

创建线程的步骤。

  1. 定义类实现Runnable接口
  2. 覆盖接口中的run方法
  3. 创建Thread类的对象
  4. 将Runnable接口的子类对象作为参数传递给Thread类的构造函数

        调用Thread类的start方法开启线程

代码示例:

public class Demo05 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
        System.out.println("程序结束");
        System.out.println("程序结束");

    }

}
class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("MyRunnable的run被执行了!!!");
    }
}

运行结果:

2.1.3 两种实现多线程方式的区别

1.线程创建方式一:模拟买票程序

public class Demo01 {
    public static void main(String[] args) {
        //3个窗口进行卖票
        TickerWindow t1 = new TickerWindow();
        t1.start();
        TickerWindow t2 = new TickerWindow();
        t2.start();
        TickerWindow t3 = new TickerWindow();
        t3.start();

    }
}

class TickerWindow extends Thread {
    //票数
    private int ticket = 5;

    @Override
    public void run() {
        while (ticket > 0) {
            System.out.println("售票成功,购买了第" + (ticket--) + "张票。");
        }
    }
}

运行结果:

 上面代码运行结果可以看出,每张票都被出售了3次,出现这种现象的原因是3个线没有共享5张票,而是各自出售了5张票,在程序中创建了3个TicketWindows对象,就等于创建了3个售票程序,每个售票程序都有5张票,每个线程在独立地处理各自的资源。

由于现实中票资源是共享的,因此上面的运行结果显然不合理,为了保证资源共享,在程序中只能创建一个售票对象,然后开启多个线程去运行同一个售票对象的售票方法,简单来说就是四个线程同事运行同一个售票程序,这时候就需要多线程。使用多线程的第二种方式。

2.实现接口的方式创建线程:模拟卖票程序

public class Demo02 {
    public static void main(String[] args) {
        //多个Thread对象共享了MyRunnable对象
        MyRunnable myRunnable = new MyRunnable();
        Thread t1 = new Thread(myRunnable,"窗口1");
        t1.start();
        Thread t2 = new Thread(myRunnable,"窗口2");
        t2.start();
        Thread t3 = new Thread(myRunnable,"窗口3");
        t3.start();
    }
}
class MyRunnable implements Runnable{
    //票数
    private int ticket = 5;

    @Override
    public void run() {
        while (ticket > 0) {
            System.out.println("售票成功,购买了第" + (ticket--) + "张票。");
        }
    }
}

运行结果:

 

实现Runnable接口相对于继承Thread类来说,有如下显著好处:

  1. 适合多个相同程序代码的线程去处理同一个资源的情况,把线程同程序代码、数据有效的分离
  2. 可以避免由Java的单继承带来的局限性

2.1.4 创建线程的写法
public static void main(String[] args) {
        //使用匿名内部类创建线程
        //1.继承方法
        Thread t1 = new Thread() {
            @Override
            public void run() {
                System.out.println("我是线程1");
            }
        };
        t1.start();
        
        //2.实现接口
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("我是线程2");
            }
        };
        Thread t2 = new Thread(runnable);
        t2.start();
        
        //3.不创建对象,直接调用start
        //这里使用label表达式,也可以使用匿名内部类
        new Thread(() -> System.out.println("我是线程3")).start();
        
    }

运行结果:

2.2 Thread常用方法使用

 2.2.1 线程名称的获取与更改

开启的线程都会有自己的独立运行栈内存,那么这些运行的线程的名字是什么呢?该如何获取呢?

通过结果观察,原来主线程的名称:main;自定义的线程:Thread-0,线程多个时,数字顺延。如Thread-1

进行多线程编程时,不要忘记了Java程序运行是从主线程开始,main方法就是主线程的线程执行内容。

 StringgetName()
          返回该线程的名称。
 voidsetName(String name)
          改变线程名称,使之与参数 name 相同。
static ThreadcurrentThread()
          返回对当前正在执行的线程对象的引用。

代码示例:

public static void main(String[] args) {
        //获取当前线程对象
        Thread thread = Thread.currentThread();
        //获取当前线程名称
        System.out.println("当前线程名称为:" + thread.getName());
        //创建线程
        Thread thread1 = new Thread() {
            @Override
            public void run() {
                System.out.println("子线程运行了!");
                System.out.println("获取当前线程名称:" + getName());
            }
        };
        thread1.run();
        //更改线程名称
        thread1.setName("子线程");
        thread1.start();
        
    }

运行结果:

 2.2.2 程序睡眠sleep方法
static voidsleep(long millis)
          在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

代码:

public static void main(String[] args) throws InterruptedException {
        System.out.println("我执行了程序!");
        Thread thread = new Thread();
        //单位为毫秒
        thread.sleep(5000);//使程序睡眠(停顿)5秒
        System.out.println("程序结束!");
    }

运行结果:

3 线程安全

3.1 线程安全演示
public static void main(String[] args) {
        //多个Thread对象共享了Runnable对象
        Runnable runnable = new Runnable() {
            //票数
            private int ticket = 5;

            @Override
            public void run() {
                while (true) {
                    if (ticket > 0) {
                        try {
                            //让程序停顿1毫秒,使问题更容易显示
                            Thread.sleep(1);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        String name = Thread.currentThread().getName();
                        System.out.println(name + "正在出售第" + (ticket--) + "张票。");
                    } else {
                        break;
                    }
                }
            }
        };
        Thread t1 = new Thread(runnable, "窗口1");
        t1.start();
        Thread t2 = new Thread(runnable, "窗口2");
        t2.start();
        Thread t3 = new Thread(runnable, "窗口3");
        t3.start();

    }

运行结果:

什么是线程安全问题?

​    **单线程不会发生线程安全问题**

​    **如果变量只访问,不修改,那么不会发生线程安全问题**

​    代码在运行的过程中,如果一个共享的变量被其它线程修改,那么对于当前线程而言就发生了线程安全问题

线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,那么这个全局变量是线程安全的;

若有多个线程同时执行写操作(修改),都需要考虑线程同步,否则就可能发生线程安全问题。

3.2 线程同步

解决线程安全问题?

​    让可能发生线程安全的代码在某个时刻只允许一个线程能够访问(线程同步)

Java中提供了线程同步机制,它能够解决上述的线程安全问题。

线程同步的方式有两种

  1. 方式一:同步代码块
  2. 方式二:同步方法

3.2.1 同步代码块

同步代码块: 在代码块声明上,加上synchronized,格式如下:

synchronized (锁对象(lock)) {

        可能会产生线程安全问题的代码

}

释放锁:就是让其他线程能够访问

释放锁时机:同步代码块执行结束

同步代码块中的锁对象可以是任意的对象;但多个线程时,要使用同一个锁对象才能够保证线程安全。

  1. lock 是一个锁对象,是同步代码块的关键,锁对象可以是任意类型的对象,是多个线程共享的锁对象
    1. 同步的好处:解决了多线程并发访问的问题,使多线程并发访问的共享数据能够保持一致性;
    2. 被锁对象:表示如果当前线程访问"被锁对象"的synchronized的代码块时,其它线程不能访问此代码块,另外,也不能访问"被锁对象"中的其它synchronized的代码块;
    3. 必须是唯一的,“任意”说的是共享锁对象的类型。所以,锁对象的创建代码不能放到run()方法中,否则每个线程都会创建一个新的对象,每个锁都有自己的标志,那就没有意义了
    4. 同步的弊端
      1. 使当前的对象的工作效率降低;因为要处理线程同步的问题;
  2. 主要优化票池类,注意synchronized部分

代码示例:

public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            private int ticket = 5;
            Object obj = new Object();

            @Override
            public void run() {
                while (true) {
                    //同步代码块
                    //被锁对象可以使任意类型的对象
                    //但是多个线程的被锁对象必须是同一个
                    synchronized (obj){
                        if (ticket > 0) {
                            try {
                                Thread.sleep(1);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            String name = Thread.currentThread().getName();
                            System.out.println(name + "正在出售第" + (ticket--) + "张票。");
                        } else {
                            break;
                        }
                    }

                }
            }
        };
        Thread t1 = new Thread(runnable, "窗口1");
        t1.start();
        Thread t2 = new Thread(runnable, "窗口2");
        t2.start();
        Thread t3 = new Thread(runnable, "窗口3");
        t3.start();

    }

运行结果:

 3.2.2 同步方法

同步方法:在方法声明上加上synchronized

public synchronized void method(){

    可能会产生线程安全问题的代码

}

释放锁时机:方法结束时

同步方法中的锁对象是 this

代码示例:

public class Demo01 {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            TicketPool pool = new TicketPool();
            @Override
            public void run() {
                while (true) {
                    int ticket = pool.getTicket();
                    if(ticket != 0 ){
                        String name = Thread.currentThread().getName();
                        System.out.println(name + "正在出售第" + ticket + "张票。");
                    }else{
                        break;
                    }
                }
            }
        };
        Thread t1 = new Thread(runnable, "窗口1");
        t1.start();
        Thread t2 = new Thread(runnable, "窗口2");
        t2.start();
        Thread t3 = new Thread(runnable, "窗口3");
        t3.start();
    }
}
public class TicketPool {
    private int ticket = 5;

    //同步方法
    public synchronized int getTicket() {
        if (ticket > 0) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return ticket--;
        }
        return 0;

    }
}

运行结果:

3.3 Lock接口

JDK5的Lock锁

        在JDK5之前我们使用synchronized定义同步代码块,同步方法;这是一种锁的方式;

        在JDK5之后,出现了一种新的锁的方式,使用:Lock(接口),

Lock 实现提供了比使用 synchronized方法和语句可获得的更广泛的锁定操作。

Lock接口常用方法
 voidlock()
          获取锁。
 voidunlock()
          释放锁。

用法:

Lock l = new ReentrantLock(); //使用实现类ReentrantLock创建Lock接口对象

l.lock();// 加锁(只让一个线程进入)

try {

        // access the resource protected by this lock

} finally {

        l.unlock();// 释放锁

}        

代码示例:

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

public class Deno01 {
    public static void main(String[] args) {
        //使用实现类ReentrantLock创建Lock接口对象
        Lock lock = new ReentrantLock();

        Runnable runnable = new Runnable() {
            private int ticket = 5;
            @Override
            public void run() {
                //加锁
                while(true){
                    lock.lock();
                    try {
                        if(ticket>0){
                            try {
                                Thread.sleep(1);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            String name = Thread.currentThread().getName();
                            System.out.println(name+"正在出售第"+(ticket--)+"张票!");
                        }else{
                            break;
                        }
                    } finally {
                        //释放锁
                        lock.unlock();
                    }
                }
            }
        };
        Thread t1 = new Thread(runnable,"窗口1");
        t1.start();
        Thread t2 = new Thread(runnable,"窗口2");
        t2.start();
        Thread t3 = new Thread(runnable,"窗口3");
        t3.start();
    }
}

运行结果:

3.4 StringBuffer与StringBuilder线程安全的区别

StringBuffer:线程同步的,线程安全的,效率低;(以append方法为例)

StringBuilder:线程不同步的,线程不安全的,效率高;(以append方法为例)

另外:ArrayList线程不同步的,线程不安全的,效率高;

3.5 细节问题

Java中两个线程是否可以同时访问一个对象的两个不同的synchronized方法,以StringBuffer为例,截图如下

两个线程是否可以同时访问appedn和delete方法?

不可以

        synchronized方法使用了java类的内置锁,即锁住的是方法所属对象本身(可以理解为将类都锁上了),同一个锁某个时刻只能被一个执行线程所获取,因此其他线程都得等待锁的释放

模拟解释:

StringBuffer中append和delete方法都使用了synchronized进行同步现有线程1和线程2

        线程1调用了append方法(没有执行完)

        此时线程2调用delete方法就需要等待线程1释放锁

3.6 死锁

同步锁使用的弊端:当线程任务中出现了多个同步锁时,如果同步中嵌套了其他的同步。这时容易引发一种现象:程序出现无限等待,这种现象我们称为死锁。这种情况能避免就避免掉。

死锁产生的四个必要条件:

        

  1. 互斥使用,当一个资源被一个线程使用是,别的线程不能使用
  2. 不可抢占,不可抢占别人的资源,只能由使用者释放
  3. 请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。

     4.循环等待,存在一个等待队列,即A占有B,B占有A

代码示例:

public class Demo extends Thread {
    int flag = 0;
    static Object obj1 = new Object();
    static Object obj2 = new Object();
    public static void main(String[] args) {
        Demo d1 = new Demo();
        Demo d2 = new Demo();
        d1.flag = 1;
        d2.flag = 2;

        d1.start();
        d2.start();

    }

    @Override
    public void run() {
        System.out.println("我被调用了!!!");
        if (flag ==1) {
            synchronized (obj1){
                try {
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (obj2){
                    System.out.println("我被锁住了1");
                }
            }
        }

        if (flag ==2) {
            synchronized (obj2){
                try {
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (obj1){
                    System.out.println("我被锁住了2");
                }
            }
        }
    }
}

运行结果:

 

4.案例

4.1 线程等待唤醒机制(通信)

在开始讲解等待唤醒机制之前,有必要搞清一个概念:

        线程之间的通信:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制。

等待唤醒机制所涉及到的方法:

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

        以上方法都定义在Object类中

 voidnotify()
          唤醒在此对象监视器上等待的单个线程。
 voidnotifyAll()
          唤醒在此对象监视器上等待的所有线程。
 voidwait()
          在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
 voidwait(long timeout)
          在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。
 voidwait(long timeout, int nanos)
          在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。

其实,所谓唤醒的意思就是让线程具备执行资格。必须注意的是,这些方法都是在同步中才有效。同时这些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程。

因为这些方法在使用时,必须要标明所属的锁,而锁又可以是任意对象。能被任意对象调用的方法一定定义在Object类中

 

代码:

import java.util.ArrayList;


public class BreadStore {
    ArrayList breads = new ArrayList<>();

    
    public synchronized void setBreads(String bread) {

        this.breads.add(bread);
        //唤醒所有等待的线程
        notifyAll(); // 或者notify()
    }

    
    public synchronized String getBreads() {
        //若包子普里没有包子了,请等待
        if(this.breads.size()<=0){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //取第一个包子
        String bread = breads.get(0);
        //移除第一个包子
        breads.remove(0);

        return bread;
    }


}
public class SetThread extends Thread {
    BreadStore bs;

    public SetThread(BreadStore bs) {
        this.bs = bs;
    }

    @Override
    public void run() {
        while (true) {
            try {
                //每0.5秒生产一个包子
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            bs.setBreads("a");
        }
    }
}
public class GetThread extends Thread{
    BreadStore bs;

    public GetThread(BreadStore bs) {
        this.bs = bs;
    }

    @Override
    public void run() {
        while(true){
            System.out.println("得到一个包子:"+bs.getBreads());
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        BreadStore breads = new BreadStore();
        SetThread set = new SetThread(breads);
        GetThread get = new GetThread(breads);
        set.start();
        get.start();
    }
}

运行结果:

 

4.2 定时器

Java中的定时器

  1. 可以在指定的时间,开始做某件事情
  2. 可以从指定的时间开始,并且间隔多长时间,会重复的做某件事情

实现定时器

  1. java.util.TimerTask(抽象类):定义执行的任务
    1. 自定义类,继承自TimerTask;
    2. 重写run()方法;(将要执行的任务定义在这里)
  1. java.util.Timer(类):计时,并启动任务

构造方法

        Timer();

Timer()
          创建一个新计时器。
成员方法
voidschedule(TimerTask task, Date time)
          安排在指定的时间执行指定的任务。
 voidschedule(TimerTask task, Date firstTime, long period)
          安排指定的任务在指定的时间开始进行重复的固定延迟执行
 voidschedule(TimerTask task, long delay)
          安排在指定延迟后执行指定的任务。
 voidschedule(TimerTask task, long delay, long period)
          安排指定的任务从指定的延迟后开始进行重复的固定延迟执行
  1. java.util.TimerTask(抽象类):定义执行的任务
    1. 自定义类,继承自TimerTask;
    2. 重写run()方法;(将要执行的任务定义在这里)

        

代码示例:

public class Demo {
    public static void main(String[] args) {
        //定时器:定时完成一些操作
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("任务被执行了!");
            }
        },1000*3,1000);//TimerTask任务三秒后开始,每个一秒执行一次
    }
}

运行结果:

 

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/863202.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号