《图解Java多线程设计模式》学习笔记(一)Java线程
一、何谓线程
1. 单线程程序
- 处理流程始终如一条线
- 某一时间点执行的处理只有一个
- 正在执行程序的主体称为线程
2. 多线程程序
- 多个线程组成的程序称为多线程程序
public class MyThread extends Thread{
public void run(){
for (int i = 0; i < 10000; i++) {
System.out.println("Nice!");
}
}
}
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
// 注意要调用start,run方法可以调用,但不会开启新县城
thread.start();
for (int i = 0; i < 10000; i++) {
System.out.println("Good!");
}
}
}
结果是交叉输出
概念:
- 顺序:多个操作依次处理
- 并行:多个操作同时处理
- 并发:将一个操作分成多个部分并允许无序处理
二、线程的启动
- 继承Thread,重写run方法
public class PrintThread extends Thread {
private String message;
PrintThread(String message) {
this.message = message;
}
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println(message);
}
}
}
public class Main {
public static void main(String[] args) {
new PrintThread("Good!").start();
}
}
- 实现Runnable接口,实现run方法
public class Printer implements Runnable {
private String message;
Printer(String message) {
this.message = message;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println(message);
}
}
}
new Thread(new Printer("Nice!")).start();
Thread本身实现了Runnable接口,且有run方法,方法体为空。
三、线程的暂停
一秒打印一个Good!
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println("Good!");
try {
// 当前线程暂停1秒
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
new Thread(new Printer("Nice!")).start();
}
}
容易引起歧义的写法:
public class MyThread extends Thread{
public void run(){
for (int i = 0; i < 10000; i++) {
System.out.println("Nice!");
}
}
}
想让启动的线程暂停一秒
public class Main {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
try {
thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
这么写是不对的,thread.sleep(1000)暂停的并不是thread有关的线程,这句相当于Thread.sleep(1000),调用的其实是Thread的静态方法,暂停的是执行这条语句的线程。想要暂停thread线程要写在run方法里。
四、线程的互斥处理
- 多线程中各个程序独立运行,有时就会操作同一实例。
- 线程间互相竞争而引起的与预期相反的情况成为数据竞争。
- 一个线程正在执行某一部分操作,其他线程就不可再执行这部分操作,叫互斥。
- Java使用syschronized进行互斥处理。
- 加上syschronized的方法称为同步方法,一次只能由一个线程运行。
public class Bank {
private int money;
private String name;
public Bank(int money, String name) {
this.money = money;
this.name = name;
}
public String getName() {
return name;
}
// 存钱
public synchronized void deposit(int m) {
money += m;
}
// 取钱
public synchronized boolean withdraw(int m) {
if (money >= m) {
money -= m;
return true;
} else {
return false;
}
}
}
-
如果有一个线程运行deposit(获得了锁),其他线程无法运行deposit和withdraw,但可以运行getName。
-
每个实例都有一个锁。
-
当前线程是否获取某一对象的锁用Thread.holdLock(obj)确认。
-
synchronized代码块:
public synchronized void method(){
}
等价于
public void method() {
synchronized (this) {
}
}
五、线程的协作
1. 等待队列
- 所有实例都拥有等待队列
- 执行wait方法后,线程会暂停操作进入等待队列
- 线程退出等待队列的条件:
- 有其他线程notify方法唤醒
- 有其他线程notifyAll方法唤醒
- 有其他线程interrutp方法唤醒
- wait方法超时
2. wait()方法
- wait()方法让线程进入等待队列
- 线程必须持有锁才能执行wait()
- 线程进入等待队列后会释放其实例的锁
3. notify()方法
- notify(通知),会取出等待队列中的一个线程
- 线程必须持有锁才能执行notify()
- 执行notify后,被唤醒的线程并不会立即重新运行,因为此时执行notify的线程还有锁
- 等待队列有多个线程时,具体取出哪个取决于运行环境
4. notifyAll()方法
- notifyAll()会取出等待队列中的所有线程
- 线程必须持有锁才能执行notifyAll()
- 被唤醒的线程都处于阻塞状态,只有当执行notifyAll()方法的线程释放锁后,才能有一个线程获得锁,实际运行
5. 总结
- 以上方法都是Object类的方法,不是Thread类的方法
- 若执行方法的线程没有锁,则会抛出异常
六、线程的状态迁移

七、线程相关的其他话题
- 取消线程处理的中断
- interrupt
- isInterrupted
- interrupted
- InterruptedException
- 线程优先级
- setPriority
- getPriority
- 等待线程终止
- join
- 阻塞与被阻塞
- 含义相同,当线程A想执行synchronized方法时,有其他方法已经获取了该实例的锁,则线程A阻塞
八、练习题

- √
- × start
- × run声明在Runnable中,start声明在Thread中
- √
- √ Thread类的实例方法与其他类实例方法没有不同,所以也会被多个线程调用
- × 只暂停调用该方法的线程
- × 只停止要获取同一实例的锁的线程
- × sleep不会进入等待队列,而是wait
- × 调用wait的语句可写在synchronized方法或synchronized代码块中,或者二者调用的方法中
- √

浙公网安备 33010602011771号