Java的线程

Java的线程实现

1.线程,进程和管程

1.1线程(Thread)

  • 定义:线程是操作系统中能够独立运行的最小单位,是进程的一个执行分支。一个进程可以包含多个线程,它们共享同一进程的资源(如内存和文件句柄)。
  • 特点
    • 线程之间的创建和销毁开销较小。
    • 线程间共享内存,通信较为高效,但也容易引发竞争条件和数据不一致问题。

1.2进程(Process)

  • 定义:进程是程序在计算机上运行的实例,它拥有自己的内存空间和资源。进程之间是相互独立的,通常通过进程间通信(IPC)进行数据交换。
  • 特点
    • 进程有自己的地址空间,线程间不共享内存。
    • 进程的创建和销毁开销较大,但提供更好的隔离性和稳定性。

1.3管程(Monitor)

  • 定义:管程是一种高层次的同步机制,用于控制对共享资源的访问。它将共享资源的访问和管理封装在一个对象中,并提供互斥访问。
  • 特点
    • 管程通常包括一个互斥锁和一些条件变量。
    • 通过管程,可以避免线程间的竞争条件,简化线程同步的复杂性。

2.用户线程和守护线程

  • 用户线程:系统的工作线程,会完成这个程序需要完成的业务操作。

  • 守护线程:服务进程,没有服务对象就没有必要继续运行下去了,如果用户线程全部结束,意味着程序需要完成的业务操作已经结束,系统可以退出,当只剩下守护线程时,Java虚拟机会自动退出。

3.并发和并行

3.1并发(Concurrency)

  • 定义:并发是指多个任务在同一时间段内进行,不一定是同时执行的。任务可能在共享的时间片上交替运行。
  • 特点
    • 任务之间可以相互影响,可以在同一时间片上切换。
    • 并发的实现可以通过多线程、异步编程等方式。
    • 适用于需要同时处理多个任务的场景,但不一定能提高程序的执行速度。

3.2并行(Parallelism)

  • 定义:并行是指多个任务在同一时刻同时执行。通常是在多核处理器上,多个任务可以同时在不同的核心上运行。
  • 特点
    • 任务是独立的,可以同时进行,不需要相互干扰。
    • 并行通常涉及到对任务的划分,能够有效利用多核处理器的资源。
    • 适用于计算密集型任务,可以显著提高程序的执行速度。

4.创建线程的方式

4.1. 继承Thread

通过继承Thread类并重写其run()方法来创建线程。然后实例化该类并调用start()方法启动线程。

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread is running.");
    }
}

MyThread thread = new MyThread();
thread.start();

4.2. 实现Runnable接口

通过实现Runnable接口并重写其run()方法,然后将该实现传递给Thread对象,再调用start()方法启动线程。

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Thread is running.");
    }
}

Thread thread = new Thread(new MyRunnable());
thread.start();

5.线程的状态及转换

Java中的线程状态主要有以下几种,每种状态之间可以通过特定的操作进行转换:

5.1 新建状态(New)

  • 状态描述:线程被创建但尚未启动。
  • 转换:通过调用start()方法将线程从新建状态转换为就绪状态。

5.2 就绪状态(Runnable)

  • 状态描述:线程在此状态中准备执行,但可能因为操作系统调度等原因未被执行。
  • 转换:线程可能因为被调用start()而进入此状态,随后调度器决定何时将其转换为运行状态。

5.3 运行状态(Blocked)

  • 状态描述:线程正在执行其任务。
  • 转换:线程会在就绪状态中被调度到运行状态。运行状态可以因时间片耗尽或调用yield()等返回就绪状态。

5.4 阻塞状态(Blocked)

  • 状态描述:线程因等待锁而被阻塞。
  • 转换:当线程尝试获取一个已被其他线程占用的锁时,进入此状态。当锁可用时,线程会转回就绪状态。

5.5 等待状态(Waiting)

  • 状态描述:线程在此状态中等待其他线程的特定动作(如notify()notifyAll())。
  • 转换:线程可以通过调用wait()join()LockSupport.park()进入此状态。当被其他线程唤醒时,会转回就绪状态。

5.6 超时等待状态(Timed Waiting)

  • 状态描述:线程在此状态中等待一段时间。
  • 转换:通过调用带有超时参数的方法(如sleep(milliseconds)wait(milliseconds)等)进入此状态。当时间到达后,线程会转回就绪状态。

