java--进程和线程

进程和线程的概述

在学习线程之前要先知道什么是进程,进程就是正在运行的程序,它是系统资源调度的独立单位,并且一个进程可以执行多个任务,而线程就是程序执行的任务,它是程序使用CPU的基本单位,因此也可以说线程是依赖于进程的。

进程

进程就是正在运行的程序,它是系统资源调度的独立单位,各个进程之间不会相互影响,因为系统给它们分配了不同的空间和资源,它分为单进程和多进程。

单进程与多进程的概述

单进程的计算机一次只能做一件事情,而多进程的计算机可以做到一次做不同的事情,比如一边听音乐,一边听打游戏,这两件事情虽然感觉起来是在同时一起进行的,但其实是CPU在做着程序间的高效切换,这才让我们觉得是同时进行的。

线程

线程就是程序(进程)执行的任务,它分为单线程和多线程。

单线程与多线程的概述

单线程也就是做的事情专一,不会分神去做别的事,也就是程序只有一条执行路径;多线程就是可以分出多条路去做同一件事情,也就是程序有多条执行路径,比如三个伙伴迷路了,大家分别去问路人路线,最后大家在目的地集合,因此多线程的存在,不是提高程序的执行速度,其实是为了提高应用程序的使用率,也可以说程序的执行其实都是在抢CPU的资源,也就是抢CPU的执行权,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权,但这一过程是随机的,不知道哪一个线程会在哪一个时刻占到这个资源,所以线程的执行有随机性。

Thread类

想要实现多进程就要继承Thread类,重写run方法,然后调用start方法,这样就可以创建并执行新的线程,直接调用run方法可不是开始执行线程哟,因为run方法是由jvm从线程中调用的,因此不要以为直接调用run方法是在执行线程哟。

匿名内部类的方式实现线程

格式:
        new Thread(){代码…}.start();
                    或
        new Thread(new Runnable(){代码…}).start();

Runnable接口

Runnable接口也可以实现线程,这种方式扩展性强,可以实现一个接口,还可以再去继承其他类,当然这个也是要重写run方法的,实现该接口后与Thread类一起使用。

代码演示

某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票,通过实现Runnable接口实现(模拟网络延迟)。

public class Ticket implements Runnable{
    //设置票数
    int ticket = 100 ;
    @Override
    public void run() {
        while (true) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(1000);//模拟网络延迟
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在出售" + (ticket--) + "张票");
                }else {
                //当票不满足条件是退出循环
                    break;
                }
        }
    }
}
public class MyTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        //把Ticket对象放入Thread中可以起到对象共享,这样数据才会同步,如果变量是静态的可直接new Thread对象,因为静态变量是共享的
        Thread thread1 = new Thread(ticket);
        //设置线程名字
        thread1.setName("窗口1");

        Thread thread2 = new Thread(ticket);
        thread2.setName("窗口2");

        Thread thread3 = new Thread(ticket);
        thread3.setName("窗口3");
        //开启线程
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

结果

