Java线程的基本操作(设置和获取、sleep、interrupt、join、yield、daemon、线程状态总结)

作者简介:练习时长两年半的Java up主
个人主页:程序员老茶
ps:点赞是免费的,却可以让写博客的作者开兴好久好久
系列专栏:Java全栈,计算机系列(火速更新中)
格言:种一棵树最好的时间是十年前,其次是现在
动动小手,点个关注不迷路,感谢宝子们一键三连

目录

  • 课程名:Java
    • 内容/作用:知识点/设计/实验/作业/练习
    • 学习:Java线程的基本操作
  • Java 线程名称的设置和获取
    • 1. 线程名称的设置
    • 2. 线程名称的获取
    • 3. 注意事项
    • 4. 总结
  • Java线程的sleep操作详解
    • 1. 语法
    • 2. 示例代码
    • 3. 注意事项
  • Java 线程中断操作(interrupt)详解
    • 1. 什么是线程中断?
    • 2. 如何发送线程中断信号?
      • 2.1 捕获中断异常
      • 2.2 终止线程执行
  • Java线程的join操作详解
    • 1. join()方法的作用
    • 2. join()方法的用法
      • 2.1 无参数的join()
      • 2.2 带有参数的join()
  • Java线程的yield操作
    • 1. yield操作的原理
    • 2. yield操作的方法
      • 2.1 使用Object类的wait()方法
      • 2.2 使用Thread类的yield()方法
    • 3. yield操作的影响
  • Java线程的守护线(daemon)程操作
    • 1. 创建守护线程
    • 2. 停止守护线程
  • Java线程状态总结
    • 1. 新建(NEW)
    • 2. 可运行(RUNNABLE)
    • 3. 阻塞(BLOCKED)
    • 4. 等待(WAITING)

课程名:Java

内容/作用:知识点/设计/实验/作业/练习

学习:Java线程的基本操作

Java 线程名称的设置和获取

本文将介绍Java中线程的基本操作,包括线程名称的设置和获取。线程是程序执行的最小单元,通过多线程可以提高程序的执行效率。本文将详细介绍如何设置和获取线程名称,并提供相应的代码示例。

1. 线程名称的设置

在Java中,可以通过Thread类的setName()方法为线程设置名称。示例代码如下:

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程运行中...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程名称:" + getName());
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.setName("MyThread-1");
        myThread.start();
    }
}

输出结果:

线程运行中...
线程名称:MyThread-1

可以看到,我们成功地为线程设置了名称"MyThread-1"。

2. 线程名称的获取

在Java中,可以通过Thread类的getName()方法获取线程的名称。示例代码如下:

public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程运行中...");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程名称:" + getName());
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.setName("MyThread-1");
        myThread.start();
    }
}

输出结果:

线程运行中...
线程名称:MyThread-1

可以看到,我们成功地获取了线程的名称"MyThread-1"。

3. 注意事项

  • 同一个线程对象不能被重复创建,但可以调用setName()方法为其设置不同的名称。
  • 同一个线程对象可以被多个线程共享,因此不同线程之间可以通过getName()方法获取到相同的线程名称。
  • 在多线程编程中,合理地设置和获取线程名称有助于提高程序的可读性和可维护性。

4. 总结

本文介绍了Java中线程的基本操作,包括线程名称的设置和获取。通过Thread类的setName()getName()方法,我们可以方便地为线程设置和获取名称。在实际开发中,合理地使用线程名称可以帮助我们更好地理解和调试程序。

Java线程的sleep操作详解

在Java中,我们可以使用Thread.sleep()方法让当前线程暂停执行一段时间。这个方法属于Thread类,因此所有继承自Thread类的子类都可以使用它。本文将详细介绍Thread.sleep()方法的用法,并通过代码示例进行演示。

1. 语法

Thread.sleep(long millis)方法的参数是一个长整型数值,表示线程需要暂停执行的时间,单位为毫秒(ms)。例如,如果你想让线程暂停执行1000毫秒(1秒),可以调用Thread.sleep(1000)

注意:传入负数作为参数时,Thread.sleep()方法会使当前线程无限期地等待,这可能会导致死锁。因此,建议始终传入正数作为参数。

2. 示例代码

