进程:进程是系统中正在运行的一个程序,程序一旦运行就是进程。
进程可以看成程序执行的一个实例。进程是系统资源分配的独立实体,每个进程都拥有独立的地址空间。一个进程无法访问另一个进程的变量和数据结构,如果想让一个进程访问另一个进程的资源,需要使用进程间通信。进程是系统进行资源分配和调度的一个独立单位。
线程:线程可以理解为进程中独立运行的子任务。
通过继承Thread类:
继承Thread方法,重写Thread的run()方法
public class MyThread extends Thread{ @Override public void run() { doSomething(); } private void doSomething() { System.out.println("我是一个线程中的方法"); } } public class NewThread { public static void main(String[] args) { MyThread myThread=new MyThread(); myThread.start();//开启一个线程方法 } } 复制代码
Thread类中的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象run方法。如果调用myThread.run()就不是异步执行,而是同步,那么此线程对象并不交给“线程规划器”来进行处理,而是由main主线程来调用run()方法。
实现Runable接口
public class RunnableThread implements Runnable{ @Override public void run() { doSomeThing(); } private void doSomeThing() { System.out.println("我是一个线程方法"); } } 复制代码
实现Runable接口类中没有start()方法,需要用Thread构造个方法开启线程。
public class NewThread { public static void main(String[] args) { Runnable runnable=new RunnableThread(); Thread thread=new Thread(runnable); thread.start();//开启一个线程方法 } } 复制代码
实现Callable接口和Future创建线程
首先创建Callable接口的实现类CallableThread,实现call()方法,并且有返回值。Callable接口是一个带泛型的接口,泛型的类型就是线程返回值的类型。实现Callable接口中的call()方法,方法的返回类型与泛型的类型相同。
public class CallableThread implements Callable<String>{ @Override public String call() throws Exception { doSomeThing(); return "需要返回的值"; } private void doSomeThing() { System.out.println("我是线程中的方法"); } } 复制代码
Callable不能直接获取返回值,需要用FutureTask在外部封装一下再获取返回值。
public class NewThread { public static void main(String[] args) { Callable<String> callable=new CallableThread(); FutureTask<String> futureTask=new FutureTask<String>(callable); Thread thread=new Thread(futureTask); thread.start();//开启一个线程方法 //以下的方法可与上边的线程并发执行 doSomething(); try { futureTask.get();//获取线程返回值 } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } private static void doSomething() { } } 复制代码
currentThread()方法可返回代码段正在被那个线程调用的信息。
public class MyRun extends Thread { public MyRun(){ System.out.println("构造 thread :" + Thread.currentThread().getName()); System.out.println("构造 thread :" + this.getName()); } @Override public void run() { System.out.println("Thread.currentThread().getName(): " + Thread.currentThread().getName()); System.out.println("this.getname:"+ this.getName()); } } 复制代码
public class ThreadTest { public static void main(String[] args) throws InterruptedException { MyRun myRun = new MyRun(); myRun.setName("aaaa"); myRun.start(); } } 复制代码
运行结果
构造 thread :main 构造 thread :Thread-0 Thread.currentThread().getName(): aaaa this.getnam:eaaaa 复制代码
isAlive()方法用于判断当前线程是否处于活动状态。活动状态时线程已经启动尚未终止。线程处于正在运行或者准备开始运行的状态就认为线程是存活的。
sleep()方法是在指定的毫秒数内让当前“正在执行的线程”休眠(暂时执行)。这个正在执行的线程是指this.currentThread()返回的线程。
getId()方法可以取得线程的唯一标识。
Thread.java类中提供了两种方法来判断线程是否停止。
yield()方法的作用是放弃当前的CPU资源,让给其他的任务去占用CPU执行的时间。但是放弃的时间不确定,有可能刚放弃,马上又获取了CPU的时间片。
停止线程意味着在线程处理完任务之前停掉正在做的操作,也就是放弃当前操作。
有三种停止线程的方式:
停止一个线程可以使用Thread.stop()方法,但最好不要使用,虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且已经被废弃。
大多数停止线程的操作使用 Thread.interrupt()
方法,但这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。
使用退出标志退出线程:
当run方法执行完后,线程就会退出。但有时run方法是永远不会结束的。如在服务端程序中使用线程进行监听客户端请求,或是其他的需要循环处理的任务。在这种情况下,一般是将这些任务放在一个循环中,如while循环。如果想让循环永远运行下去,可以使用while(true){……}来处理。但要想使while循环在某一特定条件下退出,最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while循环是否退出。
package chapter2; public class ThreadFlag extends Thread { public volatile boolean exit = false; public void run(){ while (!exit){ //do something }; } public static void main(String[] args) throws Exception{ ThreadFlag thread = new ThreadFlag(); thread.start(); sleep(5000); // 主线程延迟5秒 thread.exit = true; // 终止线程thread thread.join(); System.out.println("线程退出!"); } } 复制代码
使用stop方法终止线程:
使用stop方法可以强行终止正在运行或挂起的线程。我们可以使用如下的代码来终止线程:
thread.stop(); 复制代码
虽然使用上面的代码可以终止线程,但使用stop方法是很危险的,就象突然关闭计算机电源,而不是按正常程序关机一样,可能会产生不可预料的结果,因此,并不推荐使用stop方法来终止线程。
使用interrupt方法终止线程:
使用interrupt方法来终端线程可分为两种情况:
public class ThreadSafe extends Thread { public void run() { while (true){ try{ Thread.sleep(5*1000);//阻塞5妙 }catch(InterruptedException e){ e.printStackTrace(); break;//捕获到异常之后,执行break跳出循环。 } } } 复制代码
public class ThreadSafe extends Thread { public void run() { while (!isInterrupted()){ //do something, but no throw InterruptedException } } } 复制代码
为什么要区分进入阻塞状态和和非阻塞状态两种情况了,是因为当阻塞状态时,如果有interrupt()发生,系统除了会抛出InterruptedException异常外,还会调用interrupted()函数,调用时能获取到中断状态是true的状态,调用完之后会复位中断状态为false,所以异常抛出之后通过isInterrupted()是获取不到中断状态是true的状态,从而不能退出循环,因此在线程未进入阻塞的代码段时是可以通过isInterrupted()来判断中断是否发生来控制循环,在进入阻塞状态后要通过捕获异常来退出循环。因此使用interrupt()来退出线程的最好的方式应该是两种情况都要考虑。
暂停线程意味着此线程还可以恢复运行,可以使用suspend()方法暂停线程,使用resume()方法恢复线程。
public class MyRun extends Thread { private long i = 0; public long getI(){ return i; } public void setI(long i){ this.i = i; } @Override public void run() { while (true){ i++; } } } 复制代码
public class ThreadTest { public static void main(String[] args) throws InterruptedException { MyRun myRun = new MyRun(); myRun.start(); Thread.sleep(3000); myRun.suspend(); System.out.println("A= " + System.currentTimeMillis() + " i = " + myRun.getI()); // myRun.resume(); Thread.sleep(5000); // myRun.suspend(); System.out.println("B= " + System.currentTimeMillis() + " i = " + myRun.getI()); } } 复制代码
suspend和resume方法可以是线程暂停和重启,但是如果使用不当,极易造成公共的同步对象的独占和因为线程暂停而导致的数据不同步的情况。