在进入Java并发编程的主要内容前,我们先从并发编程的几个基础概念说起。
1.进程和线程的区别:
进程:程序运行资源分配的最小单位,进程内有多个线程,会共享这个进程资源。
线程:CPU调度的最小单位。
引入线程的必要性: 在还没有线程这个概念之前,进程是程序资源分配的最小单位,可是进程间的变量却是独一份的,计算机在这种情况下 很难实现变量之间的共享,这样很难实现并发运行程序,使得CPU的资源大大浪费。在引入线程之后,不同的线程之间能共享同一个进程间独一份的变量,这不仅很好地实现了并发运行,也使得CPU的利用率大大提高。
2.并行与并发的区别:
并行:同一时刻,可已处理事情的能力
并发:与时间单位相关,在单位时间内处理事情的能力(不一定要同时)
举一下简单的例子:
你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。
你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。 (不一定是同时的)
你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
饭堂里有8个窗口可以同时进行打饭,每个人打饭的时间是30s,则饭堂的并行度为8,饭堂在一分钟内的并发度为16。
3.新建线程的方式(Java里的程序天生就是多线程的)
1)类Thread:继承该类,重写run(), 新生: new Thread(类).start()
代码:
package thread基础;
/**
* @author lenovo
*/
public class StartAndRun {
public static class ThreadRun extends Thread{
@Override
public void run() {
int i=10;
while(i>0){
try {
Thread.sleep(1000);
System.out.println("I am "+Thread.currentThread().getName()
+" and now the i="+i--);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ThreadRun beCalled=new ThreadRun();
beCalled.setName("BeCalled");
beCalled.run();
}
}
运行结果:
2) 接口Runnable: 实现该接口,重写run() 新生: new Thread(类).start()
3) 接口Callable:(这个接口的实现类可以有返回值,可以抛出异常)
实现该类,重写call()
新生:UseCall useCall=new UseCall(); //UseCall是我们自己书写的Callable的实现类
FutureTask<> futureTask=new FutureTask<>(useCall);
new Thread(futureTask).start();
2和3的代码示例:
package thread基础;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author lenovo
*/
public class NewThread {
/**
* 实现Runnable接口
*/
private static class UseRun implements Runnable{
@Override
public void run() {
System.out.println("I am implements Runnable");
}
}
/**
* 实现Callable接口
*/
private static class UseCall implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("I am implements Callable");
return "CallResult";
}
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
UseRun useRun=new UseRun();
new Thread(useRun).start();
UseCall useCall=new UseCall();
//FutureTask实现了Callable到Runnable的转换
FutureTask<String> futureTask=new FutureTask<>(useCall);
new Thread(futureTask).start();
System.out.println(futureTask.get());
}
}
运行结果
区别: Runnable重写的run () 方法没有返回值, Callable重写的call () 方法有返回值
4.线程的终止:
1)stop() resume() suspend() ------>强制终止,不一定会释放资源 (新版的JDK中已经摒弃了这些方法,所以建议读者一般不要使用)
2)java的线程是协作的(建议使用下面方法对线程进行中断)
注:以下的方法都不是强制终止线程,它只是发出一个信号,只有我们在线程的处理逻辑对该信号进行处理,才能正真意义上的中断线程
interrupt() 中断一个线程,并不是强行关闭这个线程,只是置中断标志位为true
isInterrupted(): 判断当前线程是否处于中断状态
**-------》**由类Thread提供
static方法下的interrupted(): 判断是否处于中断状态,中断标志位改为false
代码示例:
package thread基础;
/**
* @author lenovo
*/
public class EndThread {
private static class UseThread extends Thread{
public UseThread(String name){
super(name);
}
@Override
public void run() {
String threadName=Thread.currentThread().getName();
while (!isInterrupted()){
System.out.println(threadName+" is run! ");
}
System.out.println(threadName+" interrupt flag is "+isInterrupted());
}
public static void main(String[] args) throws InterruptedException {
Thread endThread=new UseThread("EndThread");
endThread.start();
Thread.sleep(1);
endThread.interrupt();
}
}
}
运行结果
特别注意:当我们线程的处理逻辑中的方法会抛出InterruptedException时,线程的中断标志位会被复位为false,需要我们从自己的catch里再次中断
代码示例:
package thread基础;
/**
* @author lenovo
*/
public class HasInterruptException {
public static class UseThread extends Thread{
public UseThread(String name) {
super(name);
}
@Override
public void run() {
String threadName=Thread.currentThread().getName();
while(!isInterrupted()){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
System.out.println(threadName+" interrupt flag is "+isInterrupted());
e.printStackTrace();
interrupt();
}
System.out.println(threadName);
}
System.out.println(threadName+" interrupt flag is "+isInterrupted());
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread=new UseThread("UseThread");
thread.start();
Thread.sleep(500);
thread.interrupt();
}
}
运行结果
解释:因为线程的处理逻辑中有Thread.sleep(100);它会抛出InterruptedException的异常所以它的标志位会置为false,这时我们应该在catch语句块中手动interrupt(),否则线程将不会中断