java笔记 11.11

Java笔记


对象序列化
反序列化
多线程
继承thread类
实现runnable接口
线程休眠和线程优先级
线程等待
线程中断
守护线程
线程并发问题
sync锁简介
lock锁
线程池
 


对象序列化
Java允许我们在内存中创建可复用的Java对象,但一般情况下,这些对象的生命周期不会比JVM的生命周期更长。但在现实应用中,可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象
Java对象序列化就能够帮助我们实现该功能。使用Java对象序列化,在保存对象时,会把其状态保存为一组字节,在未来再将这些字节组装成对象
必须注意地是,对象序列化保存的是对象的"状态",即它的成员变量。由此可知,对象序列化不会关注类中的静态变量
除了在持久化对象时会用到对象序列化之外,当使用 RMI ,或在网络中传递对象时,都会用到对象序列化
Java序列化API为处理对象序列化提供了一个标准机制,该API简单易用,但性能不是最好的
实例 demo
在Java中,只要一个类实现了 java.io.Serializable 接口,它就可以被序列化(枚举类可以被序列化)。
// Gender类,表示性别
// 每个枚举类型都会默认继承类java.lang.Enum,而Enum类实现了Serializable接口,所以枚举类型对象都是默认可以被序列化的。
public enum Gender {  
    MALE, FEMALE  

// Person 类实现了 Serializable 接口,它包含三个字段。另外,它还重写了该类的 toString() 方法,以方便打印 Person 实例中的内容。
public class Person implements Serializable {  
    private String name = null;  
    private Integer age = null;  
    private Gender gender = null;  

    public Person() {  
        System.out.println("none-arg constructor");  
    }  
 
    public Person(String name, Integer age, Gender gender) {  
        System.out.println("arg constructor");  
        this.name = name;  
        this.age = age;  
        this.gender = gender;  
    }  
 
    // 省略 set get 方法

    @Override 
    public String toString() {  
        return "[" + name + ", " + age + ", " + gender + "]";  
    }  

 
// SimpleSerial类,是一个简单的序列化程序,它先将Person对象保存到文件person.out中,然后再从该文件中读出被存储的Person对象,并打印该对象。
public class SimpleSerial {  
    public static void main(String[] args) throws Exception {  
        File file = new File("person.out");  
        ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file)); // 注意这里使用的是 ObjectOutputStream 对象输出流封装其他的输出流
        Person person = new Person("John", 101, Gender.MALE);  
        oout.writeObject(person);  
        oout.close();  
 
        ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));  // 使用对象输入流读取序列化的对象
        Object newPerson = oin.readObject(); // 没有强制转换到Person类型  
        oin.close();  
        System.out.println(newPerson);  
    }  

 
// 上述程序的输出的结果为:
arg constructor  
[John, 31, MALE] 

•    序列化:把Java对象转换为字节序列的过程。
•    反序列化:把字节序列恢复为Java对象的过程。
对象的序列化主要有两种用途:
•    把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;(持久化对象)
•    在网络上传送对象的字节序列。(网络传输对象)
2 使用
在Java中,如果一个对象要想实现序列化,必须要实现下面两个接口之一:
•    Serializable 接口
•    Externalizable 接口
那这两个接口是如何工作的呢?两者又有什么关系呢?我们分别进行介绍。
2.1 Serializable 接口
一个对象想要被序列化,那么它的类就要实现此接口或者它的子接口。
这个对象的所有属性(包括private属性、包括其引用的对象)都可以被序列化和反序列化来保存、传递。不想序列化的字段可以使用transient修饰。
由于Serializable对象完全以它存储的二进制位为基础来构造,因此并不会调用任何构造函数,因此Serializable类无需默认构造函数,但是当Serializable类的父类没有实现Serializable接口时,反序列化过程会调用父类的默认构造函数,因此该父类必需有默认构造函数,否则会抛异常。
使用transient关键字阻止序列化虽然简单方便,但被它修饰的属性被完全隔离在序列化机制之外,导致了在反序列化时无法获取该属性的值,而通过在需要序列化的对象的Java类里加入writeObject()方法与readObject()方法可以控制如何序列化各属性,甚至完全不序列化某些属性或者加密序列化某些属性。
2.2 Externalizable 接口
它是Serializable接口的子类,用户要实现的writeExternal()和readExternal() 方法,用来决定如何序列化和反序列化。
因为序列化和反序列化方法需要自己实现,因此可以指定序列化哪些属性,而transient在这里无效。
对Externalizable对象反序列化时,会先调用类的无参构造方法,这是有别于默认反序列方式的。如果把类的不带参数的构造方法删除,或者把该构造方法的访问权限设置为private、默认或protected级别,会抛出java.io.InvalidException: no valid constructor异常,因此Externalizable对象必须有默认构造函数,而且必需是public的。

