1、进程与线程
(1)进程(Process):“正在执行的程序”,程序进入内存运行就变成了一个进程。一个进程会产生多个线程。
(2)多线程(Multithread):一个进程中同时存在几个执行体。单线程是按照函数的顺序执行,多线程是多段代码同时交替运行。CPU的一个核,在某一时刻只能执行一个线程,CPU在多个线程之间快速地切换。
(3)线程的调度:分时和抢占。分时调度是预先分配每个线程的时间。抢占式调度是按优先级。
(4)资源与调度
进程:
进程是操作系统分配资源的最小单元
线程:
线程本身拥有少量资源,它可以共享所属进程的资源
线程是操作系统调度的最小单元
(5)默认有两个进程:main进程和GC进程
进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元。
一个程序至少有一个进程,一个进程至少有一个线程。
2、多线程的实现
(1)方式一:继承Thread类
public class MyThread extends Thread{ //继承自Thread类 public MyThread(String name) { //调用父类的构造方法 super(name); } public void run() { //重写run方法,线程需要执行的代码放在run方法内 for (int i = 0; i < 20; i++) { System.out.println(getName()+":正在执行!"+i);//Thread.currentThread().getName();获取当前线程对象的名称 } } }
public class Test { public static void main(String[] args) { MyThread mt = new MyThread("我的线程"); mt.start(); for (int i = 0; i < 20; i++) { System.out.println("主函数线程!"+i); } System.out.println("主函数执行结束了"); } }
由运行结果可知,两个线程的运行顺序由线程抢占到的CPU资源而定。
Thread.currentThread()获取当前线程对象
Thread.currentThread().getName();获取当前线程对象的名称
启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。
(2)方式二:实现Runable接口
以实现Runable接口的方式创建线程比继承Thread类有很大的优越性,因为类不能多重继承,即一个类只能继承一个类,那么如果该类已经继承了一个类,就不能实现多线程了,但是可以通过实现Runable接口的方式实现多线程。
Runnable实现多线程:
package pers.zhb.runnable;
public class MyThread implements Runnable{
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+":正在执行!"+i);
}
}
}
package pers.zhb.runnable;
public class RunnableDemo {
public static void main(String[] args) {
MyThread mt=new MyThread();
Thread t2=new Thread(mt);//Thread类本质上也是实现了Runnable接口,但是Run方法是空的
t2.start();
for (int i = 0; i < 20; i++) {
System.out.println("主函数线程!"+i);
}
System.out.println("主函数执行结束了");
}
}
(3)join()方法的使用
join()方法的作用是使线程串行化,如在线程B中调用线程A的join方法,则线程A执行完后线程B再执行
package pers.zhb.runnable;
public class MyThread implements Runnable{
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+":正在执行!"+i);
}
}
}
package pers.zhb.runnable;
public class RunnableDemo {
public static void main(String[] args) throws InterruptedException {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt);
t1.start();
t1.join();
for (int i = 0; i < 20; i++) {
System.out.println("主函数线程!" + i);
}
System.out.println("主函数执行结束了");
}
}
主线程在子线程运行结束后才开始运行。
3、线程池
(1)概念
我们用两种方式创建的线程,在使用后都会被销毁,频繁地创建和销毁会造成时间和资源的浪费。线程池是一个能够容纳多个线程的容器,里面的线程可以反复使用。线程是稀缺资源,它的创建与销毁是一个相对偏重且耗资源的操作,而Java线程依赖于内核线程,要进行操作系统状态切换,为避免资源过度消耗需要设法重用线程执行多个任务。线程池就是一个负责对线程进行统一分配、调优与监控。
public class MyThread implements Runnable { public void run() { System.out.println(Thread.currentThread().getName()); } }
public class RunnableDemo { public static void main(String[] args) throws InterruptedException { ExecutorService es = Executors.newFixedThreadPool(3); // Executors:线程池创建工厂类,调用方法返回线程池对象 es.submit(new MyThread()); es.submit(new MyThread()); es.submit(new MyThread()); } }
(2)使用
- 单个任务处理时间比较短
- 需要处理的任务数量很大
(3)优点
- 重用存在的线程,减少线程创建,消亡的开销,提高性能。我们用创建的线程,在使用后都会被销毁,频繁地创建和销毁会造成时间和资源的浪费。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。线程池是一个能够容纳多个线程的容器,里面的线程可以反复使用。
- 提高线程的可管理性,可统一分配,调优和监控
4、多线程的优点
(1)充分利用CPU资源
在单线程的程序中,如果线程被阻塞程序就不能执行,因为它们是顺序执行的。而在多线程程序中,如果一个线程被阻塞了别的线程就可以利用CPU资源。也就是说CPU一直处于忙碌的状态。在有读写操作的系统中,当处于读写操作的时候不需要CPU资源,只有计算的时候才需要,如果是单线程执行,CPU就会有一段时间处于空闲状态,多线程的话CPU就能一直处于忙碌状态,提高了CPU的利用率。
(2)简化编程模型
手机游戏中既有背景音乐又有画面等功能,这些功能是并行的,如果利用单线程只能按照顺序执行,但是运用多线程的知识就可以很容易地实现