Thread

Thread类

进程与线程

进程

程序是静止的,运行中的程序是进程

特征:

  • 动态性:动态的占用内存、CPU、网络…
  • 独立性:各进程相互独立,有自己的内存空间
  • 并发性:如果 CPU 单核,一个时刻只能一个进程被执行。CPU 会分时轮询切换依次服务进程,因为切换速度快,感受就是各个进程都在同时执行

并行:

  • 一个时刻同时有多个在执行

线程

  • 线程属于进程,一个进程至少一个线程
  • 线程开销相对于进程少
  • 线程也有并发性

乱七八糟:

StringBuilder 是不安全的,而 StringBuffer 是安全的,但是淘汰了,性能差

SringBuffer的 append 方法,为了实现同步,很多方法使用 synchronized 修饰

底层字符串都是由 char 数组

StringBuilder和StringBuffer的append(String str)方法都是通getChars方法来实现字符串拼接的

关于变量不可见

原因

  • JMM( Java Memory Model ),所有共享变量存放于主内存,每个线程有工作内存,保留了变量的副本,所有线程所有的操作都在操作副本

解决方法

  • volatile
    • 子线程修改了变量后,当变量写入主内存,会失效其他线程的此变量的副本,其他线程操作此变量时,会从主内存 copy 最新的值,使得变量可见,区分 synchronized,两者不同
  • synchronized
    • 线程拿到锁,清空工作内存,从主内存 copy 共享变量到工作内存,变量如果有修改,会将修改的变量刷新回主内存,释放锁

线程状态

共 6 种

1
2
3
4
5
6
7
8
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}

image-20201225153203732

注意事项

sleep、wait、yield

  • sleep 使当前线程让出了 CPU,但是,当前线程仍然持有它所获得的监视器锁
  • wait 同时让出 CPU 资源和监视器锁

wait(0) 表示无限等待

sleep(0) 会让其他线程有机会优先执行,调用 sleep(0) 的线程的状态可能变成 TIMED_WAITING

yield 指当前线程愿意让出 CPU,但是对于 CPU 这只是一个建议,要不要让看厂商调度

  • yield 顶多会让当前线程状态从 RUNNING 变成 READY,并不会退出 RUNNABLE

join

注意 join 方法有加 synchronized,说明执行某个线程实例的 join 方法必须拿到对象锁( this 锁 ),即 myThread 对象所关联的监视器对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// join() 就是 join(0)
public final void join() throws InterruptedException {
join(0);
}
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");
}

// 如果 millis 为 0,就无限等待
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + ": main 开始");

Thread myThread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ": 开始");
System.out.println(Thread.currentThread().getName() + ": 要睡觉了");
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + ": 醒了");
System.out.println(Thread.currentThread().getName() + ": 结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "myThread");

try {
myThread.start();
System.out.println(Thread.currentThread().getName() + ": 我要等下面的线程执行完我才继续");
myThread.join();
// myThread.join(500);
System.out.println(Thread.currentThread().getName() + ": 上面的线程执行完了");
System.out.println(Thread.currentThread().getName() + ": 退出");
} catch (InterruptedException e) {
e.printStackTrace();
}

}

main 方法中调用了 myThread.join(),会无限等待,等到 myThread 执行结束( 线程结束默认会执行 this.notifyAll ),main 线程会被唤醒,继续执行,发现 myThread 的 isAlive 是 false,join 执行完毕

1
2
3
4
5
6
7
8
main: main 开始
main: 我要等下面的线程执行完我才继续
myThread: 开始
myThread: 要睡觉了
myThread: 醒了
myThread: 结束
main: 上面的线程执行完了
main: 退出

而如果 main 方法中调用了 myThread.join(500),main 只会等待 0.5 s,发现 myThread 还是活着,那就 break

1
2
3
4
5
6
7
8
main: main 开始
main: 我要等下面的线程执行完我才继续
myThread: 开始
myThread: 要睡觉了
main: 上面的线程执行完了
main: 退出
myThread: 醒了
myThread: 结束

参考文章