Java 应用程序中的多线程允许多个线程在单个进程中同时运行。线程是独立执行的任务,可以共享数据和其他资源,例如文件和网络连接。在本文中,我们将探讨什么是Java多线程以及它的优点和缺点。
  线程是可由操作系统独立调度的轻量级进程。它也被定义为程序中允许同时执行代码的独立执行路径,这意味着多个线程可以同时执行。
  每个线程都有自己的堆栈,这意味着它可以有局部变量并跟踪自己的执行。线程可用于实现并行性,即同时执行多个任务。
  在Java(和其他编程语言)中,线程共享相同的地址空间,因此可以访问应用程序或程序的所有变量、对象、方法和全局范围。
  什么是Java中的多线程?
  多线程是操作系统在同一时间点在内存中拥有大量线程的能力,让人误以为所有这些线程都在并发执行。操作系统为每个“线程”分配一个特定的时间片,每当它们的时间片到期时,就在它们之间切换。线程之间的切换过程称为关联转换。
  上下文切换包括:
  l 存储线程的状态。
  l 清空CPU寄存器。
  l 将CPU控制权传递给队列中的下一个线程。
  应当注意,在任何时间点,处理器可以执行一个且仅一个线程。通过同时启动多个任务,当有许多任务等待完成时(如从磁盘读取),与运行单线程应用程序相比,你可以获得更好的吞吐量。
  Java中多线程的优势:
  l 提高响应能力
  l 更快的执行
  l 更好的CPU和内存利用率
  l 支持并发
  多线程的缺点:
  l 测试和调试中的挑战
  l 增加了代码库的复杂性
 Java中的多线程
  Java 编程语言具有对使用多线程的内置支持。当你运行 Java 应用程序时,Java 虚拟机 (JVM) 会创建一个称为主线程的线程。主线程负责运行应用程序的 main() 方法。然后主线程可以创建其他线程,这些线程可以与主线程并发运行。
  线程的并发执行可以通过利用多个 CPU 或处理器来帮助提高应用程序的性能。它还可以通过允许在用户与图形用户界面 (GUI) 交互时在后台执行任务来帮助提高响应能力。
  Java中的线程状态是什么?
  当 Java 程序启动时,只有一个线程——主线程。该线程负责执行程序的 main() 方法。一旦 main() 方法退出,程序就会终止。但是,Java 程序可以有多个线程同时运行。
  线程可以处于以下几种状态之一:
  l 就绪或可运行——这是线程在就绪或可运行队列中等待分配处理器的状态。当你在线程对象上调用 start 方法时,线程进入此状态。当运行时隐式调用 yield 方法时,线程将控制权交给就绪或可运行队列中的下一个线程。
  l Running – 这是处理器正在执行线程时的状态。调度程序负责在适当的时间将线程调度到运行状态——通常是在轮到它并且当前运行的线程完成执行之后。
  l Waiting/Suspended/Blocked – 当你调用线程对象的挂起方法时,线程进入挂起状态。在调用 resume 方法后,可以将挂起的线程移回运行状态。线程处于等待状态时等待I/O。
  当线程完成执行或终止时,它会停止。


在java中线程来使用有两种方法。
继承Thread类,重写run方法
实现Runnable接口,重写run方法
/创建线程的方式一:继承Thread类,重写run()方法,调用start开启线程
//线程开启不一定立即执行,由cpu决定
public class TestThread1 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("我在看代码!!!!!!!!!!!!!!!!!!");
        }
    }

    public static void main(String[] args) {
        TestThread1 testThread1=new TestThread1();
        testThread1.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("我在学习多线程---");
        }
    }
}
实现多线程同步下载图片#
代码演示#
public class TestThread2 extends Thread{
    private String url;
    private String name;

    public TestThread2(String url,String name){
        this.url=url;
        this.name=name;
    }

    @Override
    public void run() {
        WebDownloader webDownloader=new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载了文件名为:"+name);
    }

    public static void main(String[] args) {
        TestThread2 testThread201=new TestThread2("http://8.136.39.89:9090/asserts/img/portfolio-1.jpg","portfolio-1.jpg");
        TestThread2 testThread202=new TestThread2("http://8.136.39.89:9090/asserts/img/portfolio-2.jpg","portfolio-2.jpg");
        TestThread2 testThread203=new TestThread2("http://8.136.39.89:9090/asserts/img/portfolio-3.jpg","portfolio-3.jpg");
        testThread201.start();
        testThread202.start();
        testThread203.start();
    }
}

