先了解下多进程,什么是多进程什么是多线程,多进程跟多线程的关系
进程:指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,下面为三个进程
线程:进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少一个线程,也可以有多个线程
举个栗子:
多进程:比如食堂,食堂里的一个个卖馒头,卖盖饭,卖砂锅的窗口就是一个个进程,一到饭点,这些窗口同时卖饭,这叫多进程,也即是你手机上着微信也能聊QQ,还能听音乐
多线程:比如卖馒头的今天只有100个馒头,你可以排一队,也可以排两队,甚至三队,这个队就是线程,这就是多线程
简而言之:一个程序运行后至少有一个进程,也可以是多个,一个进程中至少有一个线程,也可以有多个
1.1主线程
当一个程序启动时,就有一个进程被操作系统(OS)创建,与此同时一个线程也立刻运行,该线程通常叫做程序的主线程(Main Thread),因为它是程序开始时就执行的,如果你需要再创建线程,那么创建的线程就是这个主线程的子线程,发现创建新执行线程有两种方法。
①一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。创建对象,运行线程
②另一种方法是声明一个实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程
1.2Thread
创建线程的步骤:
1 定义一个类继承Thread。
2 重写run方法。
3 创建子类对象,就是创建线程对象。
4 调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法
举个例子:
public class doubleDemo extends Thread{
@Override
public void run() {
for (int i=0;i<1000;i++)
{
System.out.println("i="+i);
}
}
}
public class Demo2 {
public static void main(String[]args)
{
doubleDemo d=new doubleDemo();
d.start();
}
}
原理:Thread类的子类重写Run方法,只是将Run方法里的代码块启动起来,创建子类对象并start才是运行起来
可是为什么不直接创建线程Thread的对象呢,那是因为start调用的是Run方法,相当于线程没有启动就运行
1.3实现Runnable接口
创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程
创建线程的步骤。
1、定义类实现Runnable接口。
2、覆盖接口中的run方法。。
3、创建Thread类的对象
4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
5、调用Thread类的start方法开启线程
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i=0;i<1000;i++)
{
System.out.println("i="+i);
}
}
}
public class Demo3{
public static void main(String[]args){
Runnable runnable=new MyRunnable();
Thread thread=new Thread(runnable);
thread.start();
for (int j=0;j<1000;j++){
System.out.println("j="+j);
}
}
实现Runnable接口跟继承Thread类的区别
实现Runnable接口,避免了继承Thread类的单继承局限性。覆盖Runnable接口中的run方法,将线程任务代码定义到run方法中。但创建Thread类的对象,只有创建Thread类的对象才可以创建线程
实现Runnable的好处
第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。
1.4线程池
线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源
在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务
1.4.1使用线程池方式--Runnable接口
通常,线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法
Executors:线程池创建工厂类
public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象
ExecutorService:线程池类
Futuresubmit(Runnable task):获取线程池中的某一个线程对象,并执行
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
使用线程池中线程对象的步骤:
创建线程池对象
创建Runnable接口子类对象
提交Runnable接口子类对象
关闭线程池
具体代码如下:
public class MyRunnablePool implements Runnable{
@Override
public void run() {
System.out.println("同桌张杰");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class MyRunnablePoolDemo {
public static void main(String[]args){
ExecutorService service= Executors.newFixedThreadPool(2);
Runnable R=new MyRunnablePool();
service.submit(R);
service.submit(R);
service.shutdown();
}
}
1.4.2使用线程池方式—Callable接口
Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
ExecutorService:线程池类
Futuresubmit(Callable task):获取线程池中的某一个线程对象,并执行线程中的call()方法
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
使用线程池中线程对象的步骤:
创建线程池对象
创建Callable接口子类对象
提交Callable接口子类对象
关闭线程池
代码演示:
public class MyCallablePool implements Callable {
int x;
int y;
public MyCallablePool(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public Integer call() throws Exception {
return x+y;
}
}
public static void main(String[]args){
ExecutorService service= Executors.newFixedThreadPool(2);
MyCallablePool myCallablePool=new MyCallablePool(100,300);
Future<Integer> future=service.submit(myCallablePool);
try {
Integer I=future.get();
System.out.println(I);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}