1、同步和异步
同步:同步方法一旦开始以后,调用者须等到方法调用返回才能进行后续操作
异步:异步方法通常会在另一个线程里执行,中途不会阻碍调用者的后续操作
2、并发和并行
并发:注重多个任务交替执行,而这些任务可能依然都是串行的
并行:多个任务是严格的同时执行的
3、临界区,阻塞
里面是共享资源,即多个线程公共的资源。
每一次只能有一个线程访问临界区,其他线程会被挂起,这种情况即称其他线程被阻塞。
4、死锁,饥饿,活锁
死锁:线程之间互相占用的对方的资源且拒绝释放,造成了线程都无法正常执行
饥饿:由于一些原因一直得不到该得到的资源,比如优先级太低的线程一直被高优先级线程插队,拿不到资源
活锁:资源拥有权在线程间不断跳动,导致都无法正常执行
多线程的三个性质
原子性:操作不能被中断(比如用32位系统对long型数据读写操作,long类型是64位的)
可见性:一个线程修改了某共享变量的值时,其他线程需要立刻知道
有序性:程序在执行时可能进行指令重排,在多线程情况下程序执行可能乱序
指令重排
在硬件中的指令,比如汇编指令,采用的是流水线型的工作原理,即第二条指令开始时第一条指令可能还没有执行完。使用该工作模式会大大提高性能,因为指令间的等待时间减少了许多,然而这样的特性也会出现一些空隙,比如:
LW R1,B
LW R2,C
ADD R3,R1,R2
将B加载入R1寄存器,C加载入R2寄存器,R1和R2的和存入R3寄存器。
如果在执行第三条指令时前两条赋值语句还没执行完,那么它就必须等待它们全部执行完成。中间就产生了“等待”的空隙。
为了解决这类问题,可能会将可以置换顺序或者调换语义的指令进行重排,从而减少这种空隙,而指令重排在多线程的情况下就可能产生前面提到的执行乱序。
基础操作--方法篇
1、新建线程
Thread t1 = new Thread();
t1.start();
start()方法会新建一个线程并执行该线程的run()方法。
而直接调用run()方法只能作为普通方法使用,这是这两种方式的区别。
通过继承Thread类或Runnable类并重写run()方法我们可以规定该线程的任务是什么。
2、终止线程
stop()方法可以终止线程,但是它太暴力,线程中途被终止可能导致数据不一致(因为保持数据一致的锁都被释放了)等问题。
要避免暴力应该自己设置线程的终止时间,比如添加一个应该被终止的标志符,并在合适的位置让线程终止。
3、线程中断
通过interrupt()方法来通知线程应该被中断了。可以使用isInterrupted()方法来判断是否被中断并写好终止的逻辑代码。
4、等待wait()和通知notify()
调用wait()后,线程会转为等待状态,直到其他线程调用notify()方法。 注:notify()是随机唤醒某一等待状态的线程
唤醒所有线程的话使用notifyAll()方法.
5、挂起suspend()和继续执行resume()
这两个方法已经不推荐使用,因为如果先执行resume()方法,被挂起的线程很难继续执行,并且不会释放锁。
6、等待线程结束join()和谦让yeild()
如果一个线程的输入非常依赖于其他一些线程的输出等,那么需要让它等到其他线程有输出才能继续执行。
join()方法是无限期地等待,而join(Long mills)表示等到这么长时间之后,它就会等不及了继续执行。
而yeild()方法是让线程让出CPU,然后它们会再次参与CPU资源的争夺。
关于关键字等内容在后续博客中更新,下一篇关于自己写一个简单的springboot分布式锁博客在准备中。