frankltf

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(){
		
	}
  • 同步方法(非静态)是以当前对象作为锁。
    • 如果用非静态同步方法加锁,一般独立出一个对象池,将同步方法放在对象池内,然后多线程对象每次从同一个对象池取资源。
  • 同步方法(静态) 是以类作为锁。
线程生命周期

enter image description here
enter image description here

  • enter image description here
  • 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>线程在等待某个触发条件;
参考

分类:

技术点:

相关文章: