Java基础篇--多线程的创建与使用

6 篇文章 0 订阅

目录

 

基本概念

线程的创建和使用

线程的生命周期

线程的同步

线程的通信

JDK5.0新增线程的创建方式


 

基本概念

进程是操作系统中进行保护和资源分配的基本单位,操作系统分配资源以进程为基本单位。而线程是进程的组成部分,它代表了   一条顺序的执行流。

   线程(Thread) 进程可以进一步细化为线程,是一个程序内部的一条执行路径。 

   若一个进程同一时间并行的执行多个线程,就是支持多线程的,线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程的切换开销小

  一个进程中的多个线程共享相同的内存单元/内存地址空间->他们从同一堆中分配对象,可是访问相同的变量和对象,这就使得线程建通信更简便、高效,但是多个线程操作共享的资源可能会带来安全隐患

可以共享的内容可以参照JVM内存模型 如下图中的 虚拟机栈和程序计数器就是每一个线程独有的

方法区和堆是一个进程一份,进程中的所有线程共享方法区和堆中的内容

1 Class Loader 类加载器

2 Execution Engine 执行引擎负责解释命令,提交操作系统执行

3  Native Interface 本地接口

4.Runtime Data Area 运行数据区

 

线程的创建和使用

   1、通过继承Thread类的方式创建线程

package test;

/**
 * 多线程的方式一:继承于Thread类
 * 1、创建一个继承于Thread类
 * 2、重写run方法 --> 将此线程执行的操作声明在run()中
 * 3、创建Thread类的子类对象
 * 4、通过此对象调用 start方法
 *
 * @author zjt
 */

// 1、创建一个继承于Thread类
class MyThread extends Thread {
    @Override
    // 2、重写run方法
    public void run() {
        for (int i = 0; i < 10000; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
            }
        }
    }
}

public class ThreadTest {

    public static void main(String[] args) {
        // 3、创建Thread类的子类对象 主线程帮我们构造对象
        MyThread t1 = new MyThread();
        // 4、通过此对象调用 start方法 t1独立去执行
        // t1.start()两个作用 1、启动当前线程 2、调用当前线程的run() 方法
        t1.start();
        // 下面的操作仍然在主线程中执行
        for (int i = 0; i < 1000; i++) {
            System.out.println("hello");
        }
    }

}

如果在main方法中不调用start() 方法直接调用run方法是没有启动新的线程去执行的

package test;

/**
 *
 * @author zjt
 */

// 1、创建一个继承于Thread类
class MyThread extends Thread {
    @Override
    // 2、重写run方法
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                // 获取当前线程的名称
                System.out.println(Thread.currentThread().getName() + ":" + i);
            }
        }
    }
}

public class ThreadTest {

    public static void main(String[] args) {
        // 3、创建Thread类的子类对象 主线程帮我们构造对象
        MyThread t1 = new MyThread();
        // 4、通过此对象调用 start方法 t1独立去执行 
        // t1.start();
        // 调用run方法
        t1.run();
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + "hello");
        }
    }

}

idea 也会有有提示

若不需要重复调用仅仅使用一次,也可以使用匿名内部类调用Thread

public static void main(String[] args) {
        new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                if (i % 2 != 0) {
                    // 获取当前线程的名称
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                }
            }
        }).start();

        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + "hello");
        }
    }

Thread中常用的方法简单介绍

    1、start() 启动当前线程,调用当前线程的run()方法

    2、run() 通常情况下需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中

    3、currentThread() 返回执行当前代码的线程

    4、getName() 获取当前线程的名字

    5、setName() 设置当前线程的名字

    6、yield() 释放当前CPU的执行权,不代表不再抢占CPU,所以可能还是会抢到资源继续执行

    7、join() 在线程A的执行过程中调用线程B 的jion() ,此时线程A进入阻塞状态,直到线程B完全执行完以后,线程A才结束阻塞状态继续执行

    8、stop() 已过时,执行此方法强制结束当前线程

    9、sleep() 让当前线程进入“睡眠”状态,在指定的milltime毫秒时间内,当前线程是阻塞状态 

    10、isAlive() 判断当前线程是否存活

线程的调度

  调度策略

    时间片

    抢占式:高优先级的线程抢占CPU

  Java的调度方法

      同优先级的线程组成先进先出队列,使用时间片策略(先到先得)

     对于高优先级,使用优先调度的抢占式策略

  线程的优先级

     MAX_PRIORITY: 10 最大优先级

     MIN_PRIORITY :1 最小优先级

     NORM_PRIORITY: 5 默认优先级

    涉及到的方法

