java线程
概述
- 进程:运行时概念,运行的应用程序,进程间不能共享内存
- 线程:应用程序内并发执行的代码段,可以共享堆内存和方法区内存,而栈内存是独立的。
- 并发理解:在单核机器上,从微观角度来看,一段时间内cup只能执行一个任务,但是因为cup在只执行一段代码段的时候大部分的时间是处于等待程序的,所以可以再开几条程序,然后通过轮询机制,让cpu执行多个进程,从宏观角度来看就是所谓的并发。如果机器是多核,那么就是真正的并发。
线程调度模型
- 线程的调度模型分为: 分时调度模型和抢占式调度模型,Java使用抢占式调度模型
- 分时调度模型: 所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
- 抢占式调度模型: 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些.
- 线 程 优 先 级 主 要 分 三 种 : MAX_PRIORITY( 最 高 级 );MIN_PRIORITY ( 最 低 级 )NORM_PRIORITY(标准)默认
java 程序的运行原理?
- java 命令会启动 java 虚拟机,启动 JVM,等于启动了一个应用程序,表示启动了一个进程。该进程会自动启动一个“主线程”,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。
- 一个栈就是一个线程,所谓的并发就是多个不同的栈。
线程创建
- 继承Thread
- 线程编写要继承 java.lang.Thread
- 重写run方法
- 调用的时候调用线程类的start()方法,run方法是让cpu调用的。所以start()方法调用后run方法不会立刻调用。
- 实现runable接口
class CreateRunnable implements Runnable {
@Override
publicvoid run() {
for (inti = 0; i< 10; i++) {
System.out.println("i:" + i);
}
}
}
public class ThreadDemo2 {
publicstaticvoid main(String[] args) {
System.out.println("-----多线程创建开始-----");
// 1.创建一个线程
CreateRunnable createThread = new CreateRunnable();
// 2.开始执行线程 注意 开启线程不是调用run方法,而是start方法
System.out.println("-----多线程创建启动-----");
Thread thread = new Thread(createThread);
thread.start();
}
}
- 使用匿名内部调用
System.out.println("-----多线程创建开始-----");
Thread thread = new Thread(new Runnable() {
public void run() {
for (int i = 0; i< 10; i++) {
System.out.println("i:" + i);
}
}
});
thread.start();
System.out.println("-----多线程创建结束-----");
线程分类
- 主线程、子线程、GC线程
- 一个进程中肯定要有主线程。一般是用main函数来创建
- java主线程结束是不影响子线程的。
- GC线程是守护线程。
- 守护线程随着主线程的销毁而销毁。
- 在一个线程start前用thread.setDaemon(true)可以将用户线程变成守护线程。
- 守护线程和用户线程
- 从线程分类上可以分为:用户线程(以上讲的都是用户线程),另一个是守护线程。
- 其它所有的用户线程结束,则守护线程退出!因为 daemon守护线程是守护非守护线程的
- setDaemon(true) 使线程变成守护线程
- 守护线程一般都是无限执行的.
- 例如 java 中著名的垃圾回收器GC线程就是一个守护线程。
线程安全和锁
- 任何对象都可以是锁,因为锁的本质是参照物,类似于红绿灯,上课铃等等,关键是大家要看一个参照物。
- 同步代码块(以object为锁)
synchronized(object){
[并发逻辑]
}
- 加锁里的逻辑要尽量少
- 同步方法
public synchronized int getTicket(){
}
- 同步方法(非静态)是以当前对象作为锁。
- 如果用非静态同步方法加锁,一般独立出一个对象池,将同步方法放在对象池内,然后多线程对象每次从同一个对象池取资源。
- 同步方法(静态) 是以类作为锁。
线程生命周期
- yield();方法可以让线程方法暂时放弃cpu的抢占,但是一旦放弃后又马上开始抢,有谦让的意义
- Thread.sleep(time) 静态方法 让当前线程休眠time毫秒,不去抢占cpu
- join等到指定的线程执行完后执行
sleep() 与 wait()的比较
- 相同点:wait()的作用是让当前线程由“运行状态”进入“等待(阻塞)状态”的同时,也会释放同步锁。而sleep()的作用是也是让当前线程由“运行状态”进入到“休眠(阻塞)状态”
- 不同点:wait()会释放对象的同步锁,而sleep()则不会释放锁
join()方法作用
- 当在主线程当中执行到t1.join()方法时,就认为主线程应该把执行权让给t1
线程死亡
- 有两个原因会导致线程死亡:
- 1) run方法正常退出而自然死亡,
- 2) 一个未捕获的异常终止了run方法而使线程猝死。
- 为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.
阻塞状态
- 线程运行过程中,可能由于各种原因进入阻塞状态:
- 1>线程通过调用sleep方法进入睡眠状态;
- 2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
- 3>线程试图得到一个锁,而该锁正被其他线程持有;
- 4>线程在等待某个触发条件;