线程
多线程:
进程:
进程指正在运行的程序,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。
线程:
线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
总结:一个程序运行后至少有一个进程,一个进程中可以包含多个线程
单线程:
一个进程只有一个线程。
多线程:
一个进程有多个线程。
程序运行原理解析:
分时调度
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
抢占式调度
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
线程在内存里的体现,有几条线程,有几个栈(在java里)
主线程就是主方法。
Thread类:
创建线程的步骤:
第一种方式:
1 定义一个类继承Thread。
2 重写run方法。
3 创建子类对象,就是创建线程对象。
4 调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。
代码体现:
public class MyThread extends Thread{ //重写run方法描述线程任务(先定义一个类继承Thread类) public void run() { //int y=1/0; //Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } }
public class Demo02 { public static void main(String[] args) { //线程在内存里的体现,有几条线程,有几个栈(在java里) //创建线程对象 MyThread my=new MyThread(); //开启线程 my.start(); //描述主线程的任务 //currentThread返回(哪个程序正在执行)的Thread对象,() for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } }
获取线程名称:
总结:线程对象调用run方法不开启线程。仅是对象调用方法。线程对象调用start开启线程,并让jvm调用run方法在开启的线程中执行。
Runnable接口:
第二种方式:
创建线程的步骤。
1、定义类实现Runnable接口。
2、覆盖接口中的run方法。。
3、创建Thread类的对象
4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
5、调用Thread类的start方法开启线程。
代码:
public class MyRunnable implements Runnable{ //第二种方式创建多线程 //先实现Runnable接口 public void run() { //描述线程任务 for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } //避免了单继承的缺陷,实现了更多的可能性 //没有将线程任务与线程对象绑在一起 }
public class Demo03 { public static void main(String[] args) { // 创建线程任务对象 MyRunnable my = new MyRunnable(); // 创建线程对象,并将线程任务交给线程对象 Thread t = new Thread(my); // 开始线程, t.start(); // 描述主线程任务 for (int i = 0; i < 100; i++) { // 先获得当前任务对象,调用方法获得主线程的名字 System.out.println(Thread.currentThread().getName() + ":" + i); } } }
好处:
第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。
匿名内部类的使用:
使用匿名内部类的方式实现Thread类。
public class Demo04 { public static void main(String[] args) { //匿名内部类 //new 父类/接口(){} //方法重写 //1:继承Thread类的方式 Thread t=new Thread(){ public void run() { for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } }; //开启线程 t.start(); } }
使用匿名内部类的方式实现Runnable接口。
public class Lianxi01 { public static void main(String[] args) { //创建线程任务对象 //创建匿名内部类对象 Runnable r=new Runnable(){ public void run() { //重写run方法 for(int i=0;i<100;i++){ //获取线程名 System.out.println(Thread.currentThread().getName()+":"+i); } } }; //创建线程对象,并将线程任务交给线程对象 Thread t=new Thread(r); //开始线程 t.start(); //描述主线程任务 for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } }
线程池:
概念:线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。
线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。
原理:
可以将线程对象存到ArrayList集合中,泛型是<Thread>,通过add方法存到集合中,再通过remove方法取出来,在用add方法加进去。
使用线程池方式1:
Runnable接口:
Executors:线程池创建工厂类。public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象,int 表示的几条线程。
ExecutorService:线程池类 Future<泛型> submit(Runnable task):获取线程池的某一个对象,并执行。
Future接口:用来记录线程任务执行完毕后产生的结果。
使用线程池中线程对象的步骤:
创建线程池对象
创建Runnable接口子类对象
提交Runnable接口子类对象
关闭线程池
代码:
public class MyRunnable implements Runnable{ //第二种方式创建多线程 //先实现Runnable接口 public void run() { //描述线程任务 for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+":"+i); } } //避免了单继承的缺陷,实现了更多的可能性 //没有将线程任务与线程对象绑在一起 }
public class Demo01 { public static void main(String[] args) { //1:获取线程池对象 ExecutorService es=Executors.newFixedThreadPool(2); //创建线程任务对象 MyRunnable r=new MyRunnable(); //由线程池随机选一条空闲线程取执行线程任务 es.submit(r); es.submit(r); es.submit(r); //销毁线程池(不推荐使用) es.shutdown(); } }
第二种方式:
Callable接口:
Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
ExecutorService:线程池类
<T> Future<T> submit(Callable<T> task):获取线程池中的某一个线程对象,并执行线程中的call()方法
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
使用线程池中线程对象的步骤:
创建线程池对象
创建Callable接口子类对象
提交Callable接口子类对象
关闭线程池
代码:
public class MyCallable implements Callable<String>{ public String call() throws Exception { return "abc"; } }
public class Demo02 { public static void main(String[] args) throws InterruptedException, ExecutionException { //获取线程池对象, ExecutorService es=Executors.newFixedThreadPool(5); //创建线程任务 MyCallable r=new MyCallable(); //提交线程任务 Future<String> f=es.submit(r); //获取返回值 String s=f.get(); System.out.println(s); //关闭线程池 es.shutdown(); } }
线程练习:
public class GetSum implements Callable<Integer>{ private int n; public GetSum(){ } public GetSum(int n){ this.n=n; } public Integer call() throws Exception { int sum=0; for(int i=1;i<=n;i++){ sum+=i; } return sum; } }
public class Demo03 { public static void main(String[] args) throws InterruptedException, ExecutionException { //分别用两条线程计算1——n的和,一条计算1-100的和,一条计算1-200的和 //获取线程池对象 ExecutorService es=Executors.newFixedThreadPool(2); //创建线程池任务 GetSum g1=new GetSum(100); GetSum g2=new GetSum(200); //提交任务 Future<Integer> f1=es.submit(g1); Future<Integer> f2=es.submit(g2); //获取返回值 Integer sum1=f1.get(); Integer sum2=f2.get(); System.out.println(sum1); System.out.println(sum2); //销毁线程池 es.shutdown(); } }