CPU的基本执行单位数是线程
有进程才有线程
每个进程里至少有一个线程
每个进程内可以有多个线程
分时调度模型:线程轮流使用cpu,平均分配时间,如果没进行完等下次分配,如果进行完了还有时间就等待
抢占式:按优先级大小来使用cpu,如果优先级相同就随机选择线程执行
同一时刻,cpu只能执行一个线程
但是多线程提高了“程序运行速度” (假的,提高了cpu的使用效率)
同时能执行多个线程,但是同一时刻只能执行一个线程
由于多个线程之间cpu使用切换贼快,用户无法肉眼区分,但是同一时刻确实是只执行了一个线程
虽然原理是提高运行效率,但是我们在开发时就可以认为是多个线程同时执行,然后相互抢夺cpu的使用权
当java执行main的时候就是在执行一个叫main的线程
可以在main方法执行时开启多个线程,然后多个线程同时执行相互抢夺cpu
Thread类。java.lang包下的一个常用类,每个Thread类的对象就代表一个处于某种状态(正在使用,没有在使用等)的线程
开启线程的步骤:
方式一
1.指定线程执行目标:定义一个Thread类的子类然后重写run方法,将相关逻辑实现
run(),线程要执行的业务逻辑方法,相当于该线程的main方法
2.创建自定义的线程子类对象
3.开启线程
start() 是线程开始执行
getName()获取线程名称,是线程类的方法
setName()指定线程名称
Thread currentThread() 获取当前线程对象
注意:如果创建了Thread的引用并传给了Thread的currentThread方法,就会执行Thread的线程,因为这个是父线程
方式二:
(频繁使用)
1.指定线程执行目标:
使用Runnable接口,他的对象就是线程要执行的目标
定义Runnable线程执行目标子类,重写run方法,指定目标逻辑
2.通过指定线程执行目标的构造方法创建线程本身的对象
public Thread(Runnable target)
所以现在执行的不是当前对象,而是传进来的Runnable对象,也可以说是产生的Runnable目标
对象:
1.线程执行目标对象
2.通过线程执行目标创建线程对象
3.开启线程的动作(start方法)
两种方式的区别
方式一简单粗暴,但是由于是创建类继承Thread类,Java的单继承使得该类无法继承其他业务逻辑类,但是产生的对象的内容互不干扰
方式二更容易共享数据,因为这个类只创建了一歌对象然后被父线程对象调用后面产生的该线程的对象使用的都是MyRunnable中的那个成员,所以实现了成员内容共享
并且因为是实现了接口所以能继承其他类
总结:方式一试类继承了Thread类,所以不能实现其他业务逻辑类,并且是因为创建对象的时候是分开创建点的,不同对象之间互不影响
方式二是实现类,可以继承其他的业务逻辑类,而且创建对象之后,要被Thread用构造方法调用才会出现线程,对象创建一次然后被多个Thread调用,可以被多个类共享对象的内容,使代码的复用性更高,所以这个是频繁使用的方式
线程的四个基本周期:新建状态,可运行状态,运行状态,死亡状态
线程池:将线程放入线程池中可以反复利用线程,避免了线程释放的时候的资源浪费
Executors:newFixedThreadPool 产生一个线程数量为nThreads的线程池
Future<?> submit(Runnable task)接收一个Runnable,执行该线程执行目标,线程池执行多少个目标就执行多少次提交
线程池没有销毁的时候就会一直运行
线程如果超过了nThreads的话不会报错
Thread的sleep(long milis)方法:线程睡眠,使线程延时处理
同级线程在执行过程中可以乱抢!
哪怕是第一个线程语句块没有执行完,第二个线程也可以抢,而第一个线程就停住了,直接开始第二个线程的操作
线程出现安全隐患的原因:操作共享数据,一次完整的操作过程不应该分割执行,即该完整操作过程操作期间,不应该被其他线程抢占cpu
synchronized(锁对象){}
使用同步代码块的时候。使用synchronized的时候,并不在意创建的对象的类型是什么,只要进行的线程是一个类型就行
只要多个线程使用同一把锁就可以使多个线程操作相同的数据
执行原理:
当一个线程进入synchronized的时候,会获取到这个锁的对象,然后从里面锁上,假如其他线程想要抢cpu就会被锁给拒绝(对象已经有了自己的值),然后synchronized结束了之后其他的才可以进来
如果锁已经被锁上了(同一把锁),那么不管这个执行代码块被复制多少次,也都是安全的
锁的定义是在成员变量的位置
同步代码块:就是用synchronize的锁上的代码块,在当前类的其他位置也有,并且锁是同一把锁
同步方法:用synchronized在方法返回值之前修饰,这样方法内部,不管有没有同步代码块都是同步的。(うそ)
锁对象其实就是this,因为线程对象传入的对象是同一个Runnable(其实是Runnable实现类)对象,当这个对象被一个进程占用的时候,其他进程无法使用。如果锁用的是this,那么就不用定义锁对象了,同样同步方法也是安全的
如果方法用static修饰,那么锁默认使用该类的字节码文件.class(字节码文件对象),这个时候锁用的不是this,而是类名.class
synchronized要是锁的话要在死循环内锁,要是在死循环外锁了一个死循环,结果是一直执行一个死循环其他进程不进行
生产者和消费者共享资源的原理:
要是两边的类各产生了一个对象,那么这是两个对象,并没有进行数据之间的绑定。
而要是想要让数据绑定同一个数据,就先在主方法里创建一个共同的对象,然后要么就在两个类的方法级别位置创建对象然后用构造方法将对象绑定为同一个,要么就是将对象的引用私有化,只让构造方法引用,这样这两个类内部的对象就是同一个对象,就能实现了数据的绑定
要是多行的话就用锁锁住,要不会出现安全问题
等待唤醒的机制:
因为想要让某些生产者和消费者交替执行,所以需要一个标志位,当标志位为某个值的时候就执行某个类。
规则:true有数据,生产者等待,false无数据,消费者等待
方法:
Object的方法:
wait() 等待
notify() 唤醒
先判断flag值,如果满足某个条件就wait(),然后在执行完毕之后将falg改变,用notify唤醒另一个类,然后交替
sleep由于线程会苏醒,所以不会释放锁
wait会释放锁,然后等待其他线程唤醒该线程,然后接着wait下面的代码执行
线程死锁- -啥