窗口1正在出售100张票
窗口3正在出售99张票
窗口2正在出售98张票
窗口2正在出售97张票
窗口1正在出售96张票
窗口3正在出售95张票
窗口3正在出售94张票
窗口1正在出售93张票
窗口2正在出售92张票
窗口1正在出售91张票
窗口3正在出售90张票
窗口2正在出售89张票
窗口1正在出售88张票
窗口3正在出售87张票
窗口2正在出售86张票
窗口2正在出售85张票
窗口1正在出售84张票
窗口3正在出售83张票
窗口1正在出售82张票
窗口3正在出售81张票
窗口2正在出售80张票
窗口2正在出售79张票
窗口3正在出售78张票
窗口1正在出售77张票
窗口2正在出售76张票
窗口3正在出售75张票
窗口1正在出售74张票
窗口2正在出售73张票
窗口3正在出售72张票
窗口1正在出售71张票
窗口3正在出售70张票
窗口1正在出售69张票
窗口2正在出售68张票
窗口1正在出售67张票
窗口3正在出售66张票
窗口2正在出售65张票
窗口1正在出售64张票
窗口3正在出售63张票
窗口2正在出售62张票
窗口1正在出售61张票
窗口3正在出售60张票
窗口2正在出售59张票
窗口1正在出售58张票
窗口3正在出售57张票
窗口2正在出售56张票
窗口1正在出售55张票
窗口3正在出售54张票
窗口2正在出售53张票
窗口3正在出售52张票
窗口1正在出售51张票
窗口2正在出售50张票
窗口1正在出售49张票
窗口3正在出售48张票
窗口2正在出售47张票
窗口3正在出售46张票
窗口1正在出售45张票
窗口2正在出售44张票
窗口1正在出售43张票
窗口3正在出售42张票
窗口2正在出售41张票
窗口3正在出售40张票
窗口1正在出售39张票
窗口2正在出售38张票
窗口3正在出售37张票
窗口1正在出售36张票
窗口2正在出售35张票
窗口2正在出售34张票
窗口1正在出售33张票
窗口3正在出售32张票
窗口2正在出售31张票
窗口2正在出售30张票
窗口3正在出售28张票
窗口1正在出售29张票
窗口2正在出售27张票
窗口3正在出售26张票
窗口1正在出售25张票
窗口2正在出售24张票
窗口2正在出售23张票
窗口3正在出售22张票
窗口1正在出售21张票
窗口2正在出售20张票
窗口3正在出售19张票
窗口1正在出售18张票
窗口2正在出售17张票
窗口3正在出售16张票
窗口1正在出售15张票
窗口2正在出售14张票
窗口2正在出售13张票
窗口3正在出售12张票
窗口1正在出售11张票
窗口2正在出售10张票
窗口3正在出售9张票
窗口1正在出售8张票
窗口2正在出售7张票
窗口1正在出售6张票
窗口3正在出售5张票
窗口1正在出售4张票
窗口3正在出售3张票
窗口2正在出售2张票
窗口2正在出售1张票
窗口3正在出售0张票
窗口1正在出售-1张票

你会发现这里会出现0和-1张票,这是为什么呢?(拿上述结果说明)因为在窗口2线程抢到执行权进来时,会休眠1秒,休眠的这段期间,另一个窗口抢到了执行权,也就是窗口3,这时候窗口3也进来了,然后它也休眠了,这个时候窗口2苏醒啦,它就完成了票减1,票就变为1了,在窗口3还没苏醒时窗口1进来并休眠了,然后窗口3苏醒了,它也进行了减1,票就变为了0,最后窗口1醒来并减1,票就变为-1了,因此出现了以上的结果,这就体现了线程的互斥性;

上面的结果也可能出现相同票数的窗口,原因是ticket--不具有原子性(原子性也就是式子不是一次计算出结果的,而是分布计算结果的,如:i--,它是先输出i的值,然后在进行i=i-1,同理i++)式子,如:当窗口1休眠时,窗口3进入之后也休眠,这时窗口1苏醒了,进行到输出i的值这一步时,窗口3苏醒了并抢到了执行权,它也进行了输出i,由于上一步还没进行到i-1的步骤,因此窗口3和窗口1输出的值相同,这就是原子性。这些都是在网络延迟中会出现的问题。

想要解决以上问题(也就是线程安全问题),只让一个线程进入,并阻止另一个线程进入,那么就使用到了synchronize(同步代码块)或者继承Lock(锁)。

线程安全问题

线程安全问题遵循这三个规律:是否是多线程;是否有共享数据;是否有多个线程操作共享数据。

synchronize同步代码块

同步代码块解决的出现解决了线程的安全问题,但是当线程相当多时,每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率;同步代码块把进入的线程锁住,这样就可以解决之前演示的问题了,这个把线程锁住的锁是内置锁(java中的内置锁是互斥锁),也就意味着只有一个线程能去获得这个锁,另一个线程想要获得这个锁,必须等上一个线程释放锁,因此如果这个线程不释放锁,那么另一个线程会一直等下去。

格式:
        synchronized(对象){
            需要同步的代码;
        }
