概念

  • 进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。
  • 线程是进程中的一个执行单元(执行路径),负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
  • 多线程就是一个程序中有多个线程在同时执行,一个核心的CPU在多个线程之间进行着随即切换动作,由于切换时间很短(毫秒甚至是纳秒级别),导致我们感觉不出来
  • 单线程程序即,若有多个任务只能依次执行。当上一个任务执行结束后,下一个任务开始执行。如去网吧上网,网吧只能让一个人上网,当这个人下机后,下一个人才能上网。
  • 多线程程序即,若有多个任务可以同时执行。如,去网吧上网,网吧能够让多个人同时上网。

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

线程的运行模式

  • 分时调度

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

  • 抢占式调度

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

  • 大部分操作系统都支持多进程并发运行,现在的操作系统几乎都支持同时运行多个程序。比如:现在我们上课一边使用编辑器,一边使用录屏软件,同时还开着画图板,dos窗口等软件。此时,这些程序是在同时运行,”感觉这些软件好像在同一时刻运行着“。

  • 实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。

Thread类

介绍

Thread是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程,发现创建新执行线程有两种方法。 将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。创建对象,开启线程。run方法相当于其他线程的main方法。 声明一个实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。

  • 构造方法
Constructor and Description
Thread()
分配一个新的 Thread对象。
Thread(Runnable target)
分配一个新的 Thread对象。
Thread(Runnable target, String name)
分配一个新的 Thread对象。
Thread(String name)
分配一个新的 Thread对象。
Thread(ThreadGroup group, Runnable target)
分配一个新的 Thread对象。
Thread(ThreadGroup group, Runnable target, String name)
分配一个新的 Thread对象,使其具有 target作为其运行对象,具有指定的 name作为其名称,属于 group引用的线程组。
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
分配一个新的 Thread对象,以便它具有 target作为其运行对象,将指定的 name正如其名,以及属于该线程组由称作 group ,并具有指定的 堆栈大小
Thread(ThreadGroup group, String name)
分配一个新的 Thread对象。
  • 常规方法
