《图解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阻塞
八、练习题

  1. × start
  2. × run声明在Runnable中,start声明在Thread中
  3. √ Thread类的实例方法与其他类实例方法没有不同,所以也会被多个线程调用
  4. × 只暂停调用该方法的线程
  5. × 只停止要获取同一实例的锁的线程
  6. × sleep不会进入等待队列,而是wait
  7. × 调用wait的语句可写在synchronized方法或synchronized代码块中,或者二者调用的方法中
posted @ 2021-02-23 10:29  当代艺术家  阅读(102)  评论(0)    收藏  举报