这个对象其实就是一把锁,也可以称之为监视器,监视进程的进出,它是同步代码块保证数据的安全性的一个主要因素
要注意的是这个对象,要定义为静态成员变量,才能被所有线程共享,不能在里面new对象,这样每把锁都不一样了,这个锁也就没有意义了
 

代码演示

以上买票为例,Ticket修改为以下

public class Ticket implements Runnable{
    //设置票数
    int ticket = 100 ;
    //创建一个锁对象,你可以随便new,锁对象:就是一个任意对象
    Object obj=new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                //线程一旦进入同步代码块,就会持有锁的对象
                if (ticket > 0) {
                    try {
                        Thread.sleep(1000);//模拟网络延迟
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在出售" + (ticket--) + "张票");
                }else {
                    break;
                }
            }
        }
    }
}
public class MyTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread thread1 = new Thread(ticket);
        thread1.setName("窗口1");
        Thread thread2 = new Thread(ticket);
        thread2.setName("窗口2");
        Thread thread3 = new Thread(ticket);
        thread3.setName("窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

结果

窗口2正在出售100张票
窗口2正在出售99张票
窗口2正在出售98张票
窗口2正在出售97张票
窗口2正在出售96张票
窗口2正在出售95张票
窗口2正在出售94张票
窗口3正在出售93张票
窗口3正在出售92张票
窗口1正在出售91张票
窗口1正在出售90张票
窗口3正在出售89张票
窗口3正在出售88张票
窗口3正在出售87张票
窗口3正在出售86张票
窗口2正在出售85张票
窗口2正在出售84张票
窗口2正在出售83张票
窗口2正在出售82张票
窗口2正在出售81张票
窗口2正在出售80张票
窗口2正在出售79张票
窗口2正在出售78张票
窗口2正在出售77张票
窗口3正在出售76张票
窗口1正在出售75张票
窗口3正在出售74张票
窗口3正在出售73张票
窗口3正在出售72张票
窗口3正在出售71张票
窗口3正在出售70张票
窗口3正在出售69张票
窗口3正在出售68张票
窗口3正在出售67张票
窗口2正在出售66张票
窗口2正在出售65张票
窗口2正在出售64张票
窗口2正在出售63张票
窗口2正在出售62张票
窗口2正在出售61张票
窗口2正在出售60张票
窗口2正在出售59张票
窗口2正在出售58张票
窗口2正在出售57张票
窗口2正在出售56张票
窗口2正在出售55张票
窗口2正在出售54张票
窗口2正在出售53张票
窗口2正在出售52张票
窗口2正在出售51张票
窗口2正在出售50张票
窗口2正在出售49张票
窗口3正在出售48张票
窗口1正在出售47张票
窗口1正在出售46张票
窗口1正在出售45张票
窗口3正在出售44张票
窗口3正在出售43张票
窗口3正在出售42张票
窗口2正在出售41张票
窗口3正在出售40张票
窗口1正在出售39张票
窗口3正在出售38张票
窗口2正在出售37张票
窗口2正在出售36张票
窗口3正在出售35张票
窗口1正在出售34张票
窗口3正在出售33张票
窗口3正在出售32张票
窗口3正在出售31张票
窗口3正在出售30张票
窗口3正在出售29张票
窗口3正在出售28张票
窗口3正在出售27张票
窗口3正在出售26张票
窗口3正在出售25张票
窗口3正在出售24张票
窗口2正在出售23张票
窗口3正在出售22张票
窗口1正在出售21张票
窗口3正在出售20张票
窗口2正在出售19张票
窗口2正在出售18张票
窗口3正在出售17张票
窗口1正在出售16张票
窗口1正在出售15张票
窗口3正在出售14张票
窗口3正在出售13张票
窗口3正在出售12张票
窗口3正在出售11张票
窗口3正在出售10张票
窗口3正在出售9张票
窗口3正在出售8张票
窗口3正在出售7张票
窗口3正在出售6张票
窗口2正在出售5张票
窗口2正在出售4张票
窗口2正在出售3张票
窗口3正在出售2张票
窗口1正在出售1张票

Lock接口

用同步代码块我们可以给线程上锁,但是具体在哪里上锁和解锁,我们都不知道,因此为了让我们更加清楚如何加锁和解锁,在JDK5以后提供了Lock接口和它的ReentrantLock子类,这个锁与synchronized不同,需要手动去加锁和解锁。

Lock类
void lock()加锁
void unlock()解锁

代码演示

以上买票为例,把Ticket改为以下

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

public class Ticket implements Runnable{
    //设置票数
    int ticket = 100 ;
    Lock lock = new ReentrantLock();
    @Override
    public void run() {

        while (true) {
            //加锁,进入循环立马上锁,防止另一个线程跟着进入下面的if判断
            lock.lock();
            if (ticket > 0) {
                    try {
                        Thread.sleep(1000);//模拟网络延迟
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在出售" + (ticket--) + "张票");
                    //减完票后,这个线程的任务就该释放锁了,好让下一个线程执行任务
                    lock.unlock();
                }else {
                    break;
                }
           // }
        }
    }
}
public class MyTicket {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        Thread thread1 = new Thread(ticket);
        thread1.setName("窗口1");
        Thread thread2 = new Thread(ticket);
        thread2.setName("窗口2");
        Thread thread3 = new Thread(ticket);
        thread3.setName("窗口3");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

结果

窗口2正在出售100张票
窗口2正在出售99张票
窗口2正在出售98张票
窗口2正在出售97张票
窗口2正在出售96张票
窗口2正在出售95张票
窗口2正在出售94张票
窗口3正在出售93张票
窗口3正在出售92张票
窗口1正在出售91张票
窗口1正在出售90张票
窗口3正在出售89张票
窗口3正在出售88张票
窗口3正在出售87张票
窗口3正在出售86张票
窗口2正在出售85张票
窗口2正在出售84张票
窗口2正在出售83张票
窗口2正在出售82张票
窗口2正在出售81张票
窗口2正在出售80张票
窗口2正在出售79张票
窗口2正在出售78张票
窗口2正在出售77张票
窗口3正在出售76张票
窗口1正在出售75张票
窗口3正在出售74张票
窗口3正在出售73张票
窗口3正在出售72张票
窗口3正在出售71张票
窗口3正在出售70张票
窗口3正在出售69张票
窗口3正在出售68张票
窗口3正在出售67张票
窗口2正在出售66张票
窗口2正在出售65张票
窗口2正在出售64张票
窗口2正在出售63张票
窗口2正在出售62张票
窗口2正在出售61张票
窗口2正在出售60张票
窗口2正在出售59张票
窗口2正在出售58张票
窗口2正在出售57张票
窗口2正在出售56张票
窗口2正在出售55张票
窗口2正在出售54张票
窗口2正在出售53张票
窗口2正在出售52张票
窗口2正在出售51张票
窗口2正在出售50张票
窗口2正在出售49张票
窗口3正在出售48张票
窗口1正在出售47张票
窗口1正在出售46张票
窗口1正在出售45张票
窗口3正在出售44张票
窗口3正在出售43张票
窗口3正在出售42张票
窗口2正在出售41张票
窗口3正在出售40张票
窗口1正在出售39张票
窗口3正在出售38张票
窗口2正在出售37张票
窗口2正在出售36张票
窗口3正在出售35张票
窗口1正在出售34张票
窗口3正在出售33张票
窗口3正在出售32张票
窗口3正在出售31张票
窗口3正在出售30张票
窗口3正在出售29张票
窗口3正在出售28张票
窗口3正在出售27张票
窗口3正在出售26张票
窗口3正在出售25张票
窗口3正在出售24张票
窗口2正在出售23张票
窗口3正在出售22张票
窗口1正在出售21张票
窗口3正在出售20张票
窗口2正在出售19张票
窗口2正在出售18张票
窗口3正在出售17张票
窗口1正在出售16张票
窗口1正在出售15张票
窗口3正在出售14张票
窗口3正在出售13张票
窗口3正在出售12张票
窗口3正在出售11张票
窗口3正在出售10张票
窗口3正在出售9张票
窗口3正在出售8张票
窗口3正在出售7张票
窗口3正在出售6张票
窗口2正在出售5张票
窗口2正在出售4张票
窗口2正在出售3张票
窗口3正在出售2张票
窗口1正在出售1张票

死锁问题

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

代码演示

两个人只有一把钥匙串 ,如果一个人拿走钥匙取开门,另一个人就需要等待另一个人开门

//定义两把钥匙,也就是定义锁对象,把它们定义为静态的,可以直接类名调用
public interface MyLock {
	
	public static final Object key1 = new Object() ;
	public static final Object key2 = new Object() ;

}
//继承Thread,重写run方法
public class Demo extends Thread {
    //定义一个布尔类型来,判断是谁先拿锁,true是1先拿锁,反之是2
    boolean flag;

    public Demo(boolean flag){
        this.flag=flag;
    }
    @Override
    public void run() {
        if(flag){
            //嵌套同步代码块
            synchronized (MyLock.key1){
                System.out.println("门开了1");
                synchronized (MyLock.key2){
                    System.out.println("门开了2");
                }
            }
        }else {
            synchronized (MyLock.key2){
                System.out.println("门开了2");
                synchronized (MyLock.key1){
                    System.out.println("门开了1");
                }
            }
        }

    }
}
public class DemoTest {
    public static void main(String[] args) {
        Demo demo1 = new Demo(true);
        Demo demo2 = new Demo(false);
        demo1.start();
        demo2.start();
    }
}

结果

门开了2
门开了1

当是true时,由于1先拿了钥匙,还没释放,2就拿不到钥匙,因此就会出现一直等待的现象,这就是死锁。

内存可见性问题

Java内存模型规定了所有的变量都存储在主内存中,每条线程中还有自己的工作内存,工作内存中保存了被该线程所使用到的变量(这些变量是从主内存中拷贝而来), 线程对变量的所有操作(读取,赋值)都必须在工作内存中进行,不同线程之间也无法直接访问对方工作内存中的变量,它们间变量值的传递均需要通过主内存来完成。对于解决该问题,Java提供了volatile关键字来,该关键字保证了内存可见性。

代码演示

可见性问题

public class Volatile implements Runnable {
    //定义一个布尔类型,用修改布尔值看这一过程
    private boolean b=false;
    public boolean isB() {
        return b;
    }

    public void setB(boolean b) {
        this.b = b;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(50);//模拟网络延迟
        } catch (InterruptedException e) {

        }
        //把修改b的值
        b=true;
        //输出b的值
        System.out.println("b="+isB());
    }
public class VolatileTest {
    public static void main(String[] args) {
        Volatile v = new Volatile();
        Thread th = new Thread(v);
        th.start(); 
        while(true){
        //判断v是否修改成功,修改成功就输出进来啦
            if(v.isB()){
                System.out.println("进来啦");
                break;
            }
        }
    }
}

结果

b=true

上面输出的结果只有修改后的值,并没有输出进来啦这句话,是因为普通修改的值,不会马上就修改到主存中,并且各个线程之间的工作内存是不会互相干扰的,是个独立的存在,它们间变量值的传递均需要通过主内存来完成,因此想要想把修改的值立即写入主存的话就需要volatile这个轻量级的锁(比较省资源);另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中,因此可以保证可见性。

修改如下:

public class Volatile implements Runnable {
//volatile 关键字,可以使修改的值,立即写入主存中
    private volatile boolean b=false;
    public boolean isB() {
        return b;
    }

    public void setB(boolean b) {
        this.b = b;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {

        }
        b=true;
        System.out.println("b="+isB());
    }
}

结果

b=true
进来啦
//这个结果的顺序是随机的,因为线程具有随机性

 

线程池

程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互,而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池,因为线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在JDK5之前,我们必须手动实现自己的线程池,但从JDK5开始,Java内置支持线程池。

线程池产生的方法
public static ExecutorService newCachedThreadPool()根据任务的数量来创建线程
public static ExecutorService newFixedThreadPool(int nThreads)固定初始化几个线程
public static ExecutorService newSingleThreadExecutor()初始化一个线程的线程池

代码演示

1、

public class MyDemo implements Runnable{
    @Override
    public void run() {
        //通过查看线程名字,看一共有多少线程在执行任务
        System.out.println(Thread.currentThread().getName()+"正在执行程序");
    }
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class DemoTest {
    public static void main(String[] args) {
        //创建一个线程池,里面有两个线程,里面的2就带表线程数,可以随便写
        ExecutorService pool = Executors.newFixedThreadPool(2);
        //创建一个Runnable对象
        MyDemo myDemo = new MyDemo();
        //提交一个 Runnable 任务用于执行
        pool.submit(myDemo);
        pool.submit(myDemo);
        pool.submit(myDemo);
        pool.submit(myDemo);
        //关闭线程池
        pool.shutdown();
    }
}

结果

pool-1-thread-2正在执行程序
pool-1-thread-1正在执行程序
pool-1-thread-2正在执行程序
pool-1-thread-2正在执行程序

从结果你可以看到虽然你提交了四个Runnable对象,但是始终只有两个线程在执行任务,这就是线程池的好处,可以合理的利用线程。

2、累加操作

public class MyCallable implements Callable<Integer> {
    int len;
    //用来获取需要累加的数的长度
    public MyCallable(int i) {
        len=i;
    }

    @Override
    public Integer call() throws Exception {
        //做累加操作
        int sum=0;
        for (int i = 1; i <= len; i++) {
            sum+=i;
        }
        return sum;
    }
}
public class MyTest2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //获取一个线程对象的线程池
        //里面的核心线程数和线程数都是1,并且工作队列使用的是无界队列。由于是单线程工作,每次只能处理一个任务,所以后面所有的任务都被阻塞在工作队列中,只能一个个任务执行。
        ExecutorService service = Executors.newSingleThreadExecutor();
        Future<Integer> submit = service.submit(new MyCallable(10));
        Future<Integer> submit2 = service.submit(new MyCallable(100));
        Future<Integer> submit3 = service.submit(new MyCallable(1000));
        System.out.println(submit.get());
        System.out.println(submit2.get());
        System.out.println(submit3.get());
        service.shutdown();
    }
}

结果

55
5050
500500

 

定时器

定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行,在Java中,可以通过Timer和TimerTask类来实现定义调度的功能。

Timer类

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 void schedule(TimerTask task,  Date firstTime, long period)计划重复固定延迟执行指定的任务,在指定的开始时间

 

TimerTask类

TimerTask类
public abstract void run()定时器任务执行的动作
public boolean cancel()取消此定时器任务

代码演示

1、输出爆炸啦

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

//继承TimerTask,需要重新rum方法
public class Time extends TimerTask {
    @Override
    public void run() {
        System.out.println("爆炸啦");
    }
}
import java.util.Timer;

public class TimeTest {
    public static void main(String[] args) {
        //new一个Timer对象,用来调方法
        Timer timer = new Timer();
        //调用Timer对象的方法schedule,第一个参数必须是TimerTask对象,Time继承了它因此也是这个对象,第二个参数表示在2秒后运行run方法,这个参数只有在第一次使用run方法,最后一个参数是每隔1秒,运行一次run方法
        timer.schedule(new Time(),2000,1000);
    }
}

这个结果是,不停的输出爆炸啦,要想停掉的话,可以加入cancel()方法,在run方法最后加入这个话,结果会输出一个爆炸啦,因为执行第一次完后就执行到这一语句,定时器就取消了,如下。

2、

public class Time extends TimerTask {
    //定义一个Timer对象,到时候可以用它来调用cancel方法
    Timer time;
    //通过构造器给Timer对象赋值
    public Time(Timer time){
        this.time=time;
    }
    @Override
    public void run() {
        System.out.println("爆炸啦");
        //取消定时器
        time.cancel();
    }
}
public class TimeTest {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new Time(timer),2000,1000);
    }
}

结果

爆炸啦

 

  • 30
    点赞
  • 114
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值