1、进程是一个应用程序
什么是线程1、线程是一个进程中的执行场景/执行单元
2、一个进程可以启动多个线程
进程和线程的关系1、进程A和进程B的内存不共享
2、线程A和线程B【Java中】
(1)堆内存和方法区内存共享
(2)栈内存独立,一个线程一个栈
多线程使用了多线程机制,main方法结束了,只是主线程结束,主栈空了,其他的栈(线程)可能还在压栈弹栈。
目的1、提高程序的处理效率
实现1、第一种方式
// 编写一个类,直接继承java.lang.Thread,重写run方法
public class Test { public static void main(String[] args) { // 这里是main方法,这里的代码属于主线程,在主栈中运行 // 新建一个分支线程 MyThread myThread = new MyThread(); // 启动线程 myThread.start(); // 以下代码还是运行在主线程中 for (int i = 0; i < 10; i++) { System.out.println("主线程---->" + i); } } } class MyThread extends Thread{ public void run(){ for (int i = 0; i < 3; i++) { System.out.println("分支线程----->" + i); } } }
(1)start()方法作用:启动一个分支线程, 在JVM中开辟一个新的栈空间,这段代码任务完成之后,就瞬间结束了。只是为了开启一个新的栈空间,只要开辟成功,线程就启动成功。
(2)启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
(3)run方法在分支栈的栈底部,main方法在主栈的底部。run和main是平级的。
(4)若是直接调用run()方法,则不会启动线程,不会分配新的分支栈。
2、第二种方式
线程生命周期// 实现线程的第二种方式,编写一个类实现java.lang.Runnable接口
public class Test { public static void main(String[] args) { // 创建一个可运行的对象 MyRunnable r = new MyRunnable(); // 将可运行的对象封装成一个线程对象 Thread t = new Thread(r); // 可合并成一行:Thread t = new Thread(new MyRunnable()); // 启动线程 t.start(); for (int i = 0; i < 10; i++) { System.out.println("主线程---->" + i); } } } class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("分支线程---->" + i); } }
1、新建状态
2、就绪状态
3、运行状态
4、阻塞状态
5、死亡状态
6、图解:
线程常用方法 设置线程名字获取线程名字myThread.setName("thread");
获得当前线程的引用// 默认线程名字:Thread-数字【数字从0开始】
myThread.getName();
sleep()作用:获得当前线程的引用,当t1线程执行时,获取的就是t1线程的引用;当t2线程执行时,获取的就是t2线程的引用
Thread currentThread = Thread.currentThread();
interrupt()【中断线程】静态方法,参数是毫秒
作用:让当前线程进入“阻塞”状态,放弃占有CPU时间片,让给其他线程使用【间隔特定的时间,实现】
stop()【强行终止线程,现在已过时】实例方法
作用:中断线程的睡眠【依靠java中的异常处理机制】
t.interrupt();
线程调度 常见的线程调度模型 抢占式调度模型作用:强行终止线程,缺点是容易丢失数据,不建议使用
代替实现:设置run标志,满足条件则线程运行,否则return
t.stop();
哪个线程的优先级比较高,抢到的CPU时间片的概率就高一些,Java采用的就是抢占式调度模型。
均分式调度模型平均分配CPU时间片,每个线程占有的CPU时间片时间长度一样。
常用的线程调度方法 优先级分类1、最低优先级为1,最高优先级为10,默认为5
设置线程的优先级void setPriority(int newPriority)
获取线程的优先级int getPriority()
让位方法1、static void yield()
2、作用:暂停当前正在执行的线程对象,并执行其他线程。
3、yield()不是阻塞方法,让当前线程让位,让给其他线程使用,执行结果会让当前线程从“运行状态”回到“就绪状态”。
合并线程1、void join()
2、作用:当前线程进入阻塞,调用这个方法的线程执行,知道这个线程结束,原线程才继续执行。
线程安全 多线程并发情况下数据不安全的情况1、多线程并发
2、有共享数据
3、共享数据有修改的行为
如何解决线程安全问题1、线程排队执行,不能并发,用排队执行解决线程安全问题,这种机制叫做:线程同步机制
2、缺点:会牺牲一部分效率,但是安全性问题是第一位
线程同步 异步编程模型线程t1和线程t2,各自执行,t1和t2互不影响,谁也不需要等待谁,其实就是多线程并发
同步编程模型线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行结束,或者说在t2线程执行的时候,必须等待t1线程结束,其实就是排队。
语法第一种方法:
synchronized(共享对象){
// 线程同步代码块
}
注意:1、synchronized后面小括号中传的这个“数据”是相当关键的,这个数据必须是多线程共享的数据。才能达到多线程排队。
2、()中写的内容
(1)假设有t1、t2、t3、t4、t5,有5个线程,只希望t1,t2,t3排队,t4,t5不排队,一定要在()内写一个t1,t2,t3共享的对象,而这个对象对于t4,t5来说不是共享的
第二种方法:
public synchronized void m1(){}
注意:synchronized出现在实例方法上,一定锁的是this,所以这种方式不灵活
缺点:表示整个方法体都需要同步,可能会无故扩大同步的范围,导致程序的执行效率降低,所以这种方式不常用
优点:代码节俭,如果共享的对象就是this,并且需要同步的代码块是整个方法体,建议使用这种方式
第三种方法:在静态方法上使用synchronized,表示找类锁,类锁永远只有1把,就算创建了100个对象,也只有一个类锁。
哪些变量会有安全问题1、局部变量:栈,局部变量不可能在多线程共享。
2、静态变量:方法区,可能存在
3、实例变量:堆,可能存在
局部变量情况建议使用:StringBuilder,不存在线程安全问题,效率高
死锁