下面是一个简单的示例代码,演示了如何使用Thread.sleep()方法:

public class SleepDemo {
    public static void main(String[] args) {
        System.out.println("线程开始执行");
        try {
            // 让线程暂停执行1秒(1000毫秒)
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程恢复执行");
    }
}

运行上述代码,你会看到以下输出:

线程开始执行
线程恢复执行

从输出结果可以看出,线程确实暂停了1秒后才恢复执行。

3. 注意事项

虽然Thread.sleep()方法非常方便,但在实际开发中使用时需要注意以下几点:

  1. 尽量避免使用Thread.sleep()方法,因为它会阻塞当前线程,导致其他任务无法及时执行。如果确实需要暂停线程,可以考虑使用其他同步机制,如wait()/notify()CountDownLatch等。

  2. Thread.sleep()方法可能会抛出InterruptedException异常,因此需要在调用该方法的地方进行异常处理。可以使用try-catch语句来捕获并处理异常。

  3. 在使用Thread.sleep()方法时,尽量传入正数作为参数,以避免线程无限期地等待。

Java 线程中断操作(interrupt)详解

在 Java 中,线程中断是一种协作机制,它允许一个线程通知另一个线程停止执行。这种机制可以帮助我们更好地控制程序的执行流程,避免死锁等问题。本文将详细介绍 Java 中的线程中断操作,并提供详细的解释和代码示例。

1. 什么是线程中断?

线程中断是一种协作机制,当一个线程需要停止执行时,它可以向另一个线程发送一个中断信号。收到中断信号的线程可以选择如何处理这个信号,例如优雅地停止执行或者立即停止。

2. 如何发送线程中断信号?

在 Java 中,我们可以使用 Thread 类的 interrupt() 方法来发送线程中断信号。当一个线程被中断时,它会收到一个中断异常(InterruptedException),我们可以捕获这个异常来实现优雅地停止执行。

2.1 捕获中断异常

要捕获中断异常,我们需要在可能抛出此异常的代码块前添加 try-catch 语句。以下是一个示例:

public class InterruptDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    for (int i = 0; i < 5; i++) {
                        System.out.println("子线程:" + i);
                        Thread.sleep(1000);
                    }
                } catch (InterruptedException e) {
                    System.out.println("子线程被中断");
                }
            }
        });

        thread.start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt(); // 发送中断信号
    }
}

在这个示例中,我们创建了一个子线程,让它每隔一秒钟打印一条消息。然后在主线程中,我们让主线程等待 3 秒后发送中断信号。子线程会在收到中断信号后优雅地停止执行。

2.2 终止线程执行

除了捕获中断异常外,我们还可以直接调用 Thread 类的 stop() 方法来强制终止线程的执行。但是这种方法不推荐使用,因为它可能导致资源泄漏等问题。以下是一个示例:

public class StopDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("子线程:" + i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        System.out.println("子线程被中断");
                        return; // 提前结束循环,优雅地停止执行
                    }
                }
            }
        });

        thread.start();
        try {
            Thread.sleep(3000); // 主线程等待 3 秒后再发送中断信号
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.stop(); // 强制终止线程执行
    }
}

在这个示例中,我们在子线程的循环中使用了 return 语句来提前结束循环。这样当收到中断信号时,子线程会立即停止执行。同时,我们还让主线程等待 3 秒后再发送中断信号,以确保子线程有足够的时间执行。

Java线程的join操作详解

在Java中,线程的join()方法是一个非常重要的同步工具。它可以让当前线程等待另一个线程完成后再继续执行。本文将详细介绍join()方法的作用、用法以及注意事项。

1. join()方法的作用

join()方法的主要作用是让当前线程等待另一个线程完成执行。当一个线程调用另一个线程的join()方法时,调用线程会被阻塞,直到被调用线程执行完毕。这样可以确保线程按照一定的顺序执行,避免出现死锁等问题。

2. join()方法的用法

join()方法有两种用法:

2.1 无参数的join()

无参数的join()方法用于等待当前线程执行完毕。换句话说,当前线程会一直阻塞,直到调用join()方法的线程执行完毕。这种用法通常用于主线程等待子线程完成任务。

示例代码:

