1.为什么使用线程?
1、减少资源的开销。通过复用线程,降低创建销毁线程造成的消耗。
2、多个线程并发执行任务,提高系统的响应速度。
3、可以统一的分配,调优和监控线程,提高线程的可管理性。
2.线程的工作区
Java内存模型将内存分为了 主内存和工作内存 。类的状态,也就是类之间共享的变量,是存储在主内存中的,每个线程都有一个自己的工作内存(相当于CPU高级缓冲区,这么做的目的还是在于进一步缩小存储系统与CPU之间速度的差异,提高性能),每次Java线程用到这些主内存中的变量的时候,会读一次主内存中的变量,并让这些内存在自己的工作内存中有一份拷贝,运行自己线程代码的时候,用到这些变量,操作的都是自己工作内存中的那一份。在线程代码执行完毕之后,然后在某个时间点上再将最新的值更新到主内存中去。
这样导致的问题是,如果线程1对某个变量进行了修改,线程2却有可能看不到线程1对共享变量所做的修改。
3.线程的优先级
线程的优先级1-10,默认是5
4.线程的几种状态
1.新建状态:当new操作符创建一个线程时,线程还没开始运行,此时线程处于新建状态,当一个程序处于新建状态时,程序还没开始运行线程中的代码
2.就绪状态:新创建的程序需要调用线程的start()方法,,start()方法创建线程的系统资源,并调度线程的run()方法,当start方法返回后,线程就处于就绪状态。处于就绪状态的线程不一定立刻运行run()方法,要获得cpu时间才能运行
3.运行状态:当获得cpu时间后线程才开始运行run()方法,真正的进入运行状态
4.阻塞状态:阻塞状态是正在运行的线程没有结束运行,而是暂时让出cpu时间,其他线程就可以获得cpu时间,进入运行状态(阻塞状态的原因:1,线程调用sleep,2,I/O上被阻塞,即输入输出操作未完成 ,3,线程试图获得一个锁,而这个锁被其线程所保持着 ,4,线程在等待某个触发条件)
5,死亡状态:需要用isAlive方法()判断线程是否活着,如果运行或者被阻塞,则返回true,如果死亡,或者是new状态,则返回false(线程死亡的原因:1,run()方法正常退出死亡 ,2,一个未捕获的异常使其猝死)
5.线程的start()方法和run()方法的区别
start()方法是有 synchronized 修饰的 ,main方法中可以多次调run()方法,而start()方法只能调用一次,多次调用start方法会抛出异常(java.lang.IllegalThreadStateException)
6.wait()和sleep()区别
1.sleep是Thread类中的方法,wait是Object类中的方法
2.sleep不会释放cpu资源,而wait会释放cpu资源
3.wait只能在同步块中运行,而sleep可以在任何地方使用,但是需要抛出异常( sleep这个方法在源码中是会判断当前进程是否处于interrupted状态的,也符合之前所所说的interrupt方法需要调用的线程)
4.wait需要用notify和notifyAll唤醒,sleep不需要,到时间继续执行,假如sleep(1000),和wait(1000),在时间过后,sleep一定执行,而wait不一定,因为wait还需要cpu分配到运行时间才可以
5.sleep是静态的,也就是谁调用谁sleep,对于一个实例t,t.sleep是无效的,假如是主方法中这样使用的这个操作会使主方法sleep
7.thread中,interrupt,interrupted,isInterrupted的区别
interrupt方法用于中断线程。调用该方法的线程的状态为将被置为"中断"状态。注意:线程中断仅仅是置线程的中断状态位,不会停止线程。需要用户自己去监视线程的状态为并做处理。
interrupted()是静态方法:内部实现是调用的当前线程的isInterrupted(),并且会重置当前线程的中断状态,如果当前是中断状态,则返回true,且中断状态会被清除
isInterrupted()是实例方法,是调用该方法的对象所表示的那个线程的isInterrupted(),不会重置当前线程的中断状态
8.线程安全问题:
如果你的代码在多线程下执行和在单线程下执行永远都能获得一样的结果,那么你的代码就是线程安全的。
线程的安全等级:1,不可变,例如String,Intiger,Long这些类,都是final类型,任何一个线程都改变不了他们的值,除非新创建一个,因此,不需要任何同步手段就可以在多线程环境下使用
2,绝对线程安全, 不管运行时环境如何,调用者都不需要额外的同步措施。要做到这一点通常需要付出许多额外的代价,Java中标注自己是线程安全的类,实际上绝大多数都不是线程安全的,不过绝对线程安全的类,Java中也有,比方说CopyOnWriteArrayList、CopyOnWriteArraySet
3,相对线程安全, 相对线程安全也就是我们通常意义上所说的线程安全,像Vector这种,add、remove方法都是原子操作,不会被打断,但也仅限于此,如果有个线程在遍历某个Vector、有个线程同时在add这个Vector,99%的情况下都会出现ConcurrentModificationException,也就是fail-fast机制
4.线程不安全的,例如HashMap,ArrayList
9.实现线程安全的方式
1.sychronized锁( 在几乎无竞争的条件下, 会使用偏向锁, 在轻度竞争的条件下, 会由偏向锁升级为轻量级锁, 在重度竞争的情况下, 会升级到重量级锁。)
2.volatile锁(只能保证数据可读性,不能保证数字原子性)
3.jdk提供的原子类 java.util.concurrent.atomic 包下
4.使用lock锁(共享锁,排他锁)
10.join https://blog.youkuaiyun.com/qq_39305860/article/details/79722918
join实际上就是把并行变为串行,在线程1 中调用线程2 的join方法,两个线程就会串行执行
底层代码,实际上是调用wait方法,线程A中调用线程B的join方法,实际上就是B调用wait方法
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
11.yield
暂停当前线程,并执行其他线程,可能失败,yield让当前线程回到可运行状态,以允许具有相同优先级的线程获得运行机会,yield的目的是让具有相同优先级的线程之间能够适当的轮换执行,但是实际上无法保证yield让步的目的,因为,让步的线程可能会再次被调度或者选中