Java Learn(十六)

线程的基本操作、线程的同步机制

线程的基本操作

static void yield() — 用于使得当前线程让出 CPU 的执行权转而去执行其他线程

static void sleep(long millis) — 用于让当前线程休眠参数指定的毫秒数

static void sleep(long millis , int nanos) — 用于休眠参数指定的毫秒加上纳秒

void interrupt() — 用于打断正在睡眠的线程(了解)

int getPriority() — 用于获取当前线程的优先级

int setPriority() — 用于设置当前线程的优先级
— 优先级高的线程并不一定先执行,只是获取时间片的机会更多一些而已

void join() — 用于等待调用的对象代表的线程终止

void join(long millis) — 用于等待该线程终止的最长时间为参数指定的毫秒

void join(long millis, int nanos) — 用于等待的时间为参数指定的毫秒加纳秒

void setDaemon(boolean on) — 用于将线程设置为守护线程

boolean isDaemon() — 用于判断该线程时候为守护线程
— 当所有非守护线程结束时,守护线程也就随之结束,如:垃圾回收线程

线程的同步机制

基本概念

当多个线程同时访问同一个共享资源时,可能会造成数据的不一致等问题,此时就需要进行线程之间的协调和通信,该机制就叫做线程的同步机制。

解决方案

  1. 由案例可知:当线程一还没有取款结束时,线程就过来取款会导致数据不一致的问题。
  2. 解决方案:让线程一取款结束后在执行线程二即可,也就是将线程的并发改成串行即可。
  3. 建议:串行会导致效率比较低,因此建议能不用则尽量不用。

实现方法

在 Java 语言中使用 synchronized 关键字来保证线程执行的原子性,具体方式如下:

使用同步语句块的方式
synchronized(引用/对象){
编写所有需要锁定的代码块;

}

该用法通常用于锁定具体某一段代码的场合中,尽量减少锁定的范围。

使用同步方法的方式

直接使用 synchronized 关键字修饰整个方法,表示锁定方法的所有代码(常见)

该方法本子上等价于 synchronized(this){} 的方式

实现原理

当启动多个线程后各个线程独立运行,并同时去抢占共享资源,若其中一个线程抢到资源则进行加锁处理,其他线程进入阻塞状态,知道该线程执行完毕所有锁定的代码并释放同步锁后,其他线程才能再次抢占共享资源,抢不到的线程继续阻塞。

只有锁定的代码是被多个线程依次串行执行的,其他地方还是并发执行。

死锁的概念

线程一执行的代码:

void run(){
  synchronized(a){            //持有同步锁 a,等待同步锁 b
    synchronized(b){}
  }
}

线程二执行的代码:

void run(){
  synchronized(b){            //持有同步锁 b,等待同步锁 a
    synchronized(a){}
  }
}

经验分享:

在以后的开发中尽量不要使用同步语句块的嵌套使用,避免死锁的发生。

Object类中的方法

void wait() — 用于让当前正在执行的线程进入等待状态,直到其他线程调用 notify() 或 notifyAll() 方法为止

void wait(long timeout) — 让线程进入等待状态,直到其他线程调用上述方法或参数指定的时间到了为止,然后接触阻塞状态

void notify() — 用于唤醒等待的任意一个线程

void notifyAll() — 用于唤醒等待的所有线程