public class JoinExample {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程开始执行");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("子线程执行完毕");
            }
        });
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程继续执行");
    }
}

输出结果:

子线程开始执行
主线程继续执行
子线程执行完毕

2.2 带有参数的join()

带有参数的join()方法用于等待指定时间后,或者指定条件满足后再继续执行。这种用法通常用于主线程等待子线程在一定时间内完成任务。

示例代码:

public class JoinExampleWithTimeout {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程开始执行");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("子线程执行完毕");
            }
        });
        thread.start();
        try {
            thread.join(3000); // 等待3秒后,如果子线程仍未执行完毕,则继续执行主线程
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程继续执行");
    }
}

输出结果:

子线程开始执行
(等待3秒)子线程执行完毕
主线程继续执行

Java线程的yield操作

在Java中,线程调度是由JVM和操作系统共同完成的。当一个线程执行了一定的时间,JVM会认为这个线程处于空闲状态,从而主动让出CPU资源给其他线程。这种让出资源的操作就是线程的yield操作。本文将详细介绍Java线程的yield操作,并给出代码示例。

1. yield操作的原理

yield操作是为了让当前线程主动放弃CPU资源,让出控制权给其他线程。当一个线程执行到yield语句时,它会将自己挂起,进入就绪状态,等待系统调度器重新分配CPU资源。需要注意的是,yield操作并不会使当前线程立即停止执行,而是暂停一段时间,让出CPU资源供其他线程使用。

2. yield操作的方法

在Java中,可以通过两种方式实现线程的yield操作:

2.1 使用Object类的wait()方法

Object类提供了wait()方法,可以让当前线程进入等待状态,并释放持有的锁。当另一个线程调用同一对象的notify()或notifyAll()方法时,该线程将从等待状态恢复并继续执行。示例代码如下:

public class YieldDemo {
    public static void main(String[] args) {
        Object lock = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("线程1开始执行");
                    lock.wait(); // 让出CPU资源给其他线程
                    System.out.println("线程1继续执行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t1.start();
        try {
            Thread.sleep(1000); // 主线程休眠1秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (lock) {
            lock.notify(); // 唤醒等待的线程t1
        }
    }
}

2.2 使用Thread类的yield()方法

Thread类提供了一个yield()方法,可以让当前线程暂时放弃CPU资源,让出控制权给其他线程。需要注意的是,yield()方法只是让当前线程进入就绪状态,而不会立即让出CPU资源。只有当其他线程调用该线程的interrupt()方法时,才会中断该线程,并重新分配CPU资源。示例代码如下:

public class YieldDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("线程1执行");
                Thread.yield(); // 让出CPU资源给其他线程
            }
        });
        t1.start();
        for (int i = 0; i < 5; i++) {
            System.out.println("主线程执行");
            Thread.sleep(1000); // 主线程休眠1秒
        }
    }
}

3. yield操作的影响

通过使用yield操作,可以让多线程程序更好地利用CPU资源,提高程序的执行效率。同时,由于yield操作不会立即让出CPU资源,因此可以避免因频繁切换线程而导致的性能损耗。但是,过度使用yield操作可能会导致某些任务得不到及时执行,从而影响程序的正确性。因此,在使用yield操作时需要根据具体情况进行权衡。

Java线程的守护线(daemon)程操作

在Java中,线程可以分为用户线程和守护线程。守护线程是一种特殊类型的线程,它的主要作用是提供一种机制,使得其他线程可以在主线程结束时自动结束执行。本文将介绍如何在Java中创建和管理守护线程。

1. 创建守护线程

要创建一个守护线程,我们需要使用Thread类的构造函数,并将一个布尔值作为参数传递,该布尔值表示线程是否是守护线程。如果传递true,则创建的线程将成为守护线程;如果传递false,则创建的线程将成为用户线程。

以下是一个创建守护线程的示例:

public class DaemonThreadDemo {
    public static void main(String[] args) {
        // 创建一个守护线程
        Thread daemonThread = new Thread(() -> {
            while (true) {
                System.out.println("守护线程正在运行...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, true); // 设置为守护线程

        // 启动守护线程
        daemonThread.start();

        // 主线程休眠5秒
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 主线程结束时,守护线程也会自动结束
        System.out.println("主线程结束,守护线程也将结束");
    }
}

在上面的示例中,我们创建了一个守护线程daemonThread,并在主线程结束后,守护线程也会自动结束。这是因为我们将true作为参数传递给了Thread构造函数。

2. 停止守护线程

要停止一个守护线程,我们可以调用该线程的interrupt()方法。这将导致守护线程抛出一个InterruptedException异常,从而允许我们在捕获到该异常时结束守护线程的执行。

以下是一个停止守护线程的示例:

public class DaemonThreadDemo {
    public static void main(String[] args) {
        // 创建一个守护线程
        Thread daemonThread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("守护线程正在运行...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    System.out.println("守护线程被中断");
                    break; // 跳出循环,停止守护线程的执行
                }
            }
        }, true); // 设置为守护线程

        // 启动守护线程
        daemonThread.start();

        try {
            // 主线程休眠5秒后,中断守护线程
            Thread.sleep(5000);
            daemonThread.interrupt(); // 中断守护线程
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("主线程结束");
        }
    }
}

在上面的示例中,我们在主线程休眠5秒后,调用了守护线程的interrupt()方法来中断守护线程。当守护线程检测到中断请求时,它会退出循环并结束执行。

Java线程状态总结

Java中的线程状态是一个非常重要的概念,它表示了线程的当前执行状态。Java线程状态一共有6种,分别是:新建(NEW)、可运行(RUNNABLE)、阻塞(BLOCKED)、等待(WAITING)、计时等待(TIMED_WAITING)和终止(TERMINATED)。下面我们将详细解释这6种状态以及相应的代码示例。

1. 新建(NEW)

当一个线程对象被创建时,它的初始状态就是新建(NEW)。此时,线程对象已经分配了内存空间,但还没有开始执行。我们可以通过调用Thread.new()方法来创建一个新线程。

public class NewThreadDemo {
    public static void main(String[] args) {
        Thread newThread = new Thread(() -> {
            System.out.println("新线程正在运行");
        });
        newThread.start();
    }
}

2. 可运行(RUNNABLE)

当线程对象调用了start()方法后,它的线程状态变为可运行(RUNNABLE),此时线程处于可运行状态,等待系统分配CPU时间片来执行。我们可以通过调用Thread.yield()方法让出CPU时间片,让其他线程执行。

public class RunnableThreadDemo {
    public static void main(String[] args) {
        Thread runnableThread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getId() + " - " + i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        runnableThread.start();
        for (int i = 0; i < 5; i++) {
            System.out.println(runnableThread.getId() + " - " + i);
            Thread.yield();
        }
    }
}

3. 阻塞(BLOCKED)

当一个线程调用了某个对象的wait()notify()synchronized方法时,它会进入阻塞状态。在阻塞状态下,线程会释放它所持有的所有锁,并等待锁的重新分配。我们可以通过调用Object.wait()Object.notify()或使用synchronized关键字来实现线程的阻塞。

public class BlockedThreadDemo {
    public static void main(String[] args) {
        Object lock = new Object();
        Thread blockedThread = new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("线程进入阻塞状态");
                    lock.wait();
                    System.out.println("线程离开阻塞状态");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        blockedThread.start();
        synchronized (lock) {
            try {
                System.out.println("主线程唤醒子线程");
                lock.notify();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

4. 等待(WAITING)

当一个线程调用了某个对象的wait()方法时,它会进入等待状态。在等待状态下,线程会释放它所持有的所有锁,并进入该对象的等待队列。我们可以通过调用Object.wait()方法来实现线程的等待。需要注意的是,当一个线程从等待状态被重新唤醒时,它会从等待队列的头部开始执行。如果此时有其他线程因为调用了同一对象的notify()notifyAll()方法而唤醒,那么唤醒的线程会从等待队列的尾部开始执行。我们可以通过调用Object.notify()Object.notifyAll()方法来唤醒等待队列中的线程。

public class WaitingThreadDemo {
    public static void main(String[] args) {
        Object lock = new Object();
        Object waitObj = new Object();
        Thread waitingThread = new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("线程进入等待状态");
                    lock.wait(3000); // 等待3秒后重新进入运行状态或者抛出InterruptedException异常退出等待状态,具体取决于条件是否满足。如果在指定的时间内条件不满足,则抛出InterruptedException异常。一旦线程被中断,就不能再次被唤醒。只有当线程处于运行状态时才能响应中断。否则,它将忽略中断并继续执行下去。另外,一旦线程从sleep、join或wait中返回,它将自动恢复为监视器的所有权。这意味着如果有其他线程在此对象上调用了notify或notifyAll方法,那么这个返回的线程将被重新排队以获取监视器锁。只有当没有其他线程持有此对象的监视器锁时,才会将当前线程重新排队到该对象的监视器锁上。如果另一个线程正在此对象上调用wait或join方法,那么当前线程将不会获得任何通知。因此这些方法是不能进行重排序的。此外,一旦一个线程获得了某个对象的锁,那么在该对象上的所有同步操作都必须对该线程可见。换句话说,对于任意一个对象而言,只能有一个线程可以访问该对象的synchronized方法或同步代码块,而且只要某条同步语句位于synchronized方法或同步代码块内,那么该语句就具有原子性。也就是说,在多线程环境下,对共享资源的操作要么全部成功完成,要么全部失败回滚。所以需要确保对共享资源的访问是原子性的以保证数据的一致性。否则就会发生数据不一致的情况出现。比如A账户扣钱10元:if(accountBalance>=10){ accountBalance-=10; }else{accountBalance=0; } 如果两个线程同时执行这段代码可能会出现一种情况:第一个线程扣除10元后,由于余额不足导致扣款失败;然后第二个线程开始执行扣款10元的操作,这时候就会出现数据不一致的情况。为了避免这种情况的发生就需要加锁来保证对共享资源的原子性操作。为了实现这个目的我们可以使用synchronized关键字或者Lock接口来进行加锁操作。synchronized关键字可以保证同一时刻最多只有一个线程执行该段代码;而Lock接口则可以实现更灵活的加锁机制,比如可以实现可重入锁等高级功能。总之,在使用同步机制的时候一定要注意加锁和解锁的顺序以及锁的粒度问题。否则就会出现死锁等问题的出现从而影响程序的正确性和性能表现。’);
                    waitObj.notify(); // 唤醒等待队列中的线程
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("线程离开等待状态");
                }
            }
        });
        waitingThread.start(); // 启动等待状态的线程
        try {
            Thread.sleep(1000); // 主线程休眠1秒后再唤醒子线程进行测试观察结果是否正确。如果在指定的时间内条件不满足,则抛出InterruptedException异常。一旦线程被中断,就不能再次被唤醒。只有当线程处于运行状态时才能响应中断。否则,它将忽略中断并继续执行下去。另外,一旦线程从sleep、join或wait中返回,它将自动恢复为监视器的所有权。这意味着如果有其他线程在此对象上调用了notify或notifyAll方法,那么这个返回的线程将被重新排队以获取监视器锁。只有当没有其他线程持有此对象的监视器锁时,才会将当前线程重新排队到该对象的监视器锁上。如果另一个线程正在此对象上调用wait或join方法,那么当前线程将不会获得任何通知。因此这些方法是不能进行重排序的。此外,一旦一个线程获得了某个对象的锁,那么在该对象上的所有同步操作都必须对该线程可见。换句话说,对于任意一个对象而言,只能有一个线程可以访问该对象的synchronized方法或同步代码块,而且只要某条同步语句位于synchronized方法或同步代码块内,那么该语句就具有原子性。也就是说,在多线程环境下,对共享资源的操作要么全部成功完成,要么全部失败回滚。所以需要确保对共享资源的访问是原子性的以保证数据的一致性。否则就会发生数据不一致的情况出现。比如A账户扣钱10元:if(accountBalance>=10){ accountBalance-=10; }else{accountBalance=0; } 如果两个线程同时执行这段代码可能会出现一种情况:第一个线程扣除10元后,由于余额不足导致扣款失败;然后第二个

待补充

往期专栏
Java全栈开发
数据结构与算法
计算机组成原理
操作系统
数据库系统
物联网控制原理与技术

你可能感兴趣的:(JAVA,#,多线程原理与实战,java,python,开发语言)