class WebDownloader{
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downder出现问题");
        }
    }
 1.定义实现Runnable接口
 2.覆盖Runnable接口中的run方法,将线程要运行的代码存放在run方法中。
3.通过Thread类建立线程对象。
4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
  为什么要讲Runnable接口的子类对象传递给Thread的构造方法。因为自定义的方法的所属的对象是Runnable接口的子类对象。
5.调用Thread类的start方法开启线程并调用Runnable接口子类run方法。
(二)线程安全的共享代码块问题
目的:程序是否存在安全问题,如果有,如何解决?
如何找问题:
1.明确哪些代码是多线程运行代码。
2.明确共享数据
3.明确多线程运行代码中哪些语句是操作共享数据的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38    class Bank{
  
  private int sum;
  public void add(int n){
    
     sum+=n;
     System.out.println("sum="+sum);
    
  }
  
}
 class Cus implements Runnable{
  
  private Bank b=new Bank();
  public void run(){
   synchronized(b){  
     for(int x=0;x<3;x++)
     {
      b.add(100);
       
     }
   }
  }
  
}
public class BankDemo{
  public static void main(String []args){
    Cus c=new Cus();
    Thread t1=new Thread(c);
    Thread t2=new Thread(c);
    t1.start();
    t2.start();
    
    
  }
  
  
}

线程休眠
相关API
方法名    说明
static void sleep(long millis)    使当前正在执行的线程停留(暂停执行)指定的毫秒数
代码示例
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
    }
}

class MyThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("睡觉前");
        System.out.println("悄咪咪要来了");
        Thread.sleep(3000);
        System.out.println("悄咪咪来了他还在睡觉");
        System.out.println("睡醒了");

        MyRunnable mr = new MyRunnable();

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

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

线程等待