Modifier and Type Method and Description
static int activeCount()
返回当前线程的thread group及其子组中活动线程数的估计。
void checkAccess()
确定当前正在运行的线程是否有权限修改此线程。
protected Object clone()
将CloneNotSupportedException作为线程抛出无法有意义地克隆。
int countStackFrames()
已弃用
此呼叫的定义取决于suspend() ,它已被弃用。 此外,此呼叫的结果从未明确。
static Thread currentThread()
返回对当前正在执行的线程对象的引用。
void destroy()
已弃用
这种方法最初是为了销毁这个线程而没有任何清理。 它所持有的任何监视器都将保持锁定。 但是,该方法从未实现。 如果要实施,那么它将是suspend()的方式是僵死的 。 如果目标线程在销毁时保护关键系统资源的锁,则无法再次访问该资源。 如果另一个线程曾尝试锁定此资源,将导致死锁。 这种僵局通常表现为“冻结”过程。 有关详细信息,请参阅Why are Thread.stop, Thread.suspend and Thread.resume Deprecated? 。
static void dumpStack()
将当前线程的堆栈跟踪打印到标准错误流。
static int enumerate(Thread[] tarray)
将当前线程的线程组及其子组中的每个活动线程复制到指定的数组中。
static Map getAllStackTraces()
返回所有活动线程的堆栈跟踪图。
ClassLoader getContextClassLoader()
返回此Thread的上下文ClassLoader。
static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler()
返回当线程由于未捕获异常突然终止而调用的默认处理程序。
long getId()
返回此线程的标识符。
String getName()
返回此线程的名称。
int getPriority()
返回此线程的优先级。
StackTraceElement[] getStackTrace()
返回表示此线程的堆栈转储的堆栈跟踪元素数组。
Thread.State getState()
返回此线程的状态。
ThreadGroup getThreadGroup()
返回此线程所属的线程组。
Thread.UncaughtExceptionHandler getUncaughtExceptionHandler()
返回由于未捕获的异常,此线程突然终止时调用的处理程序。
static boolean holdsLock(Object obj)
返回 true当且仅当当前线程在指定的对象上保持监视器锁。
void interrupt()
中断这个线程。
static boolean interrupted()
测试当前线程是否中断。
boolean isAlive()
测试这个线程是否活着。
boolean isDaemon()
测试这个线程是否是守护线程。
boolean isInterrupted()
测试这个线程是否被中断。
void join()
等待这个线程死亡。
void join(long millis)
等待这个线程死亡最多 millis毫秒。
void join(long millis, int nanos)
等待最多 millis毫秒加上 nanos纳秒这个线程死亡。
void resume()
已弃用
该方法仅用于与suspend()一起使用,因为它是死锁倾向的,因此已被弃用。 有关详细信息,请参阅Why are Thread.stop, Thread.suspend and Thread.resume Deprecated? 。
void run()
如果这个线程使用单独的Runnable运行对象构造,则调用该Runnable对象的run方法; 否则,此方法不执行任何操作并返回。
void setContextClassLoader(ClassLoader cl)
设置此线程的上下文ClassLoader。
void setDaemon(boolean on)
将此线程标记为 daemon线程或用户线程。
static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
设置当线程由于未捕获的异常突然终止而调用的默认处理程序,并且没有为该线程定义其他处理程序。
void setName(String name)
将此线程的名称更改为等于参数 name
void setPriority(int newPriority)
更改此线程的优先级。
void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
设置当该线程由于未捕获的异常而突然终止时调用的处理程序。
static void sleep(long millis)
使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
static void sleep(long millis, int nanos)
导致正在执行的线程以指定的毫秒数加上指定的纳秒数来暂停(临时停止执行),这取决于系统定时器和调度器的精度和准确性。
void start()
导致此线程开始执行; Java虚拟机调用此线程的run方法。
void stop()
已弃用
这种方法本质上是不安全的。 使用Thread.stop停止线程可以解锁所有已锁定的监视器(由于未ThreadDeath ThreadDeath异常在堆栈中ThreadDeath的自然结果)。 如果先前受这些监视器保护的任何对象处于不一致的状态,则损坏的对象将变得对其他线程可见,可能导致任意行为。 stop许多用途应该被替换为只是修改一些变量以指示目标线程应该停止运行的代码。 目标线程应该定期检查此变量,如果变量表示要停止运行,则以有序方式从其运行方法返回。 如果目标线程长时间等待(例如,在interrupt变量上),则应该使用interrupt方法来中断等待。 有关详细信息,请参阅Why are Thread.stop, Thread.suspend and Thread.resume Deprecated? 。
void stop(Throwable obj)
已弃用
该方法最初设计为强制线程停止并抛出一个给定的Throwable作为例外。 它本质上是不安全的(有关详细信息,请参阅stop() ),此外还可用于生成目标线程未准备处理的异常。 有关详细信息,请参阅Why are Thread.stop, Thread.suspend and Thread.resume Deprecated? 。
void suspend()
已弃用
这种方法已被弃用,因为它本身就是死锁的。 如果目标线程在挂起时保护关键系统资源的监视器上的锁定,则在目标线程恢复之前,线程不能访问该资源。 如果要恢复目标线程的线程在调用resume之前尝试锁定此监视器, resume导致死锁。 这种僵局通常表现为“冻结”过程。 有关详细信息,请参阅Why are Thread.stop, Thread.suspend and Thread.resume Deprecated? 。
String toString()
返回此线程的字符串表示,包括线程的名称,优先级和线程组。
static void yield()
对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器。

为什么要继承Thread

  • 我们为什么要继承Thread类,并调用其的start方法才能开启线程呢
/*
 * 因为Thread类用来描述线程,具备线程应该有功能。
 * 直接实例化类并调用start()没有错,但是该start调用的是Thread类中的run方法
 * 而这个run方法没有做什么事情,更重要的是这个run方法中并没有定义我们需要让线程执行的代码。
 */

Thread t1 = new Thread();
t1.start();
  • 创建线程的目的是什么

  • 是为了建立程序单独的执行路径,让多部分代码实现同时执行。也就是说线程创建并执行需要给定线程要执行的任务。对于之前所讲的主线程,它的任务定义在main函数中。自定义线程需要执行的任务都定义在run方法中。

  • 多线程执行时,到底在内存中是如何运行

  • 多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间。进行方法的压栈和弹栈。当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进程就结束了。

实现线程程序继承Thread

/*
 *  定义子类,继承Thread 
 *  重写方法run 
 */

public class Threadtest extends Thread{
  public void run(){
    for(int i = 0; i < 50;i++){
      System.out.println("run..."+i);
    }
  }
}
  • main的主线程中,实例化类调用start()
/*
 * 创建和启动一个线程
 * 创建Thread子类对象
 * 子类对象调用方法start()
 * 让线程程序执行,JVM调用线程中的run
 */

public static void main(String[] args) {
  Thread t = new Threadtest();
  t.start();
    for(int i = 0; i < 50;i++){
    System.out.println("main..."+i);
  }
}

getName()方法Thread类

public class Threadtest extends Thread{
    public void run(){
        System.out.println(getName());
    }
}
  • main的主线程中,实例化类调用start()
public static void main(String[] args) {
  Thread t = new Threadtest();
  t.start();
}

currentThread()方法Thread类

public static void main(String[] args) {
    System.out.println(Thread.currentThread().getName());
}

setName()方法Thread类

public class Threadtest extends Thread{
    public void run(){
        System.out.println(getName());
    }
}
  • main的主线程中,实例化类调用start()
public static void main(String[] args) {
    Thread t = new Threadtest();
    t.setName("enderman");
    t.start();
}

sleep()方法Thread类

//睡眠500ms,500ms已到并且cpu切换到该线程继续向下执行

public static void main(String[] args) throws InterruptedException {
    Thread.sleep(500);
    System.out.println("enderman");
}
  • 重写run()方法
public class Threadtest extends Thread{
  public void run(){
    try {
      Thread.sleep(500);
    } catch (InterruptedException e) {

    }
    System.out.println("enderman");
  }
}
  • main的主线程中,实例化类调用start()
public static void main(String[] args) {
    Thread t = new Threadtest();
    t.start();
}

Runnable接口

  • 实现接口方式的好处
  • 实现Runnable接口避免了单继承的局限性,所以较为常用。
  • 实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。
  • 继承Thread类,线程对象和线程任务耦合在一起。
  • 一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。
  • 实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。(降低紧密性或者依赖性,创建线程和执行任务不绑定)
/*
 *  定义子类,继承Thread 
 *  重写方法run 
 */

public class Threadtest implements Runnable{
  public void run(){
    for(int i = 0; i < 50;i++){
      System.out.println("run..."+i);
    }
  }
}
  • main的主线程中,实例化类调用start()
/*
 * 创建和启动一个线程
 * 创建Thread子类对象
 * 子类对象调用方法start()
 * 让线程程序执行,JVM调用线程中的run
 */

public static void main(String[] args) {
  Threadtest tr = new Threadtest();
  Thread t = new Thread(tr);
    t.start();
    for(int i = 0 ; i < 50; i++){
      System.out.println("main..."+i);
    }
}

匿名内部类实现线程程序

/*
 *  使用匿名内部类,实现多线程程序
 *  前提: 继承或者接口实现
 *  new 父类或者接口(){
 *     重写抽象方法
 *  }
 */

public static void main(String[] args) {
  //继承方式  XXX extends Thread{ public void run(){}}
  new Thread(){
    public void run(){
      System.out.println("!!!");
    }
  }.start();

  //实现接口方式  XXX implements Runnable{ public void run(){}}
  Runnable r = new Runnable(){
    public void run(){
      System.out.println("###");
    }
  };
  new Thread(r).start();

  new Thread(new Runnable(){
    public void run(){
      System.out.println("@@@");
    }
  }).start();
}

线程池

