进程
相关概念
- 是正在运行的程序
- 是系统进行资源分配和调用的独立单位
- 每个进程都游它自己的内存空间和系统资源
线程
相关概念
- 是进程中的单个顺序控制流,是一条执行路径
- 单线程:一个进程中只有一条执行路径,称为单线程程序
- 多线程:一个进程中有多条执行路径,称为多线程程序
JAVA中的线程调度模型
抢占式调度模型:各个线程抢占cpu时间片,抢到时间片线程才可正常运行。优先级高的抢到时间片的概率大
多线程的实现方式一(继承Thread类)
- 自定义一个类继承Thread类
- 在类中重写Thread类中的run()方法,方法体为线程执行的代码
public class MyThreadDemo extends Thread {
//声明有参构造,方便在创建对象时直接设定线程名称
public MyThreadDemo(String name) {
super(name);//此语句访问了父类Thread的有参构造,以name作为有参构造参数传递,此时name即为线程名
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//直接调用Thread中的getName()方法获取线程名称
System.out.println(getName()+":"+i);
}
}
}
- 创建类对象
- 调用继承自Thread类中的start()方法启动线程
MyThreadDemo mtd=new MyThreadDemo("aaa");//直接创建线程名为aaa的线程对象mtd
MyThreadDemo mtd2=new MyThreadDemo("bbb");//直接创建线程名为bbb的线程对象mtd2
mtd.start();//启动线程
mtd2.start();
注意点:
启动线程的步骤
- 调用statrt()方法
- JVM会调用此线程中重写的run()方法
若直接调用重写后的run()方法,线程不会启动,只会单纯执行该类run()方法调用
Thread类中的常用方法
1.设置线程名称:void setName(String name)
MyThreadDemo mtd=new MyThreadDemo("aaa");//直接创建线程名为aaa的线程对象mtd
mtd.setName("线程1");//原线程名为aaa修改为线程1
2.获取线程对象的线程名称:String getName
MyThreadDemo mtd=new MyThreadDemo("aaa");//直接创建线程名为aaa的线程对象mtd
String str=mtd.getName();
System.out.println(str);//结果为aaa
3.获取当前正在执行的线程对象: static Thread currentThread()
4.获取线程的优先级: int getPriority() 默认线程优先级为5
5.设置线程的优先级:void setPriority(int newPriority) 线程优先级范围为1~10,10最高
**注意点:**线程优先级越高只是代表抢占到cpu时间片的概率越大,并非是一定比优先级低的线程先运行
线程控制(Thread类中方法)
1.static void sleep(long millis): 使当前正在执行的线程对象暂停执行指定参数的毫秒数,暂停时间结束后继续执行
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//直接调用Thread中的getName()方法获取线程名称
System.out.println(getName()+":"+i);
Thread.sleep(1000);//线程每输出一次就会暂停1s
}
2.void join:等待线程死亡,若有线程对象调用此方法,那么其他线程必须等待此线程结束,才能运行
ThreadJoin one =new ThreadJoin("飞机");
ThreadJoin two =new ThreadJoin("高铁");
ThreadJoin three =new ThreadJoin("自行车");
//调用join()使得线程对象one先死亡,后其余两个线程才能开始执行
one.start();
one.join();
two.start();
three.start();
注意点: 线程对象调用join()方法必须在线程已经调用start()方法的情况才有效
3.void setDaemon(boolean on):将线程设置为守护线程,当运行的线程都是守护线程时,JAVA虚拟机将会退出(但不会立即推出,守护线程仍会运行一小段时间)当参数为true表示设置为守护线程
线程的生命周期
多线程的实现方式二(实现Runnable接口)
- 自定义一个类实现Runnable接口
- 在类中重写run()方法,方法体为线程执行的代码
//创建自定义类,该类要实现Runnable接口
public class RynnableDemo implements Runnable {
//重写Runnable中的run()方法
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
3.创建自定义类对象
4.以自定义类对象作为参数,创建Thread类对象
5.调用start()方法启动线程
//创建实现Runnable接口的自定义类对象
RynnableDemo rd=new RynnableDemo();
//可采用有参构造方式,直接声明线程的名称
Thread t=new Thread(rd,"666");
//启动线程
t.start();
相比于继承Thread类实现多线程的优点
- 避免了Java单继承的局限性
- 适合多个相同程序的代码取处理同一个资源的情况,把线程和程序的代码、数据有效分离较好的体现了面向对象的设计思想
多线程程序数据安全问题
数据安全问题判断标准
- 是否为多线程
- 是否有共享数据(一般指自定义的成员变量)
- 多条语句操作共享数据
以上标准全部满足才会发生数据安全问题
解决方法
基本思想: 让程序没有安全问题的环境,一般针对多条语句操作共享数据进行解决
**如何实现:**把多条语句操作共享数据的代码给”锁“起来,让任意时刻只能由一个线程执行
“锁”的方法1.:同步代码块关键字synchronized
Object obj=new Object()
// synchronized (obj) {
多条语句操作共享数据代码;
}
“锁”的方法2.:同步方法关键字synchronized此时锁的对象为this
“锁”的方法3.:同步静态方法关键字synchronized此时锁的对象为“类名.class”