多线程编程:
1、进程与线程
线程:程序中单独顺序的控制流;
线程本身依靠程序进行运行;
线程是程序中的顺序控制流,只能使用分配给程序的资源和环境。
进程:正在执行的程序;
一个进程可以包含多个线程;
一个进程至少要包含一个线程
单线程:程序中只存在一个线程,实际上,主方法就是一个主线程;
多线程:多线程是在一个程序中运行多个任务;
多线程的目的:更好的使用CPU的资源;
2、线程的实现
两种方式:1、继承Thread类
2、实现Runnable接口
Thread类是在java.lang包中定义的,继承Thread类必须重写 run() 方法;
创建线程:继承于Thread
- public class MyThread extends Thread {
- private String name;
- //构造器 传参
- public MyThread(String name) {
- // 获取传入 MyThread 方法的字符串
- this.name = name;
- }
- public void run() {
- // 打印 name 的同事,打印 J 的数值
- for (int j = 0; j < 20; j++) {
- System.out.println(name + ":" +j);
- }
- super.run();
- }
- }
线程的调用:
- public class ThreadDemo01 {
- public static void main(String[] args) {
- // 调用 MyThread 类,启动线程
- MyThread mt1 = new MyThread("A");
- MyThread mt2 = new MyThread("B");
- /*mt1.run();
- mt2.run();*/
- mt1.start();
- mt2.start();
- }
- }
1、进程与线程
进程:正在执行的程序;
每个进程执行,都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元
* 一个进程可以包含多个线程;
* 一个进程至少要包含一个线程
线程:程序中单独顺序的控制流;是进程中独立的控制单元;
* 线程本身依靠程序进行运行;
* 线程是程序中的顺序控制流,只能使用分配给程序的资源和环境。
单线程:程序中只存在一个线程,实际上,主方法就是一个主线程;
多线程:多线程是在一个程序中运行多个任务;
多线程的目的:是更好的使用CPU的资源;
JavaVM 启动的时候会有一个进程java.exe;
该进程中至少一个线程负责Java程序的执行,
而且这个线程运行的代码存在于main方法中,该线程称之为主线程;
扩展:JVM启动不止一个线程,除了执行主函数代码,还有一条路径负责垃圾回收机制的线程;
有多条执行路径,称为多线程。
多线程存在的意义:
多条代码同时执行,提高效率;
2、线程的实现:自定义线程控制单元
通过API的查找,Java已经提供了对这类事物的描述:
java.lang 包 Thread:程序中的执行线程;
两种方式:1、继承Thread类
2、实现Runnable接口
Thread类是在java.lang包中定义的,继承Thread类必须重写 run() 方法;
/ThreadDemo/ThreadDemo01.java
class Demo extends Thread{
public void run(){// 重写run方法
System.out.println("demo run");
}
}
class ThreadDemo{
public static void main(String[] args){
// 建立好一个对象
Demo d = new Demo(); // 就是创建好一个线程
d.start(); // 使进程开始执行
// start调用run()方法
}
}
发现运行结果每一次都不同;
因为多个线程都在获取CPU的执行权,CPU执行到谁,谁就运行;
明确一点,在某一时刻,只能有一个程序在运行,多核除外;
CPU 其实是在 快速的切换,以达到看上去是同时运行的效果;
我们可以形象的把多线程的运行,形容为在互相抢夺CPU的执行权;
这就是多线程的一个特性:随机性。 谁抢到谁执行,至于执行多长时间,是CPU说了算;
为什么要覆盖 run() 方法?
Thread类用于描述线程:
该类定义了一个功能,用于存储线程要运行的代码,该存储功能就是run()方法;
run() 就是存储线程要运行的代码。
如果直接调用run()方法,只会被主线程单一执行,没有第二条执行路径,因为没调用start()启动线程控制单
元。
d.run(); 仅仅是对象调用方法,线程虽然创建了,但是没有开启
d.start(); 开启线程并执行该线程的run方法
3、线程的状态:
sleep(time); // 时间一到,自然醒来
wait(); // 不能自动恢复
notify; // 主动唤醒wait()
stop(); // 停止线程
阻塞状态:临时状态,具备执行资格,但没有执行权;
冻结状态:放弃了执行资格;
* 唤醒时,先回到临时状态,获取执行资格;
4、线程标识(线程名称)
调用父类的私有对象 name 两种方法:
1、getName();
2、super(name);
4、线程的常用方法
currentThread(); 获取当前线程对象
getName(); 获取线程的名称
设置线程名称:setName 或者 构造函数
* 局部变量,在每一个线程区域中都有独立的一份地址;
int x = 0; 所指的内存地址是不同的;
* 全局变量 private static int ticket = 100; // 多个线程共用一个变量
示例:卖票 TichetDemo.java
如果定义静态,变量的生命周期较长,占用CPU 资源较多
要实现Runnable接口解决:
创建线程的第二种方式:实现 Runnable 接口
步骤: 1、定义类实现Runnable接口
2、覆盖Runnable接口的run方法
3、通过Thread建立线程对象
4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
5、调用Thread累的start方法开启线程并调用RUN你爸了本来、接口子类的run方法
¥¥经常考:实现接口和继承类的区别是什么?
* 继承只能继承一次,当一个类已经继承了一个父类,此时就不能再继承Thread;
此时就需要通过实现Runnable接口,调用线程执行代码;
* 实现的好处,就是避免了单继承的局限性; 在定义线程时,建议使用实现方式、
* 区别:存放代码位置不同
继承Thread:线程代码存放Thread子类run方法中;
实现Runnable:线程代码存在接口的子类的run方法中;
接口的方法不能抛出异常,否则报错,只能try catch;
try{Thread.sleep(10);}catch{Exception e};
* 通过分析发现:打印出 0, -1, -2 等错票;
* 多线程的运行,最担心出现安全问题;
问题原因:
* 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完;
导致共享数据的错误;
= = =
Java对于多线程的安全问题提供了专业的解决方法:
同步代码块;
synchronized(对象){
需要被同步的代码
}
()里的对象就是“锁”;
“同步代码块”相当于“上了锁”,使用的时候,其他线程就无法执行这块代码;
同步的前提:
1、必须要有两个或者两个以上的线程;
2、必须是多个线程使用同一个锁
3、必须保证同步中只能有一个线程在运行
同步的好处:解决多线程的安全问题
同步的弊端:多个线程都要判断是否有“锁”,消耗资源,变得稍慢;
如何找问题:1、明确哪些代码是多线程与运行代码~
2、明确共享数据;
3、明确多线程运行代码中哪些语句是操作共享数据的;
同步的两种方法:1、同步代码块;2、同步方法