// 返回线程的优先级值
int priority = thread.getPriority();

// 设置线程的优先级
thread.setPriority(5);



// 注意设置值 需要在 MIN_PRIORITY 到 MAX_PRIORITY 否则会抛出错误 设值的源码如下
 public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

     线程创建时,继承父线程的优先级

      低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用

2、方式二 实现 Runnable 接口创建线程

Java线程的分类

Java的线程分为两类,一类是守护线程,一类是用户线程

他们在每个方面几乎都是相同的,唯一的区别是判断何时离开

守护线程是用来服务用户线程的,通过start() 方法前调用,thread.setDaemon(true) 可以把一个用户线程变成一个守护线程

Java垃圾回收就是一个典型的守护线程

若JVM中都是守护线程,当前JVM会退出

 

线程的生命周期

   JDK中使用Thread.State类定义了线程的几种状态

要想实现多线程必须在主线程中创建新的线程对象,Java语言使用Thread类及其子类对象来表示线程,在它的一个完整的生命周期中,通常要经历如下的五种状态

新建:当一个Thread类或其子类的对象被声明并创建时,新生的线程处于新建状态

就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已经具备了运行的条件,只是还没有分配到                    CPU资源

运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能

阻塞:在某种特殊的情况下,被人为挂起获取执行输入输出操作时,让出CPU并临时中止自己的执行,进入阻塞状态

死亡:线程完成了它的全部工作,或者线程被提前强制的中止,或者出现异常导致结束

 

线程的同步

class MyRunnable implements Runnable {

    // 共享数据
    private int ticket = 100;

    // 2、实现的类去重写Runnable接口中抽象方法
    @Override
    public void run() {
        while (true) {
            if (ticket > 0) {
                try {
                    // 强制线程 停止 10 微秒
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "卖票:票号为:" + ticket);
                ticket--;
            } else {
                break;
            }
        }
    }
}

  安全问题如上代码的卖票会有重复或者其他异常

  问题出现的原因:当某个线程操作 ticket 的过程中,尚未操作完成时,其他线程进入方法 操作 ticket-- 导致

  解决方式思路:当一个线程A在操作ticket的时候,其他线程不能参与进来,直到当前线程A操作完成后,其他线程才可以开始操作ticket ,即使线程A出现阻塞,其他线程也需要等待

  在Java中通过同步机制来解决线程安全问题

  方式一 :同步代码块

synchronized(同步监视器){
    // 操作共享数据的代码,即为需要被同步的代码
    // 共享数据,多个线程共同操作的变量,比如上述代码中的 ticket
    // 同步监视器 :也就是锁 任何一个类的对象都可以成为锁 要求 多个线程必须共用同一把锁
}

 

class MyRunnable implements Runnable {

    private int ticket = 100;
    // 共用此对象 但是还是比较麻烦 可以使用当前对象this 
    private final Object obj = new Object();


