1 线程与进程:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。多线程即一个程序中有多个线程在同时执行。
2 多线程调度模式:
分时调度:所有线程轮流使用CPU,平均分配每个线程占用CPU的时间。
抢占式调度:优先级高的线程优先使用CPU,优先级相同,随机选择一个(线程随机性),Java使用的为抢占式调度。
抢占式调度在多个线程间高速切换。对于CPU的一个核而言,某个时刻只能执行一个线程,而 CPU在多个线程间切换速度很快,感觉像是在同一时刻运行。多线程并不能提高 运行速度,但能提高运行效率,让CPU的使用率更高。
3 主线程:JVM启动后,主线程从main方法开始,一直到main方法结束。
4 创建新线程的方法:
①定义一个类继承Thread类,该子类重写Thread类的 run方法。创建子类对象,即创建线程对象,调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。
1 //自定义测试类
2 public classDemo01 {3 public static voidmain(String[] args) {4 //创建自定义线程对象
5 MyThread mt = newMyThread();6 //开启新线程
7 mt.start();//线程对象调用start开启线程,并让jvm调用run方法在开启的线程中执行。调用run方法不开启线程,只是调用对象的方法。8 //在主方法中执行for循环
9 for (int i = 0; i < 10; i++) {10 System.out.println("main线程!"+i);11 }12 }13 }14 //自定义线程类
15 public class MyThread extendsThread {16 //定义指定线程名称的构造方法
17 publicMyThread(String name) {18 //调用父类的String参数的构造方法,指定线程的名称
19 super(name);20 }21 /**
22 * 重写run方法,完成该线程执行的逻辑23 */
24 @Override25 public voidrun() {26 for (int i = 0; i < 10; i++) {27 System.out.println(getName()+":正在执行!"+i);28 }29 }30 }
为什么要继承Thread类,并调用其start方法才能开启线程呢?
继承Thread类:因为Thread类用来描述线程,具备线程应该有功能。直接mt.run();是直接调用了MyThread对象的run方法,重写的run方法执行了,但没有开启多线程。
那为什么不直接创建Thread类的对象呢?如下代码:
Thread t1 = new Thread();
t1.start();
这样做没有错,但是该start调用的是Thread类中的run方法而不是我们创建的MyThread类中的run方法,这个run方法并没有我们需要让线程执行的代码。
②声明一个类,实现Runnable接口。该类实现run方法。创建Runnable的子类对象,new一个新Thread对象,将Runnable子类对象传入到新Thread的构造方法中,开启线程。
1 //测试类
2 public classDemo02 {3 public static voidmain(String[] args) {4 //创建线程执行目标类对象
5 Runnable runn = newMyRunnable();6 //将Runnable接口的子类对象作为参数传递给Thread类的构造函数
7 Thread thread = newThread(runn);8 Thread thread2 = newThread(runn);9 //开启线程
10 thread.start();11 thread2.start();12 for (int i = 0; i < 10; i++) {13 System.out.println("main线程:正在执行!"+i);14 }15 }16 }17 //自定义线程执行任务类
18 public class MyRunnable implementsRunnable{19
20 //定义线程要执行的run方法逻辑
21 @Override22 public voidrun() {
24 for (int i = 0; i < 10; i++) {25 System.out.println("我的线程:正在执行!"+i);26 }27 }28 }
实现Runnable的好处:
第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。
5 多线程内存分配:每创建一个新线程都会开辟一个新的栈内存空间,线程任务结束释放栈内存。
6 获取线程的名称:Thread.currentThread().getName();(原来主线程的名称:main;自定义的线程:Thread-0,多个线程时,数字顺延。如Thread-1......)
7 多线程设置名称:测试类中mt.setName("xxx");或者MyThread类中super("xxx");调用父类构造器。一般情况不需要设置名称。
8 多线程内使用匿名内部类:
1 //方式1:创建线程对象时,直接重写Thread类中的run方法
2 newThread() {3 public voidrun() {4 for (int x = 0; x < 40; x++) {5 System.out.println(Thread.currentThread().getName() + "...X...." +x);6 }7 }8 }.start();9
10 //方式2:使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法
11 Runnable r = newRunnable() {12 public voidrun() {13 for (int x = 0; x < 40; x++) {14 System.out.println(Thread.currentThread().getName() + "...Y...." +x);15 }16 }17 };18 new Thread(r).start();
9 线程池及作用:线程池,容纳多个线程的容器,其中的线程可以反复使用,省去了创建线程对象的操作,无需反复创建线程而消耗过多资源。
在java中,创建/销毁线程消耗的时间和系统资源都很大,甚至可能要比在处理用户请求的时间和资源要多。运行中的线程也占用系统资源,如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。
10 使用线程池的方式:
①Runnable接口
Executors:线程池创建工厂类
public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象
ExecutorService:线程池类
Future> submit(Runnable task):获取线程池中的某一个线程对象,并执行
Future接口:用来记录线程任务执行完毕后产生的结果。
使用线程池中线程对象的步骤:
1创建线程池对象
2创建Runnable接口子类对象
3提交Runnable接口子类对象
4关闭线程池
1 //测试类
2 public classThreadPoolDemo {3 public static voidmain(String[] args) {4 //1创建线程池对象
5 ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象6 //2创建Runnable子类对象
7 MyRunnable r = newMyRunnable();8
9 //自己创建线程对象的方式10 //Thread t = new Thread(r);11 //t.start(); ---> 调用MyRunnable中的run()12
13 //3提交Runnable接口子类对象
14 service.submit(r);15 //再获取个线程对象,调用MyRunnable中的run()
16 service.submit(r);17 service.submit(r);18 //注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中19
20 //关闭线程池21 //service.shutdown();
22 }23 }24 //Runnable接口实现类:略
②Callable接口
Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call 方法可抛出异常。
ExecutorService:线程池类
Future submit(Callable task):获取线程池中的某一个线程对象,并执行线程中的call()方法
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
使用线程池中线程对象的步骤:
1创建线程池对象
2创建Callable接口子类对象
3提交Callable接口子类对象
4关闭线程池