线程基础概念

77 阅读5分钟

1. 并发,并行,线程,进程

a. 并发 同一时间段内执行  例如:在10分钟内同时有十个人在一窗口买票。      
b. 并行 同一时间执行 例如:在94750秒有十个窗口同时在售票。   
c. 进程 一个应用程序在操作系统上运行,例如: QQ      
d. 线程 进程上的最小执行单元,一个进程可以有多个线程,但是最少有个一个线程。     

2. 线程的调度方式

a. 分时调度 : 每个线程执行的时间都是一样的。       
b. 抢占式调度: 哪个线程抢到cpu,哪个线程执行。      
一个cpu在同一时间只能执行一个线程,多个cpu可以在同一时间执行多个线程。        
在同一时间段内,一个cpu频繁切换执行多个线程,在宏观角度看像是同时执行,实际在微观程度上看是串行执行。

3. 创建线程的方式

继承Thread类,重写run方法,创建Thread子类实例,调用start方法。      
实现Runnable接口,重写run方法,创建Runnable接口实现类实例,再用该实例创建Thread类实例,用Thread类实例调用start方法。    
两者不同:     
a. java 是单继承,所以如果继承了Thread 类那么就不能继承其他类。所以实现Runnable接口,可以继承其他类。    
b. Runnable可以更好的共享同一资源
c. 增强代码的健壮性,同一代码可以被多个线程共享,实现代码和线程解耦。

4. 线程常用方法

setPriority() :可以设置线程的优先级,但是不能决定线程执行的先后顺序。     
Thread.sleep(1000);  休眠
Thread.yield(); 让步, 当前线程放弃cpu 的执行权限,重新回到竞争cpu的状态。
thread.join(); 合并, 主线程执行的时候,子线程调用join方法,主线程会等待子线程执行完再执行。
thread.setDaemon(true);  设置守护线程  主线结束,守护线程也会结束,垃圾回收器线程就属于守护线程。

5. 线程的生命周期

image.png

6. 线程的安全问题

线程安全问题是由全局变量和静态变量引起的。
如果每个线程对全局变量和静态变量只是读操作,是不会引起线程安全问题的。
如果多个线程对全局变量和静态变量进行写操作,则需要考虑同步操作,否则会有线程安全问题。        
当线程访问临界资源,打破了原子操作,就会有线程安全问题。

8. 锁

synchronized 在非静态方法中的锁对象是this, 在静态方法中锁对象是该类的.class文件。
同步代码块: synchronized (this){ } 。
同步方法: synchronized 返回值 方法名(形参列表){ }
Locklock() //加锁  
unlock() // 解锁
死锁; 两个线程A,B 和两个锁1,2 ,线程A持有锁1执行,线程B持有锁2执行。
线程A在执行过程中又需要锁2,线程B执行过程中也需要锁1,但是两个线程都没有执行完,无法释放锁,
都会进入到阻塞状态,这就是死锁。

9. 线程通信

为什么会使用线程通信
1.多线程在执行的时候是无序的,获取cpu执行权限是抢占式的。
2.同时又希望在某些时候多线程有规律的操作同一资源
什么是线程通信
多线操作同一资源,并且任务不同,需要线程通信来解决线程间对同一个变量的使用和操作
什么是等待唤醒机制
这个是多线程间的一种协作机制,一个线程执行完相应的操作,进入等待状态。
等待其他线程执行完指定的代码过后再将其唤醒。
线程通信的方法
public final void wait()  释放锁,进⼊等待队列
public final void wait(long timeout) 在超过指定的时间前,释放锁,进⼊等待队列 
public final void notify() 随机唤醒、通知⼀个线程 
public final void notifyAll() 唤醒、通知所有线程
1.wait : 线程不再参与活动,不再参与调度,释放锁,会进入到锁池中。
         因此cpu不存在浪费资源,不会去竞争锁,进入等待状态。
         会等待其他线程执行唤醒操作,才会从锁池中释放出来,重新去获取锁。
         如果获取锁,线程会从等待状态进入到就绪状态
         否则,从 wait set(锁池)又进入到entry set(等待池),线程从waiting状态变成blocked状态。
2.wait(long timeout): 如果在指定的毫秒数还没有被唤醒就会自动唤醒,重新去竞争锁。
3.notify() : 选取锁通知对象锁池中的一个线程释放。
4.notifyAll: 释放锁通知对象的锁池上的全部线程。
注意:
    1. wait方法与notify方法必须要由同一个锁对象调用;
       因为对应的锁对象才能唤醒同一个锁对象调用的wait方法后的线程。
    2. wait方法与notify方法是属于Object类的方法的。
       因为锁对象可以是任意一个对象,任意对象都继承了Object类。
    3. wait方法与notify方法必须要在同步方法或者同步代码块中使用。
       因为必须要通过锁对象调用这两个方法。