    @Override
    public void run() {
        while (true) { //注意不要把它包含进去,包含进去后 只有一个线程操作 ticket
            // synchronized (this) { // 可以考虑唯一的当前对象 注意此方法在 继承thread类实现的时候不可用
            //synchronized (MyRunnable.class) { // 当前类的对象也是唯一的 继承thrad类实现多线程 可以使用 当前类作为同步监视器
            synchronized (obj) {
                if (ticket > 0) {
                    try {
                        // 强制线程 停止 10 微妙
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "卖票:票号为:" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}

  同步的方式,解决了线程安全的问题

  操作同步代码时,只能有一个线程参与,其他线程等待,相当于一个单线程的过程,效率低

 方式二:同步方法

       如果操作共享数据的代码完整的声明在一个方法中,可以将此方法声明同步的

       

package test;

/**
 * 使用同步方法 解决实现runnable接口的线程安全问题
 *
 * @author zjt
 */

class MyRunnableT implements Runnable {

    private int ticket = 100;

    private boolean flag = true;

    @Override
    public synchronized void run() {
        while (flag) {
            this.sale();
        }
    }


    private synchronized void sale() { // 同步方法当中 同步监视器 是this
        if (ticket > 0) {
            try {
                // 强制线程 停止 10 微妙
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "卖票:票号为:" + ticket);
            ticket--;
        } else {
            this.flag = false;
        }
    }
}

public class ThreadTest3 {
    public static void main(String[] args) {
        // 3、创建实现类的对象
        MyRunnableT myRunnable = new MyRunnableT();
        // 4、将此对象作为参数传递到Thread类的构造器中,创建Thead类的对象
        Thread thread = new Thread(myRunnable);
        // 5、通过Thread类的对象去调用start() 方法
        Thread thread1 = new Thread(myRunnable);
        Thread thread2 = new Thread(myRunnable);

        thread.setName("窗口1");
        thread1.setName("窗口2");
        thread2.setName("窗口3");

        thread.start();
        thread1.start();
        thread2.start();

    }
}

 

package test;

/**
 * 使用同步方法处理继承 Thread类的方式 中的线程安全问题
 *
 * @author zjt
 */


class MyThread1 extends Thread {

    private static int ticket = 100;

    private static boolean flag = true;

    private static synchronized void sale() { // 同步监视器 MyThread1.class
        //private synchronized void sale() { // 同步监视器 为m1,m2,m3 所以这种解觉方式是有误的 需要加上 static
        if (ticket > 0) {
            try {
                // 强制线程 停止 10 微妙
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "卖票:票号为:" + ticket);
            ticket--;
        } else {
            flag = false;
        }
    }

    @Overrid
    public void run() {

        while (flag) {
            sale();
        }
    }
}

public class ThreadTest4 {

    public static void main(String[] args) {
        MyThread1 m1 = new MyThread1();
        MyThread1 m2 = new MyThread1();
        MyThread1 m3 = new MyThread1();

        m1.setName("窗口1");
        m2.setName("窗口2");
        m3.setName("窗口3");

        m1.start();
        m2.start();
        m3.start();

    }


}

关于同步方法的总结

1、同步方法仍然涉及到同步监视器,只是不需要我们显示的声明

2、非静态的同步方法 同步监视器是this 静态方法的同步监视器是 当前类本身  

方式3使用Lock锁

package test1;

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

/**
 * 解决线程安全问题方式三 lock锁
 *
 * @author zjt
 * @date 2020-08-24
 */
class Account {

    // 实例化 ReentrantLock
    private final static ReentrantLock lock = new ReentrantLock(true);

    private double balance;

    public Account(double balance) {
        this.balance = balance;
    }

    public void deposit(double amt) {
        try {
            // 调用 锁定lock 方法
            lock.lock();
            if (amt > 0) {
                balance += amt;
                TimeUnit.MILLISECONDS.sleep(10);
                System.out.println(Thread.currentThread().getName() + ":存钱成功,余额为:" + balance);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 手动调用解锁方法 否则线程获取锁一直阻塞
            lock.unlock();
        }
    }
}

class Customer extends Thread {

    private final Account account;

    public Customer(Account account) {
        this.account = account;
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {

            account.deposit(1000);
        }
    }
}


public class TestAccount {

    public static void main(String[] args) {

        Account account = new Account(0);

        Customer c1 = new Customer(account);
        Customer c2 = new Customer(account);
        c1.setName("甲");
        c2.setName("乙");
        c1.start();
        c2.start();
    }

}

 

线程的死锁问题

        不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了死锁。

        出现死锁后,不会出现异常,不会出现提示,只是所有的进程都处于阻塞状态,无法继续。

        解决思路,专门的算法、原则,尽量减少同步资源的定义,尽量避免嵌套同步

package test1;

/**
 * 演示死锁的线程问题
 *
 * @author zjt
 */
public class ThreadTest {

    public static void main(String[] args) {

        StringBuffer sb1 = new StringBuffer();
        StringBuffer sb2 = new StringBuffer();

        new Thread(() -> {
            synchronized (sb1) {

                sb1.append("a");
                sb2.append("1");

                try {
                    // 此时 该线程 持有 sb1 想获取 sb2 资源 导致死锁
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (sb2) {
                    sb1.append("b");
                    sb2.append("2");
                }
            }
            System.out.println(sb1);
            System.out.println(sb2);
        }).start();

        new Thread(() -> {
            synchronized (sb2) {
                sb1.append("c");
                sb2.append("3");
                try {
                    // 此时 该线程 持有 sb2 想获取 sb1 资源 导致死锁
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (sb1) {
                    sb1.append("d");
                    sb2.append("4");
                }
            }
            System.out.println(sb1);
            System.out.println(sb2);
        }).start();

    }
}

 

线程的通信

     多个线程的操作共同的共享数据,可以看作它们之间有某种“交流”,可以看作是线程间的通信

package test3;

/**
 * @author zjt
 * 线程的通信
 * 涉及到的3个方法
 * wait()  一旦执行此方法 ,当前线程就会进入阻塞状态,并释放同步监视器
 * notify() 一旦执行此方法 ,就会唤醒被 wait() 的一个线程 如果有多个线程被wait 就会唤醒优先级高的那个线程
 * notifyAll() 一旦执行此方法 ,就会唤醒被 wait() 的所有线程
 * 说明
 * 1、使用前提,wait() notify() notifyAll() 必须使用在同步代码块,或者同步方法中
 * 2、wait() notify()  notifyAll() 调用没有加对象,默认是使用 this 调用 调用者必须是同步方法或者同步代码块中同步监视器 否则会抛出 IllegalMonitorStateException 异常
 * 3、wait() notify()  notifyAll() 是定义在 Object 当中 因为 同步监视器 是任何一个唯一的对象都可以调用 所以 放在了
 *
 * sleep() 和 wait() 方法的异同
 * 相同点 一旦执行方法 都可以是的当前线程进去阻塞状态
 * 不同点 1、两个方法声明的位置不同,Thread 类中声明 sleep() Object() 类中声明 wait()
 *       2、调用的要求不同,sleep 的调用可以在任何需要的场景下调用   wait() 必须使用在同步代码块,或者同步方法中
 *       3、关于是否释放同步监视器的问题 ,如果两个方法都使用在 同步代码块或者同步方法中,sleep() 不会释放同步监视器,wait() 会释放 同步监视器
 */

class Number implements Runnable {

    private int num = 1;

    private Object obj = new Object();

    @Override
    public void run() {
        while (true) {
            //synchronized (this) {
            synchronized (obj) {
                obj.notify();
                //this.notify();
                if (num <= 100) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + num);
                    num++;
                    try {
                        // 使得调用 wait(); 方法的线程进入阻塞状态  wait() 会释放锁
                        //this.wait();
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    break;
                }
            }
        }
    }
}

public class CommunicationTest {


    public static void main(String[] args) {
        Number number = new Number();

        Thread t1 = new Thread(number);
        Thread t2 = new Thread(number);

        t1.setName("甲");
        t2.setName("乙");

        t1.start();
        t2.start();
    }


}

 

JDK5.0新增线程的创建方式

    新增方式一 :实现Callable接口

           与Runnable相比 Callable功能更强大一些 1、与run() 方法相比 可以有返回值 2、方法可以抛出异常 3、支持泛型返回值4、需要借助于FutureTask类,比如获取返回值

package test3;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 创建方式三 实现Callable 接口创建线程
 *
 * 1、相比run()方法,可以有返回值
 * 2、方法可以抛出异常
 * 3、支持泛型的返回值
 *
 * @author zjt
 */

// 1、创建一个实现Callable<V> 的实现类
class NumThread implements Callable<Integer> {


    @Override
    //2、 实现 call() 方法,将此线程需要执行的操作声明在call() 方法当中
    public Integer call() {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {
                sum += i;
            }
        }
        return sum;
    }
}

public class CallThread {

    public static void main(String[] args) {
        // 3、创建Callable<V> 接口的实现类的对象
        NumThread t1 = new NumThread();
        // 4、将此Callable<V> 接口的实现类的对象 作为参数 传递到FutureTask<V> 中,创建FutureTask<V> 的对象
        FutureTask<Integer> futureTask = new FutureTask<>(t1);
        // 将FutureTask<V> 的对象作为参数传递到 Thread 类的构造器中,创建 Thread对象 并且调用 start 方法
        new Thread(futureTask).start();
        try {
            // 6、获取Callable<V> 方法中 call 方法的返回值
            // .get 方法的返回值 即为 FutureTask 构造器参数的Callable 实现类重写的call 方法的返回值
            Integer sum = futureTask.get();
            System.out.println(sum);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

}

新增方式二 :使用线程池

  背景:经常创建和销毁、使用量特别大资源,比如并发情况下的线程,对性能影响很大

  思路:提前创建好多个线程,放入池中,使用时直接获取,使用完成后放回池中,可以避免频繁的销毁、实现重复利用。

  好处:1、提高响应速度(减少了创建新线程的时间) 2、降低资源消耗(重复利用线程池中的线程,不需要每次创建)3、便于线程管理比如可以设置 线程池核心池的大小(corePoolSize)、最大线程数(maximumPoolSize)、线程没有任务时最多保持多长时间后会终止(keepAliveTime)等等。

 

package test3;

import java.util.concurrent.*;

/**
 * 创建线程的方式 四
 *
 * @author zjt
 */
public class ThreadPool {


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 提供指定数量的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //强制转换成子实现类 设置属性
        //ThreadPoolExecutor executorService1 = (ThreadPoolExecutor) executorService;
        // 设置 线程池属性 找接口实现类
        // System.out.println(executorService.getClass());
        //executorService1.setCorePoolSize(1);
       // executorService1.setKeepAliveTime(10, TimeUnit.SECONDS);

        // executorService.submit // 适合使用于 Callable
        //executorService.execute(); // 适合使用于 Runnable
        // 执行指定的线程的操作,需要提供 Callable 或者 Runnable 接口类实现类的对象
        FutureTask<Integer> submit = (FutureTask<Integer>) executorService.submit(() -> {
            int sum = 0;
            for (int i = 1; i <= 100; i++) {
                if (i % 2 == 0) {
                    sum += i;
                    Thread.sleep(10);
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                }
            }
            return sum;
        });

        executorService.execute(() -> {
            for (int i = 1; i <= 100; i++) {
                if (i % 2 == 0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                }
            }
        });

        Integer q = submit.get();
        System.out.println(q);

        // 关闭线程连接池
        executorService.shutdown();
    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
package com.ljl.org.test4; /** *@DEMO:Interview *@Author:jilongliang *@Date:2013-4-17 * * 分别使用Runnable接口和Thread类编程实 编写一应用程序创建两个线程一个线程打印输出1—1000之间所有的奇数(Odd Number) * 另外一个线程打印输出1-1000之间所有的偶数(Even Number)要求两个线程随机休眠一 段时间后 继续打印输出下一个数 * * 创建线程有两种方式: 1.实现Runnable接口 2.继承Thread类 * 实现方式和继承方式有啥区别? * 实现方式的好处:避免了单继承的局限性 在定义线程时. * 建议使用实现方式 * 区别: * 继承Thread:线程代码存放Thread子类run方法 实现 * Runnable:线程代码存放接口的子类的run方法 * wait释放资源,释放锁 * sleep释放资源,不释放锁 */ @SuppressWarnings("all") public class Thread1 { public static void main(String[] args) { //方法一 /* OddNumber js = new OddNumber(); js.start(); EvenNumber os = new EvenNumber(); os.start(); while (true) { if (js.i1 == 1000 || os.i2 == 1000) { System.exit(-1); } } */ //方法二 OddNum on=new OddNum(); EvenNum en=new EvenNum(); new Thread(on).start(); new Thread(en).start(); while (true) { if (on.i1 == 1000 || en.i2 == 1000) { System.exit(-1); } } } } /** * ============================继承Thread的线程=============================== */ class EvenNumber extends Thread { int i2; @Override public void run() { for (i2 = 1; i2 <= 1000; i2++) { if (i2 % 2 == 0) { System.out.println("偶數" + i2); } try { sleep((int) (Math.random() * 500) + 500); } catch (Exception e) { } } } } class OddNumber extends Thread { int i1; @Override public void run() { for (i1 = 1; i1 <= 1000; i1++) { if (i1 % 2 != 0) { System.out.println("奇數" + i1); } try { sleep((int) (Math.random() * 500) + 500); } catch (Exception e) { } } } } /** * ============================实现Runnable的线程=============================== */ @SuppressWarnings("all") class OddNum implements Runnable { int i1; @Override public void run() { for (i1 = 1; i1 <= 1000; i1++) { if (i1 % 2 != 0) { System.out.println("奇數" + i1); } try { new Thread().sleep((int) (Math.random() * 500)+500); } catch (Exception e) { } } } } @SuppressWarnings("all") class EvenNum implements Runnable { int i2; @Override public void run() { for (i2 = 1; i2 <= 1000; i2++) { if (i2 % 2 == 0) { System.out.println("偶數" + i2); } try { /**在指定的毫秒数内让当前正在执行的线程休眠 * Math.random()一个小于1的随机数乘于500+500,随眠时间不会超过1000毫秒 */ //new Thread().sleep((int) (Math.random() * 500)+500); new Thread().sleep(1000);//也可以指定特定的参数毫秒 } catch (Exception e) { } } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值