多线程之前学过,很久没用忘得差不多了,重新学习一下,然后再看《Java并发编程的艺术》。
一、进程与线程
计算机同时运行多个程序,每个运行中的程序就是一个进程。每一个进程内部又有多个线程。
进程有如下三个特征: (1)独立性:它可以有自己独立的资源,有自己的私有地址空间; (2)动态性:进程是一个在系统中活动的指令集合,具有自己的生命周期和不同的状态; (3)并发性:多个进程可以在单个处理器上并发执行。并发性与并行性:
(1)并发指在同一时刻只有一条指令执行,处理器快速切换多个进程指令; (2)并行性指在同一时刻,有多条指令在多个处理器上同时执行。 现代操作系统都支持多进程的并发,常用方式有:共用式多任务操作策略和抢占式多任务操作策略。二、创建线程的方式
1、继承Thread类
1)继承Thread类,并重新写run()方法; 2)创建上述类的实例,即创建线程对象; 3)调用start()方法启动线程。public class FirstThread extends Thread { private int i=0; public void run() { for(;i<100;++i) { System.out.println(getName()+" "+i); } } public static void main(String[] args) { for(int i=0;i<100;++i) { System.out.println(Thread.currentThread().getName()+" "+i); if(i==20) { new FirstThread().start(); new FirstThread().start(); } } }}复制代码
Thread.currentThread()是Thread类的静态方法,返回当前正在执行的线程对象; getName()是Thread类的实例方法,返回调用该方法的线程的名字。
2、实现Runnable接口
1)定义Runnable接口的实现类,重写run()方法 2)创建Runnable实现类的实例,并作为Thread类的target来创建Thread对象,该Thread对象才是真正的线程对象。 可以在创建Thread类对象的时候为该对象取一个名字:new Thread(secondThread,"线程2"); public class SecondThread implements Runnable {private int i = 0; @Override public void run() { for (; i < 100; ++i) { System.out.println(Thread.currentThread().getName() + " " + i); } } public static void main(String[] args) { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i == 20) { SecondThread st1 = new SecondThread(); new Thread(st1,"new-1").start(); new Thread(st1,"new-2").start(); } } } }复制代码
Runnable接口是函数式接口,可以使用Lambda表达式创建Runnable对象。
上面代码中,多个线程共享了一个target。3、使用Callable和Future创建线程
Callable像是Runnable的增强版: call()方法可以有返回值; call()方法可以声明,抛出异常。 Java5提供了Future接口来代表Callable接口中call()方法的返回值。它有FutureTask实现类,该类实现了Future接口和Runnable接口,可以作为Thread类的Target。 该方法创建线程的步骤: 1)创建Callable接口的实现类,并实现call()方法,call方法为线程执行体,可以有返回值。再创建Callable的实例。 2)使用FutureTask类来包装Callable对象。 3)使用FutureTask对象作为Thread对象的target创建并启动线程。 4)可以使用FutureTask类的get()方法来获得子线程执行结束后的返回值。public class ThirdThread {public static void main(String[] args) { ThirdThread rt = new ThirdThread(); FutureTasktask = new FutureTask ((Callable )()-> { int i=0; for(;i<100;i++) { System.out.println(Thread.currentThread().getName()+"的循环变量i的值:"+i); } return i; }); for(int i=0;i<100;i++) { System.out.println(Thread.currentThread().getName()+"的循环变量i的值:"+i); if(i==20) { new Thread(task,"有返回值的线程").start(); } } try { System.out.println("子线程的返回值:"+task.get()); }catch(Exception e) { e.printStackTrace(); }}}复制代码
三、线程的生命周期
在线程的生命周期中,要经过:新建、就绪、运行、阻塞、死亡5种状态。
当使用new关键字创建了一个线程后,该线程就处于新建状态。(此时还没有表现出来线程的动态特性);当调用start()方法后,线程处于就绪状态,JVM会为其创建方法调用栈和程序计数器;当就绪状态的线程获得了CPU资源,开始执行run()方法,此时处于运行状态;当发生如下情况时,程序处于阻塞状态: 1线程调用sleep()方法主动放弃处理器资源; 2该线程视图获得一个同步监视器,但是该监视器被其他线程持有; 3线程在等待通知notify 4调用了线程的suspend()方法将线程挂起。 而当sleep了指定时间;获得了同步监视器;其他线程发出了通知;挂起的线程被调用了resume()的方法,时可以解除阻塞,让线程重写处于就绪状态。 当:1run或call方法执行完成;2线程抛出异常或error;3直接调用stop()方法结束该线程(容易死锁),该线程死亡。