正在运行的程序,是系统进行资源分配和调用的独立单位;
每一个进程都有它自己的内存空间和系统资源.
# 多进程的作用:
多进程的作用不是提高执行速度,而是提高CPU的使用率.
是进程中的单个顺序控制流,是一条执行路径;
一个进程如果只有一条执行路径,则称为单线程程序;
一个进程如果有多条执行路径,则称为多线程程序.
# 多线程的作用:
多线程的作用不是提高执行速度,而是为了提高应用程序的使用率;
线程运行具有随机性.
# 并行是逻辑上同时发生,指在某一个时间内同时运行多个程序。
# 并发是物理上同时发生,指在某一个时间点同时运行多个程序。
JVM虚拟机进程启动时至少启动了运行程序的主线程和垃圾回收的线程,属于多线程.
# 线程调度模型:两种线程调度模型
分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片;
抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些.
# Java使用的是抢占式调度模型.
# 线程的生命周期:
1.新建:创建线程对象
2.就绪:线程具有执行资格,没有执行权
3.运行:线程抢占到CPU资源,具有执行资格,有执行权
阻塞:执行sleep()或wait()或其他原因使得线程处于该状态,没有执行资格,没有执行权其他的有些操作可以将该线程激活,使该线程回转到就绪状态
4.死亡:线程对象变为垃圾,等待垃圾回收器的回收
继承Thread类,重写run()方法,使用start()方法启动线程.
run()方法和start()方法的区别
# run()方法只是封装需要开启线程进行执行的代码,本身不能开启线程,单独调用时和普通方法调用效果相同;
# start()方法是开启线程后,由jvm调用run()方法,并且一个线程只能启动一次,不能启动多次(否则会报异常).
获取和设置线程名称
* 可以通过有参构造方法设置线程的名称:
public Thread(String name):创建线程名称为name的线程对象;
public final String getName():返回该线程的名称;
public final void setName(String name):将线程名称改为name.
* 获取调用当前程序的线程的名称:
public static Thread currentThread():返回对当前正在执行的线程对象的引用.
案例代码1
public class My_Thread_Demo01 {
public static void main(String[] args) {
// 创建对象,启动线程1
MyThread mt = new MyThread();
mt.start();
// 创建对象,启动线程2
MyThread mt2 = new MyThread();
mt2.start();
// 设置线程的名称
mt.setName("线程1");
mt2.setName("线程2");
// 获取调用当前程序的线程
// 获取main方法执行的线程
System.out.println(Thread.currentThread().getName());
}
}
// 创建线程方式1
class MyThread extends Thread {
// 重写run方法,定义需要在线程中执行的代码
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(this.getName()+":"+i);
}
}
}
线程优先级
# 线程优先级:默认的线程优先级是5,范围是[1,10]之间的整数,数值越大优先级越高.
public final void setPriority(int newPriority):更改线程的优先级为newPriority;
public final int getPriority():返回线程的优先级.
# 注意事项:线程的优先级只是代表线程被执行的几率高,一般在多次的调用中效果较为明显.
案例代码2
public class My_Thread_Demo02 {
public static void main(String[] args) {
MyThread2 mt1 = new MyThread2();
MyThread2 mt2 = new MyThread2();
MyThread2 mt3 = new MyThread2();
mt1.setName("线程1");
mt2.setName("线程2");
mt3.setName("线程3");
// 设置线程优先级
mt1.setPriority(1);
mt1.setPriority(5);
mt1.setPriority(9);
// 启动线程
mt1.start();
mt2.start();
mt3.start();
// 获取线程优先级
System.out.println(mt1.getPriority());
System.out.println(mt2.getPriority());
System.out.println(mt3.getPriority());
}
}
线程控制
# 线程控制:
* 线程休眠:在线程的run()方法中通过Thread直接调用
public static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠.
* 线程加入:该线程执行完毕后其他的线程才能执行
public final void join():等待该线程执行完毕后,其他的线程才能执行.
* 线程礼让:在线程的run()方法中调用,是让同一个线程类不同对象间线程的执行尽可能的均衡化.
public static void yield():暂停当前正在执行的线程对象,并执行其他线程.
* 后台线程:就是主线程执行完后,后天线程(守护线程)就会终止(可能会守护线程会再执行一段时间,但不会执行完);
public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程,当正在运行的线程都是守护线程时,Java虚拟机退出,该方法必须在启动线程前调用.
* 中断线程:
public final void stop():中断线程,是直接清除线程的状态,被终止的线程中未执行的代码不能再执行;
public void interrupt():中断线程,是清除线程的状态,并抛出异常,线程中未被执行的代码还可以继续执行.
案例代码3
public class My_Thread_Demo03 {
public static void main(String[] args) {
// 测试线程休眠,线程加入,线程礼让,后台线程
// test01();
// 测试中断线程
MyThread3 mt1 = new MyThread3();
mt1.start();
// 当mt1休眠时间操作5秒就中断
try {
Thread.sleep(5000);
// mt1.stop();
mt1.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void test01() {
MyThread3 mt1 = new MyThread3();
MyThread3 mt2 = new MyThread3();
MyThread3 mt3 = new MyThread3();
mt1.setName("线程1");
mt2.setName("线程2");
mt3.setName("线程3");
// 设置mt2和mt3为守护线程,当mt1线程执行完后,mt2和mt3会在终止执行(在继续执行一段时间后)
mt2.setDaemon(true);
mt3.setDaemon(true);
mt1.start();
// 将线程1加入
/*try {
mt1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}*/
mt2.start();
mt3.start();
}
}
class MyThread3 extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("线程开始执行");
System.out.println(this.getName() +":" + i);
// 使线程休眠1秒
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
System.out.println("线程被中断了");
}
// 线程礼让
// Thread.yield();
System.out.println("线程继续执行");
}
}
}
# 创建线程:类实现Runnable接口,并实现run方法.
* 创建线程的另一种方法是声明实现Runnable接口的类,该类然后实现run方法,
* 然后可以分配该类的实例,在创建Thread时作为一个参数来传递并调用start()方法启动线程.
# 接口方式的线程的作用:
* A:可以避免由于Java单继承带来的局限性(继承其他类的同时可以实现线程接口);
* B:适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想.实现线程接口的类只创建一个对象,但是可以将这个对象作为多个线程实例的参数,则多个线程操作一个对象中的数据.
案例代码
public class My_Runnable_Demo01 {
public static void main(String[] args) {
MyThread4 mt = new MyThread4();
Thread t1 = new Thread(mt, "线程1");
t1.start();
Thread t2 = new Thread(mt, "线程2");
t2.start();
}
}
class MyThread4 implements Runnable {
// 重写run方法
@Override
public void run() {
for (int i = 0; i < 10; i++) {
// 这种方式创建的线程不能直接获取线程的名字,可以通过Thread.currentThread()获取当前执行的线程
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class My_Thread_Demo05 {
public static void main(String[] args) {
// 方式1
// 匿名子类和匿名接口方式
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}).start();
// 方式2
// 匿名子类实现
new Thread() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}.start();
// 方式3
// 输出的结果是python的,匿名子类对象的方法将接口中的run方法覆盖掉
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("java"+":"+i);
}
}
}) {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("python"+":"+i);
}
}
}.start();
}
}
#CPU的一次操作必须是原子性的:当操作数据不是原子性(多个语句操作同一个数据),可能出现一个线程在完成数据操作的所有语句前,其他的线程就来操作数据;
* 如:线程A和线程B,操作共享数据的有a和b两条语句,
* 当A操作完数据的a语句还没操作语句b时,线程B进来操作数据,
* 由于A的b语句操作没有完成,共享数据没有发生变化,当B线程执行完b语句时,A线程再执行使用的还是之前的数据,则线程A和线程B的操作的数据的结果相同.
# 线程的执行具有随机性和一定的延迟.
# A:是否是多线程;
# B:是否存在共享数据;
# C:是否多条语句操作共享数据.
# 同步代码块:锁对象是任意对象,线程间共享的对象
* 格式:
synchronized(锁对象){
需要被同步的代码块;
}
# 同步方法:锁对象是this
在方法上添加synchronized关键字修饰
# 静态同步方法:锁对象是该类的.class对象
在方法上添加synchronized关键字修饰
# 前提:
多线程环境;
多个线程使用同一个锁对象(不同的执行路径要使用相同的锁对象)
# 好处:
解决多线程安全问题
# 弊端:
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率.
# 如果锁对象是this,就可以考虑使用同步方法;
# 否则能使用同步代码块的尽量使用同步代码块.
/*
* 模拟买电影票
*/
public class My_Thread_Test03 {
public static void main(String[] args) {
SellTickets2 s1 = new SellTickets2();
Thread t1 = new Thread(s1, "窗口1");
Thread t2 = new Thread(s1, "窗口2");
Thread t3 = new Thread(s1, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
class SellTickets2 implements Runnable {
// 定义票数
private static int tickets = 100;
private Object obj = new Object();
// 使用同步代码块
/*@Override
public void run() {
synchronized (obj) {
while( tickets > 0) {
System.out.println(Thread.currentThread().getName()+"窗口正在出售第"+(tickets--)+"张票");
}
}
}*/
// 使用同方法
@Override
public void run() {
sellTicket();
}
public synchronized void sellTicket() {
while( tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"窗口正在出售第"+(tickets--)+"张票");
}
}
}