进入Thread.State.WAITING
Java 提供了多种将线程置于 WAITING 状态的方法。
•    Object.wait()
我们可以将线程置于 WAITING 状态的最标准方法之一是通过 wait() 方法。 当一个线程拥有一个对象的监听器时,我们可以暂停它的执行,直到另一个线程完成工作并使用 notify() 方法将其唤醒。
package com.toutiao.treadwaiting;
public class TreadWaitingApplication {
private static Object lock = new Object();
public static void main(String[] args) {
//Thread1
new Thread(() -> {
System.out.println(Thread.currentThread());
synchronized (lock) {
System.out.println("线程1开始执行");
try {
lock.wait();
System.out.println("线程1执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "thread1").start();
}
}
执行main方法,thread1线程处于等待状态

线程中断
中断(Interrupt)一个线程意味着在该线程完成任务之前停止其正在进行的一切,有效地中止其当前的操作。
使用stop方法虽然可以强行终止正在运行或挂起的线程,但使用stop方法是很危险的,就象突然关闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,因此,并不推荐使用stop方法来终止线程。
 
1、任务中一般都会有循环结构,只要用一个标记控制住循环,就可以结束任务。
2、如果线程处于了冻结状态,无法读取标记,此时可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备CPU的执行资格。
(一):使用退出标志
  当run方法执行完后,线程就会退出。但有时run方法是永远不会结束的,如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。
  在这种情况下,一般是将这些任务放在一个循环中,如while循环。如果想使while循环在某一特定条件下退出,最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while循环是否退出。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27    public class test1 {
 
    public static volatile boolean exit =false;  //退出标志
     
    public static void main(String[] args) {
        new Thread() {
            public void run() {
                System.out.println("线程启动了");
                while (!exit) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程结束了");
            }
        }.start();
         
        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        exit = true;//5秒后更改退出标志的值,没有这段代码,线程就一直不能停止
    }
}
(二):使用 interrupt 方法
  Thread.interrupt()方法: 作用是中断线程。将会设置该线程的中断状态位,即设置为true,中断的结果线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序本身。线程会不时地检测这个中断标示位,以判断线程是否应该被中断(中断标示值是否为true)。它并不像stop方法那样会中断一个正在运行的线程 
  interrupt()方法只是改变中断状态,不会中断一个正在运行的线程。需要用户自己去监视线程的状态为并做处理。支持线程中断的方法(也就是线程中断后会抛出interruptedException的方法)就是在监视线程的中断状态,一旦线程的中断状态被置为“中断状态”,就会抛出中断异常。这一方法实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程检查到中断标识,就得以退出阻塞的状态。
更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,此时调用该线程的interrupt()方法,那么该线程将抛出一个 InterruptedException中断异常(该线程必须事先预备好处理此异常),从而提早地终结被阻塞状态。如果线程没有被阻塞,这时调用 interrupt()将不起作用,直到执行到wait(),sleep(),join()时,才马上会抛出 InterruptedException。

守护线程
Java守护线程
Java中,通过Thread类,我们可以创建2种线程,分为守护线程和用户线程。
守护线程是所有非守护线程的保姆,当所有非守护线程执行完成或退出了,即使还有守护线程在运行,JVM也会直接退出,因此守护线程通常是用来处理一些辅助工作。
反之,对于非守护线程,只要有一个在运行,JVM就不会退出。
典型的守护线程如垃圾回收GC线程,当用户线程都结束后,GC也就没有单独存在的必要,JVM直接退出。
 
我们可以通过Thread对象的setDaemon(boolean on)方法设置是否为守护线程,要在start之前设置:
Thread thread = new Thread(runnable);
thread.setDaemon(true); // true表示守护线程,false表示用户线程
thread.start();
 
需要注意的是,如果没有显示调用setDaemon方法进行设置,线程的模式是取决于父线程是否为守护线程,也就是创建此线程所在的线程。
如果父线程是守护线程,创建的线程默认是守护线程;
如果父线程是用户线程,创建的线程默认是用户线程。
 
这可以从Thread类的init方法源代码中看出:
Thread parent = currentThread();
this.daemon = parent.isDaemon();
 
对于daemon的设置,保存在了Thread对象的成员变量中,Thread提供了setter/getter:
private boolean daemon = false;        //    是否为守护线程

public final void setDaemon(boolean on) {
    //    SecurityManager安全检查,本文不展开讨论
    checkAccess();
    //    检查线程是否已启动,已启动无法设置daemon
    if (isAlive()) {
        throw new IllegalThreadStateException();
    }
    daemon = on;
}

public final boolean isDaemon() {
    return daemon;
}


线程并发问题
并发问题(安全性问题)
核心
要编写线程安全的代码,核心在于对状态访问操作进行管理。特别是对共享的和可变的状态的访问。
解决思路
当发生安全性问题时。有三种解决问题的角度:
•    不在线程之间共享变量
•    将状态变量修改为不可变
•    访问状态变量时使用同步机制。
前两种方式从根本上避免了多线程并发问题的原因:对共享和可变状态的访问。
1. 不在线程之间共享变量
即限制变量只能在单个线程中访问。
实现方式:
1.    线程封闭
保证变量只能被一个线程可以访问到。可以通过Executors.newSingleThreadExecutor()实现。
2.    栈封闭
栈封闭即使用局部变量。局部变量只会存在于本地方法栈中,不能被其他线程访问,因此也就不会出现并发问题。所以如果可以使用局部变量就优先使用局部变量。
3.    ThreadLocal封闭
ThreadLocal是Java提供的实现线程封闭的一种方式,ThreadLocal内部维护了一个Map,Map的key是各个线程,而Map的值就是要封闭的对象。每个线程中的对象都对应着Map中一个值,也就是ThreadLocal利用Map实现了对象的线程封闭。
2. 将状态变量修改为不可变
即使用不可变对象。
不可变对象:当一个对象构造完成后,其状态就不再变化,我们称这样的对象为不可变对象(Immutable Object),这些对象关联的类为不可变类(Immutable Class)。
比如Java中的String、Integer、Double、Long等所有原生类型的包装器类型,都是不可变的。
大多数时候,线程间是通过使用共享资源实现通信的。如果该共享资源诞生之后就完全不再变更(犹如一个常量),多线程间共同并发读取该共享资源是不会产生线程冲突的,因为所有线程无论何时读取该共享资源,总是能获取到一致的、完整的资源状态,这样也能规避多线程冲突。不可变对象就是这样一种诞生之后就完全不再变更的对象,该类对象天生支持在多线程间共享。
3. 使用同步机制
关注一个并发问题,有3个基本的关注点:
•    原子性,即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
•    可见性,当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
•    有序性,有序性指的是数据不相关的变量在并发的情况下,实际执行的结果和单线程的执行结果是一样的,不会因为重排序的问题导致结果不可预知。
所有的并发问题的都可以从这三个点进行分析并针对性的进行解决。


Sync 锁


synchronized 通过当前线程持有对象锁,从而拥有访问权限,而其他没有持有当前对象锁的线程无法拥有访问权限,保证在同一时刻,只有一个线程可以执行某个方法或者某个代码块,从而保证线程安全。synchronized 可以保证线程的可见性,synchronized 属于隐式锁,锁的持有与释放都是隐式的,我们无需干预。synchronized最主要的三种应用方式:
1.    修饰实例方法:作用于当前实例加锁,进入同步代码前要获得当前实例的锁
2.    修饰静态方法:作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
3.    修饰代码块:指定加锁对象,进入同步代码库前要获得给定对象的锁
2、synchronized 底层语义原理:
synchronized 锁机制在 Java 虚拟机中的同步是基于进入和退出监视器锁对象 monitor 实现的(无论是显示同步还是隐式同步都是如此),每个对象的对象头都关联着一个 monitor 对象,当一个 monitor 被某个线程持有后,它便处于锁定状态。在 HotSpot 虚拟机中,monitor 是由 ObjectMonitor 实现的,每个等待锁的线程都会被封装成 ObjectWaiter 对象,ObjectMonitor 中有两个集合,WaitSet 和 _EntryList,用来保存 ObjectWaiter 对象列表 ,owner 区域指向持有 ObjectMonitor 对象的线程。当多个线程同时访问一段同步代码时,首先会进入 _EntryList 集合尝试获取 moniter,当线程获取到对象的 monitor 后进入 _Owner 区域并把 _owner 变量设置为当前线程,同时 monitor 中的计数器 count 加1;若线程调用 wait() 方法,将释放当前持有的 monitor,count自减1,owner 变量恢复为 null,同时该线程进入 _WaitSet 集合中等待被唤醒。若当前线程执行完毕也将释放 monitor 并复位变量的值,以便其他线程获取 monitor。

Lock锁
Lock与synchronized
继同步代码块和同步方法之后,Lock作为解决线程安全的第三种方式,JDK5.0新增,与synchronized对比如下:
1.Lock是显示锁(手动开启和关闭锁,别忘了关闭锁),synchronized是隐式锁,出了作用域自动释放。
2.Lock只有代码块锁,synchronized有代码块锁和方法锁。
3.使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多子类)。
优先使用顺序
Lock---->同步代码块(已经进入了方法体,分配了相应资源)---->同步方法(在方法体之外)。
案例
多窗口买票(Lock锁)
Java
 1 import java.util.concurrent.locks.ReentrantLock;
 2 
 3 class Windows implements Runnable{
 4 
 5     private int ticket = 100;
 6 
 7     //true:公平锁,先进先出;不写默认为false,不公平
 8     //1.实例化ReentrantLock
 9     private ReentrantLock lock = new ReentrantLock(true);
10 
11     @Override
12     public void run() {
13         while(true){
14             try{
15                 //2.调用lock()
16                 lock.lock();
17 
18                 if(ticket > 0){
19                     try {
20                         Thread.sleep(100);
21                     } catch (InterruptedException e) {
22                         e.printStackTrace();
23                     }
24                     System.out.println(Thread.currentThread().getName() + ": 售票 :票号为: "+ ticket);
25                     ticket--;
26                 }else {
27                     break;
28                 }
29             }finally{
30                 //3.调用解锁的方法unlock()
31                 lock.unlock();
32             }
33         }
34     }
35 }
36 public class LockTest {
37     public static void main(String[] args) {
38         Windows w = new Windows();
39 
40         Thread t1 = new Thread(w);
41         Thread t2 = new Thread(w);
42         Thread t3 = new Thread(w);
43 
44         t1.setName("窗口1");
45         t2.setName("窗口2");
46         t3.setName("窗口3");
47 
48         t1.start();
49         t2.start();
50         t3.start();
51     }
52 }


线程池

 线程我们可以使用 new 的方式去创建,但如果并发的线程很多,每个线程执行的时间又不长,这样频繁的创建线程会大大的降低系统处理的效率,因为创建和销毁进程都需要消耗资源,线程池就是用来解决类似问题。
  线程池实现了一个线程在执行完一段任务后,不销毁,继续执行下一段任务。用《Java并发编程艺术》提到线程池的优点:
  1、降低资源的消耗:使得线程可以重复使用,不需要在创建线程和销毁线程上浪费资源
  2、提高响应速度:任务到达时,线程可以不需要创建即可以执行
  3、线程的可管理性:线程是稀缺资源,如果无限制的创建会严重影响系统效率,线程池可以对线程进行管理、监控、调优。
Excutor框架
  Excutor框架是线程池处理线程的核心,包括创建任务,传递任务,任务的执行三个方面
1、创建任务
  执行的任务需要实现 Runnable 或者 Callable 接口,然后重写里面的 run 方法,这两个接口区别下面会写
2、传递任务
  以前执行线程都是直接创建线程然后调用 start() 方法去执行线程,现在我们需要把任务传递到线程池里面去,传递任务的核心接口就是 Excutor 接口

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值