多线程中涉及的基本概念
- 同步-Synchronize:同步任务一旦开始执行就必须等待任务返回结果回来才能继续执行下一个任务。
- 异步-Asynchronize:异步任务开始执行,方法调用立即返回无论结果是否产生,(有点类似异步消息机制,只管消息发出,而不必关系消息是否被接收),就可以继续只需执行下一个任务。
- 串行:多个线程排队执行
- 并发:多个线程不停切换运行。单核cpu的多线程一定是并发
- 并行:真正的同时发生,采用多核cpu实现的线程同时运行
- 临界区:并行任务中的共享资源,同一时间只能被唯一线程使用,其他线程等待
- 阻塞:当临界区被某一线程占用时候,其他线程如果需要该临界区资源只能继续等待(线程挂起)该线程对临界区资源的释放,此时其他线程就属于阻塞状态
- 非阻塞:与阻塞相反,没有其它线程对临界区的资源占用,线程可以立即获得临界区资源的使用
- 饥饿:由于优先级,或其他多种原因造成某些线程始终获取不到资源,从而闲置无法运行的状态
- 死锁: 资源相互占用造成一种谁也得不到,谁也不愿意释放的状态
- 活锁:资源在连个线程之间跳动,造成谁也无法同时得到资源正常运行的状态
- 公平锁:不管线程的优先级如何,对于资源的访问都必须满足先来后到
- 非公平锁:让优先级高的线程对资源先行访问
- 乐观锁:乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量
- 悲观锁:具有强烈的独占和排他特性,总是假设最坏的情况,每次读取数据的时候都默认其他线程会更改数据
- 互斥锁: 该锁同一时间只能被一个线程得到,不能被多个线程同时得到
- 原子性:线程操作不可被其他线程所干扰。
例如:某一个线程在对某一64位数据进行书写时,刚写玩前32位数据,后来被其他线程强行中断,继续对后32位数据库进行书写,从而破坏了并发的原子性,书写的数据读出来也是紊乱的。
这种情况在具有原子性的操作中是不会发生的 - 可见性:线程之间的操作必须是对系统可见的。
例如:线程A修改了全局变量x=1,然而如果此操作是不可见的话,线程B此时读取全局变量的X的值,并不会读取到x=1,而是x变量的老数据,因为线程B不知道线程A修改了变量x的值。
有序性:线程级别的指令是有序的,而超过线程级别的指令在可能是无序的。
Java 线程状态
六种线程状态:New,Runnable,Waiting,Timed_Waiting,Blocked,Terminated
- New:创建出一个Thread对象,还未调用start()方法
- Runnable:java线程中将Ready和Running两种状态笼统的称作Runnable状态,此时线程已经调用了start()方法,等待线程调度选中,获得CPU的使用权,此时线程处于Ready状态,待获取到CPU时间片后线程进入Running状态
- Waiting:线程等待状态,需要等待其它线程唤醒(notify)或其它线程中断(interrupt),否则将一直处于Waiting状态
- Timed_Waiting:不同于Waiting状态,Timed_Waiting可以在指定时间后自行返回,不需要其他线程唤醒
- Blocked:表示线程等待锁时候的状态
- Teminated:表示线程执行完毕
关于线程的方法比较:
- start():启动线程
- t.yield():一定是当前线程调用此方法,当前线程放弃获取的CPU时间片,但不释放锁资源,只是由Running状态转变为Ready状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但是这并不能保证相同优先级的线程会轮流执行,因为实际情况中,让步的线程还有可能被线程调度程序再一次选中。另外,Thread.yield()并不会导致线程阻塞,该方法与sleep()类似,只是不能由用户指定暂停的时间。
- t.sleep():一定是当前线程调用此方法,Runnable状态的线程调用此方法进入Timed_waiting状态,但不会释放锁资源,等待时间到后,线程自动苏醒进入Runnable状态。作用:让其他线程执行的最佳方式。
- t.join() / t.join(long):当前线程里面调用其他线程的join()或join(long)方法,当前线程就会进入Waiting或Timed_Waiting状态,当前线程不会释放已经持有的对象锁,待其他线程执行完毕或者(long)时间到,当前线程进入Runnable状态 ,作用:相当于其他线程插队,另外join()方法内部其实是用wait()方法实现的
- obj.wait() / obj.wait(long):当前线程调用对象的wait()方法,当前线程释放对象锁,进入等待队列,依靠其他线程唤醒notify()/notifyAll()或(long)等待时间到自动唤醒。
- obj.notify() / obj.notifyAll():notify()唤醒在此对象监视器上处于等待状态的单个线程,选择是任意的,notifyAll()唤醒在此对象监视器上所有的处于等待状态的线程
Join() 理解
public class Test {
public static void main(String[] args) throws InterruptedException {
MyThread myThread =new MyThread();
myThread.start();
System.out.println("Main running first...");
}
static class MyThread extends Thread {
@Override
public void run() {
try {
System.out.println("MyThread running second...");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Result:
Main running first...
MyThread running Second...
public class Test {
public static void main(String[] args) throws InterruptedException {
MyThread myThread =new MyThread();
myThread.start();
//MyThread插队,并且由于join没有设置时间,所以Main线程需要等待MyThrad执行结束后才能继续执行
myThread.join();
System.out.println("Main running second...");
}
static class MyThread extends Thread {
@Override
public void run() {
try {
Thread.sleep(5000);
System.out.println("MyThread running first...");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Result:
MyThread running first...
Main running second...
public class Test {
public static void main(String[] args) throws InterruptedException {
MyThread myThread =new MyThread();
myThread.start();
//MyThread插队,并且join()设置等待时间为2000毫秒,而MyThread休眠时间为5000毫秒,所以Main线程先于MyThread重新进入Runnable状态,并执行相关代码
myThread.join(2000);
System.out.println("Main running first...");
}
static class MyThread extends Thread {
@Override
public void run() {
try {
Thread.sleep(5000);
System.out.println("MyThread running second...");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Result:
Main running first...
MyThread running second...