一意义
使用多线程的目的是为了提高 CPU 资源的利用效率在单线程应用中程序必须等待当前任务的完成才能继续执行下一项任务, CPU 在等待的时间内就闲置了, 多线程的使用可减少闲置时间
二主线程
当 Java 程序启动时, 会立即开始运行主线程其他所有的线程都是从主线程产生的, 主线程必须是最后才结束执行的线程, 因为它需要执行各种关闭动作可以通过 currentThread 静态方法获取主线程的引用
- class Solution {
- public static void main(String[] args) {
- Thread t = Thread.currentThread();
- System.out.println("Current thread:" + t); //Thread[main,5,main]
- try {
- for (int i = 0; i < 10; i++) {
- System.out.println(i);
- Thread.sleep(500);
- }
- } catch(InterruptedException exc) {
- System.out.println("Current thread interrupted");
- }
- }
- }
默认情况下, 主线程的名字是 main, 优先级是 5,main 也是主线程所属的线程组的名字线程组是将线程作为一个整体来控制状态的数据结构
三创建线程
可通过继承 Thread 类或实现 Runnable 接口来创建线程
实现 Runnable 接口创建线程时, 只需要实现 run 方法, run 方法定义线程的代码, 线程随 run 方法结束而结束在创建了新线程之后, 只有调用 start 方法线程才会运行, 本质上 start 方法执行对 run 方法的调用但是 Runnable 接口只定义了 run 方法, 执行 run 类的代码必须借助 Thread 实例
扩展 Thread 类创建线程时, 必须重写 run 方法或定义 Runnable 接口实例
- class ThreadA extends Thread {
- @Override
- public void run() {
- try {
- for (int i = 0; i < 10; i++) {
- System.out.println(i);
- Thread.sleep(500);
- }
- } catch (InterruptedException exc) {
- System.out.println(getName() + "interrupted");
- }
- }
- ThreadA(String name) {
- super(name);
- start();
- }
- }
- class ThreadB implements Runnable {
- private Thread t;
- @Override
- public void run() {
- try {
- for (int i = 0; i < 10; i++) {
- System.out.println(i);
- Thread.sleep(500);
- }
- } catch (InterruptedException exc) {
- System.out.println(t.getName() + "interrupted");
- }
- }
- ThreadB(String name) {
- t = new Thread(this, name);
- t.start();
- }
- }
- class Solution {
- public static void main(String[] args) {
- ThreadA tA = new ThreadA("A");
- ThreadB tB = new ThreadB("B");
- }
- }
四等待线程
当某个线程需要耗费大量时间运算, 而其他线程又需要等待该线程结束后才能继续获取数据这种情况下可直接调用该线程的 join 方法, 程序会停留于调用 join 方法处直至调用该方法的线程结束才会继续执行后面的代码
- class MyThread extends Thread {@Override public void run() {
- try {
- for (int i = 0; i < 10; i++) {
- System.out.println(i);
- Thread.sleep(500);
- }
- } catch(InterruptedException exc) {
- System.out.println(getName() + "interrupted");
- }
- }
- MyThread(String name) {
- super(name);
- }
- }
- class Solution {
- public static void main(String[] args) {
- MyThread tA = new MyThread("thread A");
- MyThread tB = new MyThread("thread B");
- MyThread tC = new MyThread("thread C");
- try {
- tA.start();
- tA.join(); // 停留直至 tA 结束
- tB.start();
- tB.join(1000); // 停留 1 秒后无论 tB 是否结束都继续执行后面的代码
- tC.start();
- tC.join(); // 停留直至 tC 结束
- } catch(InterruptedException exc) {
- System.out.println("Thread interrupted");
- }
- System.out.println("Thread A is alive" + tA.isAlive());
- System.out.println("Thread B is alive" + tB.isAlive());
- System.out.println("Thread C is alive" + tC.isAlive());
- }
- }
五优先级
Java 为线程指定了优先级, 优先级决定了相对于其他线程会如何处理某个线程优先级是整数, 但绝对值没有意义线程的优先级必须在 MIN_PRIORITY 到 MAX_PRIORITY 之间, 在线程中是作为静态常量定义的优先级决定何时从一个运行的线程切换到下一个, 称为上下文切换发生规则如下:
线程自愿放弃控制线程显式放弃控制权休眠或再 I/O 之前阻塞, 都会导致这种情况的出现发生这种情况时, 会检查其他线程, 并且即将运行的线程中优先级最高的会获得 CPU 资源
线程被优先级更高的线程取代没有放弃控制权的低优先级线程无论在做什么, 都会被高优先级的线程取代只要高优先级线程运行, 就会取代低优先级线程, 称为抢占式多任务处理
具有相同优先级的线程竞争 CPU 资源时, Windows 中会以循环的方式自动获得 CPU 资源, 其他操作系统中优先级相等的线程必须主动放弃控制权其他线程才能运行理论上优先级更高的线程会获得更多的 CPU 时间, 具有相同优先级的线程应当得到相同的 CPU 时间, 但不同环境的多任务方式不同, 为了安全起见, 具有相同优先级的线程应当时不时放弃控制权, 以确保所有线程在非抢占式操作系统中有机会运行
六同步
多线程使程序可以进行异步行为, 所以必须提供一种在需要时强制同步的方法例如当两个线程进行通信并共享某个复杂的数据结构, 当一个线程向数据结构中写入数据时, 必须阻止其他线程向数据结构中写入数据, 否则可能会发生冲突
同步的关键是监视器, 监视器是用作互斥锁的对象在给定时刻只有一个线程可以拥有监视器, 一旦线程进入监视器, 也就是取得锁, 其他所有线程就必须等待, 直至该线程退出监视器, 其他所有尝试进入加锁监视器的线程都会被挂起 Java 中每个类都有自己隐式的监视器, 每当对象的同步方法被调用时, 线程就会自动进入对象的隐式监视器
- class Callme {
- synchronized void call(String msg) { // 同步方法
- System.out.print("[" + msg);
- try {
- Thread.sleep(1000);
- System.out.println("]");
- } catch(InterruptedException exc) {
- System.out.println("Callme interrupted");
- }
- }
- }
- class Caller extends Thread {
- private String msg;
- private Callme target;
- Caller(Callme target, String msg) {
- this.target = target;
- this.msg = msg;
- start();
- }@Override public void run() {
- target.call(msg);
- }
- }
- class Solution {
- public static void main(String[] args) {
- Callme target = new Callme();
- Caller obA = new Caller(target, "Caller A");
- Caller obB = new Caller(target, "Caller B");
- Caller obC = new Caller(target, "Caller C");
- try {
- obA.join();
- obB.join();
- obC.join();
- } catch(InterruptedException exc) {
- System.out.println("Caller interrupted");
- }
- }
- }
但是假设某个类并没有对多线程进行设计, 即类中没有同步方法由于我们并不是该类的创建者, 无法访问其源代码, 也就无法使用上述方法为类中的方法添加 synchronized 修饰符那么可以将需要同步的部分放到 synchronized 代码块中
- class Caller extends Thread {
- private String msg;
- private Callme target;
- Caller(Callme target, String msg) {
- this.target = target;
- this.msg = msg;
- start();
- }
- @Override
- public void run() {
- synchronized (target) {// 同步代码块
- target.call(msg);
- }
- }
- }
来源: http://www.bubuko.com/infodetail-2498759.html