Java创建线程的方法有两种
两种方法分别是继承Thread类和实现Runnable接口。
- 继承Thread类
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Hello World!");
}
}
public class Main {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
- 实现Runnable接口
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Hello World!");
}
}
public class Main {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
}
}
两者区别在于,通过继承Thread类,可以重写Thread类中的run()方法,直接调用start()方法启动线程;而通过实现Runnable接口,需要先创建一个实现了Runnable接口的类的实例对象,在创建Thread对象时将该实例对象作为参数传入,并且该实现类必须实现run()方法。由于Java只支持单继承,因此如果已经继承了其他类,则无法使用第一种方法创建线程。
Java引入线程组
为了更好地管理和控制线程。通过将线程分组,可以更轻松地对线程进行统一管理和控制,例如一次性停止所有属于同一线程组的线程等。此外,在开发中,有时需要创建大量的线程,而这些线程可能是相关的,因此通过将它们放入同一个线程组中,可以更清晰地表达它们之间的关系。最后,线程组还可以用于安全管理,因为可以为不同的线程组设置不同的安全策略,以确保线程安全执行。
下面是一个简单的JAVA线程组应用示例:
public class ThreadGroupDemo {
public static void main(String[] args) {
ThreadGroup group = new ThreadGroup("testGroup"); // 创建一个名为testGroup的线程组
Thread t1 = new Thread(group, new MyRunnable(), "t1");
Thread t2 = new Thread(group, new MyRunnable(), "t2");
Thread t3 = new Thread(group, new MyRunnable(), "t3");
t1.start();
t2.start();
t3.start();
System.out.println("Active threads in thread group: " + group.activeCount());
group.list();
}
static class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running.");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
在这个示例中,我们创建了一个名为“testGroup”的线程组,并向其中添加了三个线程。通过调用activeCount()
方法,可以获取线程组当前活动的线程数量。通过调用list()
方法,可以输出线程组中所有线程的相关信息。
输出结果如下:
t1 is running.
t3 is running.
t2 is running.
Active threads in thread group: 3
java.lang.ThreadGroup[name=testGroup,maxpri=10]
Thread[t1,5,testGroup]
Thread[t3,5,testGroup]
Thread[t2,5,testGroup]
可以看到,我们创建的线程组包含了三个线程,它们都在运行中。通过list()
方法,我们可以查看线程组中包含了哪些线程,并了解每个线程的相关信息。
需要注意的是,线程组并不会影响线程本身的执行,它只是一个逻辑上的分组并提供一些管理和控制的功能。
线程调度和控制
Java中线程控制的几个方法包括:
-
start()方法:启动一个线程,该方法将在新的线程中调用run()方法。
-
run()方法:线程执行的代码放在该方法中。
-
join()方法:主线程等待该方法所属的线程执行完毕后才会继续执行。
-
sleep()方法:让当前线程休眠一定时间,可以指定时间长度或者时间点。
-
yield()方法:暂停当前线程的执行,让其他线程有机会运行,但是不会释放锁。
-
interrupt()方法:中断线程的执行,可以通过isInterrupted()方法来判断线程是否被中断;同时也可以使用Thread.interrupted()方法来清除线程中断状态。
-
wait()、notify()和notifyAll()方法:用于多线程之间的协作。wait()方法使得当前线程等待另一个线程的信号,而notify()和notifyAll()方法则可以唤醒一个或多个等待的线程。
sleep()和join()方法都可以用于控制线程的执行,
sleep()方法用于暂停当前线程,在给定的时间内不参与CPU时间片调度;
而join()方法则是等待指定线程结束,直到它退出并释放锁,才会让当前线程继续执行。
以下是一个简单的Java程序,展示了如何使用sleep()和join()方法来控制线程的执行顺序和等待时间,演示了sleep()和join()方法的使用:
public class SleepAndJoinExample implements Runnable {
public void run() {
try {
// 线程休眠5秒
System.out.println(Thread.currentThread().getName() + " is sleeping for 5 seconds...");
Thread.sleep(5000);
System.out.println(Thread.currentThread().getName() + " has woken up!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new SleepAndJoinExample(), "Thread 1");
Thread t2 = new Thread(new SleepAndJoinExample(), "Thread 2");
Thread t3 = new Thread(new SleepAndJoinExample(), "Thread 3");
// 线程t1启动并等待
t1.start();
t1.join();
// 线程t2启动并等待
t2.start();
t2.join();
// 线程t3启动并等待
t3.start();
t3.join();
System.out.println("All threads have completed.");
}
}
这个例子创建了三个线程并分别启动它们。然后,每个线程都调用了sleep()方法来模拟执行一些耗时操作。同时,每个线程也调用了join()方法,以确保前一个线程结束后再启动下一个线程。
输出结果应该类似于:
Thread 1 is sleeping for 5 seconds...
Thread 1 has woken up!
Thread 2 is sleeping for 5 seconds...
Thread 2 has woken up!
Thread 3 is sleeping for 5 seconds...
Thread 3 has woken up!
All threads have completed.
wait() 与notify()
在Java中,可以使用notify()方法来激活处于wait状态的线程。notify()方法会随机唤醒一个等待该对象锁的线程。如果想唤醒所有等待该对象锁的线程,可以使用notifyAll()方法。
以下是一个简单的示例代码:
public class WaitNotifyExample {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread thread1 = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("Thread 1 is waiting...");
lock.wait();
System.out.println("Thread 1 is awake!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("Thread 2 is sleeping...");
Thread.sleep(2000);
System.out.println("Thread 2 is notifying...");
lock.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("All threads completed.");
}
}
在这个例子中,我们创建了两个线程:thread1和thread2。thread1进入了synchronized代码块并调用wait()方法进入等待状态,直到被thread2通过notifyAll()方法唤醒。
当thread2执行完任务后,它会调用notifyAll()方法唤醒所有等待该对象锁的线程(在本例中只有一个线程在等待)。
调用yield()方法
定义了YieldDemo类作为Thread的子类,覆盖了其run()方法。在run()方法中,我们使用for循环打印出当前线程的名称和计数器的值,并调用Thread.yield()方法。此方法会暂停当前正在执行的线程并允许其他线程运行。
在main()方法中,我们创建和启动三个YieldDemo线程。当这些线程运行时,它们会交替运行,并在每次迭代时调用yield()方法,从而给其他正在运行的线程机会运行。
下面是一个简单的Java yield()示例:
public class YieldDemo extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " running " + i);
Thread.yield();
}
}
public static void main(String[] args) {
YieldDemo t1 = new YieldDemo();
YieldDemo t2 = new YieldDemo();
YieldDemo t3 = new YieldDemo();
t1.start();
t2.start();
t3.start();
}
}
interrupt()中断Java线程
下面是一个简单的示例,演示如何使用interrupt()中断Java线程:
public class MyThread extends Thread {
public void run() {
try {
System.out.println("Starting my thread...");
// 让线程睡眠5秒钟
Thread.sleep(5000);
System.out.println("My thread is running...");
} catch (InterruptedException e) {
System.out.println("My thread was interrupted!");
return;
}
System.out.println("My thread is done.");
}
}
public class Main {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
// 让主线程睡眠1秒钟
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 中断myThread线程
myThread.interrupt();
}
}
代码中创建了一个名为MyThread
的线程类,并在其中实现了run()
方法。这个方法会启动线程并将线程睡眠5秒钟,然后输出一条消息。如果线程被中断,则会捕获InterruptedException
异常并退出方法。
在Main
类中,我们创建了MyThread
实例,并启动线程。随后,我们让主线程睡眠1秒钟,并在之后调用myThread.interrupt()
方法中断线程。该方法将触发MyThread
实例中的InterruptedException
异常,从而停止线程的执行。
执行上述代码后,你会看到输出如下的结果:
Starting my thread...
My thread was interrupted!
当我们调用myThread.interrupt()
方法时,线程被中断了,并且在run()
方法中捕获到了InterruptedException
异常。
使用wait()
、notify()
和notifyAll()
方法
Java多线程编程中重要的机制,它们可以协调线程之间的操作。下面是一些场景的示例:
- 生产者-消费者模式:当生产者线程生产出一个对象后,它会将其放入一个共享的缓冲区,同时唤醒等待中的消费者线程。如果缓冲区已满,则生产者线程会等待,直到有消费者取走了一个对象才会被唤醒。
public class ProducerConsumer {
private List<Integer> buffer = new ArrayList<>();
private int capacity = 5;
public void produce() throws InterruptedException {
synchronized (this) {
while (buffer.size() == capacity) {
System.out.println("Buffer is full. Waiting for consumer to consume from buffer..");
wait();
}
int item = new Random().nextInt(100);
buffer.add(item);
System.out.println("Produced: " + item);
notify(); // notifies any waiting consumer threads
}
}
public void consume() throws InterruptedException {
synchronized (this) {
while (buffer.size() == 0) {
System.out.println("Buffer is empty. Waiting for producer to produce into buffer..");
wait();
}
int item = buffer.remove(0);
System.out.println("Consumed: " + item);
notify(); // notifies any waiting producer threads
}
}
}
- 协调线程执行顺序:例如,我们希望一个线程在另一个线程完成后才能执行,可以使用
wait()
和notify()
方法来实现。
public class SequentialExecution {
private boolean isCompleted = false;
public synchronized void waitToComplete() throws InterruptedException {
while (!isCompleted) {
wait();
}
}
public synchronized void markAsCompleted() {
isCompleted = true;
notify();
}
}
在这个例子中,一个线程可以调用waitToComplete()
方法来等待另一个线程完成工作。一旦另一个线程调用了markAsCompleted()
方法,第一个线程就会被唤醒。
- 线程间通信:例如,我们希望两个线程之间交替打印数字,可以使用
wait()
、notify()
和标志位来实现。
public class AlternatePrinting {
private int maxNumber = 10;
private int currentNumber = 1;
private boolean isOdd = true;
public synchronized void printOdd() throws InterruptedException {
while (currentNumber <= maxNumber) {
if (!isOdd) {
wait();
}
System.out.println(Thread.currentThread().getName() + ": " + currentNumber);
currentNumber++;
isOdd = false;
notifyAll();
}
}
public synchronized void printEven() throws InterruptedException {
while (currentNumber <= maxNumber) {
if (isOdd) {
wait();
}
System.out.println(Thread.currentThread().getName() + ": " + currentNumber);
currentNumber++;
isOdd = true;
notifyAll();
}
}
}
在这个例子中,我们创建了一个AlternatePrinting
类,其中包含了printOdd()
和printEven()
方法,这两个方法分别负责奇数和偶数的打印。我们使用一个属性isOdd
来记录当前应该打印奇数还是偶数,如果该属性为true
,则说明应该打印奇数,否则应该打印偶数。在每个方法内部,我们使用wait()
方法来使线程等待,直到另一个线程打印完数字并唤醒了该线程。