Modifier and Type Method and Description
static Callable callable(PrivilegedAction action)
返回一个Callable对象,当被调用时,它运行给定的特权动作并返回其结果。
static Callable callable(PrivilegedExceptionAction action)
返回一个Callable对象,该对象在被调用时运行给定的特权异常操作并返回其结果。
static Callable callable(Runnable task)
返回一个Callable对象,当被调用时,它运行给定的任务并返回null
static  Callable callable(Runnable task, T result)
返回一个Callable对象,当被调用时,它运行给定的任务并返回给定的结果。
static ThreadFactory defaultThreadFactory()
返回用于创建新线程的默认线程工厂。
static ExecutorService newCachedThreadPool()
创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程。
static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程,并在需要时使用提供的ThreadFactory创建新线程。
static ExecutorService newFixedThreadPool(int nThreads)
创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。
static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
创建一个线程池,重用固定数量的线程,从共享无界队列中运行,使用提供的ThreadFactory在需要时创建新线程。
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执行。
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执行。
static ExecutorService newSingleThreadExecutor()
创建一个使用从无界队列运行的单个工作线程的执行程序。
static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
创建一个使用单个工作线程运行无界队列的执行程序,并在需要时使用提供的ThreadFactory创建一个新线程。
static ScheduledExecutorService newSingleThreadScheduledExecutor()
创建一个单线程执行器,可以调度命令在给定的延迟之后运行,或定期执行。
static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
创建一个单线程执行器,可以调度命令在给定的延迟之后运行,或定期执行。
static ExecutorService newWorkStealingPool()
创建使用所有 available processors作为其目标并行级别的工作窃取线程池。
static ExecutorService newWorkStealingPool(int parallelism)
创建一个维护足够的线程以支持给定的并行级别的线程池,并且可以使用多个队列来减少争用。
static  Callable privilegedCallable(Callable callable)
返回一个Callable对象,当被调用时,将在当前访问控制上下文中执行给定的callable
static  Callable privilegedCallableUsingCurrentClassLoader(Callable callable)
返回一个Callable对象,当被调用时,将在当前访问控制上下文中执行给定的callable ,当前上下文类加载器作为上下文类加载器。
static ThreadFactory privilegedThreadFactory()
返回一个用于创建与当前线程具有相同权限的新线程的线程工厂。
static ExecutorService unconfigurableExecutorService(ExecutorService executor)
返回一个将所有定义的ExecutorService方法委托给给定执行程序的对象,但不能以其他方式使用转换方式访问。
static ScheduledExecutorService unconfigurableScheduledExecutorService(ScheduledExecutorService executor)
返回一个将所有定义的ScheduledExecutorService方法委托给给定执行程序的对象,但不能以其他方式使用转换方式访问。

线程池的原理

在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。 如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。

JDK5实现线程池

public class Threadtest extends Thread{
    public void run(){
        System.out.println(Thread.currentThread().getName()+" 线程提交任务");
    }
}
  • 实例化类并放入线程池
/*
 * JDK1.5新特性,实现线程池程序
 * 使用工厂类 Executors中的静态方法创建线程对象,指定线程的个数
 * static ExecutorService newFixedThreadPool(int 个数) 返回线程池对象
 * 返回的是ExecutorService接口的实现类 (线程池对象)
 * 调用方法submit (Ruunable r) 提交线程执行任务       
 */

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

public static void main(String[] args) {
    //调用工厂类的静态方法,创建线程池对象
    //返回线程池对象,是返回的接口
    ExecutorService es = Executors.newFixedThreadPool(2);
    //调用接口实现类对象es中的方法submit提交线程任务
    //将Runnable接口实现类对象,传递
    es.submit(new Threadtest());
    es.submit(new Threadtest());
}

实现线程的Callable接口方式

/*
 * Callable 接口的实现类,作为线程提交任务出现
 * 使用方法返回值
 */

import java.util.concurrent.Callable;

public class Threadtest implements Callable<String>{
    public String call(){
        return "enderman";
    }
}
  • 实例化类并放入线程池
/*
* 实现线程程序的第三个方式,实现Callable接口方式
* 工厂类 Executors静态方法newFixedThreadPool方法,创建线程池对象
* 线程池对象ExecutorService接口实现类,调用方法submit提交线程任务
* submit(Callable c)
*/

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

public static void main(String[] args) throws Exception{
    ExecutorService es = Executors.newFixedThreadPool(2);
    //提交线程任务的方法submit方法返回 Future接口的实现类
    Future<String> f = es.submit(new Threadtest());
    String s = f.get();
    System.out.println(s);
}

通过构造方法传入参数

import java.util.concurrent.Callable;

public class Threadtest implements Callable<Integer>{
    private int a;
    public Threadtest(int a){
        this.a=a;
    }
    public Integer call(){
        int sum = 0 ;
        for(int i = 1 ; i <=a ; i++){
          sum = sum + i ;
        }
        return sum;
    }
}
  • 实例化类并放入线程池
/*
 * 使用多线程技术,求和
 * 两个线程,1个线程计算1+100,另一个线程计算1+200的和
 * 多线程的异步计算
 */

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

public static void main(String[] args) throws Exception {
    ExecutorService es = Executors.newFixedThreadPool(2);
    Future<Integer> f1 =es.submit(new Threadtest (100));
    Future<Integer> f2 =es.submit(new Threadtest (200));
    System.out.println(f1.get());
    System.out.println(f2.get());
    es.shutdown();
}