文章目录
一.并行和并发(微观概念)
- 并行:指两个或多个事件在同一个时刻发生
- 并发:指两个或多个事件在同一个时间段内发生
二.进程和线程
-
进程是指一个内存中运行中的应用程序,每个进程都有自己独立的一块内存空间.一个程序可以启动多个进程.
-
线程是指进程中的一个执行任务(控制单元),一个进程可以同时并发运行多个线程.
-
多任务系统,该系统可以运行多个进程,一个进程也可以执行多个任务,一个进程可以包含多个线程.
-
一个进程至少有一个线程,为了提高效率,也可以在一个进程中开启多个执行任务,即多线程.
-
多进程:操作系统中同时运行的多个程序.
-
多线程:在同一个进程中同时运行多个程序.
-
进程和线程的区别:
- 进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程.
- 线程:堆空间是共享的,栈空间是独立的,线程消耗的资源也比进程小,相互之间可以影响,又称
为轻型进程或进程元
因为一个进程中的多个线程是并发运行的,那么从微观角度上考虑也是有先后顺序的,那么哪个线程执行
完全取决于CPU调度器(JVM来调度),程序员是控制不了的. - 我们可以把多线程并发看做是多个线程在瞬间抢CPU资源,谁抢到资源谁就运行,
Java程序的进程里至少包含主线程和垃圾回收线程(后台线程).
三.多线程优势
- 进程之前不能共享内存,而线程之间共享内存(堆内存)则很简单
- 系统创建进程时需要为该进程重新分配系统资源,创建线程则代价小很多,因此实现多任务并发时,
多线程效率更高, - Java语言本身内置多线程功能的支持,而不是单纯的作为底层系统的调度方式,从而化简了多线程编程
四.创建进程操作
新建ProcessDemo.java
public class ProcessDemo {
public static void main(String[] args) throws Exception {
//方式一:打开记事本,Runtime的exec()
Runtime r = Runtime.getRuntime();
r.exec("notepad");
//方式二:ProcessBuilder的start
ProcessBuilder pb = new ProcessBuilder("notepad");
pb.start();
}
}
五.使用继承方式创建并启动线程
- 方式1:继承Thread类
- 方式2:实现Runable接口
- 线程类(java.lang.Thread):Thread类和Thread的子类才能称之为线程类,阅读API
- 主线程(main方法运行,表示主线程).
-
方式1:继承Thread类:
步骤:- 定义一个类:A继承java.lang.Thread类
- 在A类中覆盖Thread类中的run方法
- 我们在run方法中编写需要执行的操作----->run方法里的,线程执行体
- 在main方法(线程)中,创建线程对象,并启动线程.
创建线程类: A类 a = new A类(); 调用线程对象的start方法 : a.start();//启动一个线程
注意:千万不要调用run方法,如果调用run方法好比是对象调用方法,依然还是只有一个线程,并没开启新的线程.线程只能调用一次.
新建TreadCreatDemo.java
class MusicThread extends Thread{
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("播放音乐");
}
}
}
public class TreadCreatDemo {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println("打游戏");
if (i == 10) {
MusicThread mt = new MusicThread();
mt.start();
}
}
}
}
六.实现Runnable接口
- 定义一个类A实现java.lang.Runnable接口,注意A类不是线程类
- 在A类中覆盖Runnable接口中的run方法
- 我们在run方法中编写需要执行的操作,----->run方法里的线程执行体
- 在main方法(线程)中,创建线程对象,并启动线程.
创建线程类对象: Thread t = new Tread(new A);
调用线程对象的start方法: t.start();
新建ImplementsRunnable.java
class MusicRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("播放音乐");
}
}
}
public class ImplemensRunnableDemo {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println("打游戏");
if (i == 10) {
Thread t = new Thread(new MusicRunnable());
t.start();
}
}
}
}
七.使用匿名内部类创建并启动线程
新建AnonymousInnerClassDemo.java
public class AnonymousInnerClassDemo {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println("打游戏");
if (i == 10) {
//test1();
test2();
}
}
}
private static void test2() {
new Thread() {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("播放音乐");
}
}
}.start();
}
public static void test1() {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("播放音乐");
}
}
}).start();
}
}
八.吃苹果比赛
继承方式
会导致一些问题,使用堆字段的话,每个人都吃同一个编号的苹果,
新建ExtendsDemo.java
class Person extends Thread{
public int apple = 50;
public Person(String name) {
super(name);
}
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(this.getName() + "吃了编号为 : " + apple-- + "的苹果");
}
}
}
public class ExtendsDemo {
public static void main(String[] args) {
new Person("小A").start();
new Person("小B").start();
new Person("小C").start();
}
}
实现接口
新建ImplementDemo.java
class Apple implements Runnable{
private int applenum = 50;
@Override
public void run() {
for (int i = 1; i < 51; i++) {
if(applenum > 0)
System.out.println(Thread.currentThread().getName() +
" 吃了编号为 : " + applenum-- + "的苹果");
}
}
}
public class ImplementsDemo {
public static void main(String[] args) {
Apple a = new Apple();
new Thread(a,"小A").start();
new Thread(a,"小B").start();
new Thread(a,"小C").start();
}
}
九.继承方式和实现接口的区别
- 继承方式:
- Java中类是单继承的,如果继承了Thread了,该类就不能再有其他的直接父类了
- 从操作上分析,继承方式更简单,获取线程名字也简单
- 从多行程共享同一个资源上分析,继承方式不能做到
- 实现方式
- Java中类可以多实现接口,此时该类还可以继承其他类,并且还可以实现其他接口(设计上更优秀)
- 从操作上分析,实现方式,稍微复杂点,获取线程名字也比较复杂,得使用Thread.currentThread()来获取
当前对象的引用 - 从多线程共享一个资源上分析,实现方式可以做到.(是否共享同一个资源)