5.7 终止状态(Terminated)

  • 状态描述:线程已经完成执行或因异常退出。
  • 转换:当run()方法执行完毕或线程异常抛出后,线程会进入此状态。

5.8状态转换总结

  • 新建 → 就绪:调用start()
  • 就绪 → 运行:线程调度
  • 运行 → 就绪:时间片耗尽、调用yield()
  • 运行 → 阻塞:等待锁
  • 运行 → 等待:调用wait()join()
  • 等待 → 就绪:被唤醒
  • 超时等待 → 就绪:时间到达
  • 运行 → 终止:run()结束或抛出异常

6.线程的锁

在 Java 中,synchronized 加锁主要有以下几种情况:

6.1 实例方法加锁:在方法声明上使用 synchronized 关键字,锁住当前对象实例(this)。同一时刻,只有一个线程能够执行该实例的方法。

  • Runnable创建线程时,使用实例方法加锁

    
    /**
     * 同步方法:操作共享数据的代码在一个方法中,可以声明为同步方法
     */
    public class TestRunnable {
        public static void main(String[] args) {
    
            Runnable runnable = new Runnable() {
    
                private int num = 1000;
    
                @Override
                public void run() {
                    while (true) {
                        this.show();
                    }
                }
    
                /**
                 * 同步方法,锁为当前调用者this
                 */
                public synchronized void show() {
                    try {
                        Thread.sleep(20);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                    if (num > 0) {
                        num--;
                        System.out.println(Thread.currentThread().getName() + "->" + num);
                    }
                }
    
            };
    
            for (int i = 0; i < 10; i++) {
                new Thread(runnable, "T" + i).start();
            }
    
        }
    }
    
  • 继承Thread类创建线程时,使用实例方法加锁

    
    /**
     *
     */
    public class TestThread {
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new MyThread("T"+i).start();
            }
        }
    }
    
    
    class MyThread extends Thread {
        public MyThread(String name) {
            super(name);
        }
    
        @Override
        public void run() {
            while (true) {
                test();
            }
        }
    
        private static int num = 1000;
    
        // 静态方法synchronized,同步监视器为当前类
        private static synchronized void test() {
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            if (num > 0) {
                num--;
                System.out.println(Thread.currentThread().getName() + "->" + num);
            }
        }
    }
    
    

6.2 静态方法加锁:在静态方法上使用 synchronized,锁住类的 Class 对象。这意味着同一时刻,只有一个线程能够执行该类的静态方法。

public static synchronized void staticMethod() {
    // 方法体
}

6.3 代码块加锁:在需要加锁的代码块上使用 synchronized,可以指定锁对象。锁定特定对象可以提高并发性。

  • 继承Thread类创建线程时,使用同步代码块加锁

    
    /**
     * 同步代码块 解决 继承Thread的线程安全问题
     */
    public class TestThread {
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                new MyThread("T"+i).start();
            }
        }
    
    }
    
    
    
    class MyThread extends Thread {
    
        private static Integer i = 1000;
        //同步监视器,锁
        private static Object object = new Object();
    
        public MyThread(String name) {
            super(name);
        }
    
        @Override
        public void run() {
            while(true) {
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
    
                synchronized(object) {
                    if (i > 0) {
                        i--;
                        System.out.println(Thread.currentThread().getName() +"->" + i);
                    } else {
                        break;
                    }
                }
            }
        }
    }
    
    
  • Runnable创建线程时,使用同步代码块加锁

    
    /**
     *  同步代码块 解决 implements Runnable的线程安全问题
     *
     */
    public class TestRunnable {
        public static void main(String[] args) {
            Target target = new Target();
            for (int i = 0; i < 10; i++) {
                new Thread(target, "T"+i).start();
            }
    
        }
    
    }
    
    
    class Target implements Runnable {
    
        private Integer i = 1000;
    
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (this) {
                    if (i > 0) {
                        i--;
                        System.out.println(Thread.currentThread().getName() + "->" + i);
                    } else {
                        break;
                    }
                }
    
            }
    
        }
    
    
    
    }
    
    

6.4 类锁:对一个类的 Class 对象加锁,锁为当前类的class对象(.class),通常用于静态代码块。

public static void method() {
    synchronized (YourClass.class) {
        // 需要加锁的代码块
    }
}

Java的线程
https://blog.liuzijian.com/post/7e9cf814-2856-46b9-b30d-7a77f7090b04.html
作者
Liu Zijian
发布于
2022年5月1日
更新于
2024年10月20日