线程
多线程就是一个程序中有多个线程在同时执行。
多线程下CPU的工作原理
实际上,CPU(中央处理器)使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。
其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。
一、创建线程
方法1:继承Thread类,重写run方法
public class SubThread extends Thread{public SubThread(){super("x5456"); //通过构造方法修改线程名}public void run() {for(int i=0;i<100;i++){System.out.println(super.getName()+i);}}
}
调用:
public static void main(String[] args) {//创建刚刚继承Thread类的子类的对象SubThread st = new SubThread();//通过setName方法,修改线程名st.setName("x54256");//调用对象的start方法,会自动执行我们重写的run方法st.start();for(int i=0;i<100;i++) {System.out.println(Thread.currentThread().getName()+i); //获取当前线程的对象,调用getname()方法}
}
方法2:实现接口Runnable,重写run方法
public class SubRunnable implements Runnable{public void run(){for(int i=0;i<100;i++){try {// 调用Thread类的sleep方法,休眠50ms,由于父接口没有throws异常,so我们只能用try...catchThread.sleep(50); } catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"..."+i);}}
}
调用:
public static void main(String[] args) {//创建实现Runnable接口的类的对象SubRunnable sr = new SubRunnable();//创建Thread类的对象Thread t = new Thread(sr);//启动线程t.start();for(int i=0;i<100;i++){System.out.println(Thread.currentThread().getName()+"..."+i);}
}
方法3:使用匿名内部类,实现多线程程序
匿名内部类的前提:继承或者接口实现
使用方法:
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();}
实现接口的好处:
高内聚,低耦合:模块内能做的事就自己做,模块间的关系要尽量的小
第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。
多线程的内存图解:
线程的一生:
二、线程池
线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。
线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。
方法1:使用线程池方式--Runnable接口
public static void main(String[] args) {//调用工厂类的静态方法,创建线程池对象(ExecutorService接口的实现类)//返回线程池对象,是返回的接口ExecutorService es = Executors.newFixedThreadPool(2); //池内有2个线程//调用接口实现类对象es中的方法submit提交线程任务//将Runnable接口实现类对象,传递es.submit(new SubRunnable());es.submit(new SubRunnable());es.submit(new SubRunnable());es.submit(new SubRunnable());
}
实现的Runnable接口
public class ThreadPoolRunnable implements Runnable {public void run(){System.out.println(Thread.currentThread().getName()+" 线程提交任务");}
}
方法2:使用线程池方式—Callable接口
之前的实现方法,线程运行完没有返回值,而且不能抛异常。
Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService es = Executors.newFixedThreadPool(2);//提交线程任务的方法submit方法返回 Future接口的实现类Future<Integer> f = es.submit(new SubCallable());//获取返回值Integer i = f.get();System.out.println(i);
}
实现的Callable接口
public class SubCallable implements Callable<Integer>{@Overridepublic Integer call() {return 123;}
}
使用线程实现异步计算
private int a;
//通过构造方法传参
public GetSumCallable(int a){this.a=a;
}public Integer call(){int sum = 0 ;for(int i = 1 ; i <=a ; i++){sum = sum + i ;}return sum;
}
ThreadPoolDemo.java
/** 使用多线程技术,求和* 两个线程,1个线程计算1+100,另一个线程计算1+200的和* 多线程的异步计算*/
public class ThreadPoolDemo {public static void main(String[] args)throws Exception {ExecutorService es = Executors.newFixedThreadPool(2);Future<Integer> f1 =es.submit(new GetSumCallable(100));Future<Integer> f2 =es.submit(new GetSumCallable(200));System.out.println(f1.get());System.out.println(f2.get());es.shutdown();}
}
FutureTask的使用
FutureTask继承了Callable和Future接口,所以它既能像callable一样被Thread执行,也能像Future那样获取结果。
@Overridepublic boolean testServiceUrl(ServiceUrlTestDTO url){// 调用地图服务Callable<Boolean> callable = new WebServiceUtil(url.getUrl(),url.getProxy(),url.getToken());FutureTask<Boolean> futureTask = new FutureTask<>(callable);Thread thread = new Thread(futureTask);thread.start();try {Boolean isOK = futureTask.get();System.out.println("服务【"+ url.getUrl() +"】测试:"+ isOK);return isOK;} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}return false;}