多线程的三种实现

    最近写多篇关于多线程的博客,这里做个总结。线程从创建到消亡的过程中,可能会经历五种状态:

  1. New:线程刚被创建。
  2. Runnable:线程的start方法被调用后所处的状态,该状态下,线程才有了竞争时间片的可能,即可运行态。
  3. Running:线程正在执行run方法
  4. Dead:执行完run方法,或者被stop
  5. Blocked:线程放弃CPU使用权,进入阻塞状态。该状态下,线程可能重新被赋予CPU使用权进入Runnable状态,也可能直接消亡。阻塞状态可以细分成三种:
  • 等待阻塞:Runnable状态的的中运行Object的wait方法,被JVM放入等待池。
  • 同步阻塞:线程竞争同步锁失败,被JVM放入锁池。
  • 其他:线程执行sleep、Join等方法,被JVM置为阻塞状态,等待JVM重置状态为Runnable。

    Java中实现多线程的方式主要有三种:继承Thread类、实现Runnable接口与使用线程池。

继承Thread类与实现Runnable接口

    需要重写Thread类的run方法,或者实现Runnable的run方法,通过调用线程的start方法,启动线程。直接上例子:

package test.thread;

import java.util.LinkedList;
import java.util.Queue;

public class ThreadTest1 {

	public static void main(String[] args) {

		final Queue<Integer> queue = new LinkedList<Integer>();

		for (int i = 0; i < 1000; i++) {
			queue.add(i);
		}

		Thread[] threads = new Thread[10];
		for (int i = 0; i < 10; i++) {
			threads[i] = new Thread(new Runnable() {

				@Override
				public void run() {
					synchronized (queue) {
						try {
							queue.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						for (int j = 0; j < 100; j++) {
							System.out.println(Thread.currentThread().getName() + ": " + queue.poll());
						}
					}
				}
			});
			threads[i].start();
		}

		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		synchronized (queue) {
			queue.notifyAll();
		}
	}
}

    上面例子,在主线程与10个子线程中都用到了共享变量queue。通过调用queue的wait方法阻塞子线程,等待主线程调用notifyAll方法时,重新进入Runnable状态,保证了线程安全。需要注意object的wait、notify、notifyAll等方法需要放在同步块中。

    同样的功能,我们也可以这样实现:

package test.thread;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadTest2 {

	public static void main(String[] args) {

		final Lock lock = new ReentrantLock();
		final Condition condition = lock.newCondition();

		final Queue<Integer> queue = new LinkedList<Integer>();

		for (int i = 0; i < 1000; i++) {
			queue.add(i);
		}

		Thread[] threads = new Thread[10];
		for (int i = 0; i < 10; i++) {
			threads[i] = new Thread(new Runnable() {

				@Override
				public void run() {
					lock.lock();
					try {
						condition.await();
						for (int j = 0; j < 100; j++) {
							System.out.println(Thread.currentThread().getName() + ": " + queue.poll());
						}
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					} finally {
						lock.unlock();
					}

				}
			});
			threads[i].start();
		}

		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		lock.lock();
		try {
			condition.signalAll();
		} finally {
			lock.unlock();
		}
	}
}

    上面例子用到了ReentrantLock,相比synchronized关键字提供更加精细的同步锁功能。使用时需要注意要把锁的unlock方法,置于finally块中,避免线程异常中断,锁不被释放。

    我们还可以这样实现:

package test.thread;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.Semaphore;

public class ThreadTest3 {

	public static void main(String[] args) {

		final Semaphore semaphore = new Semaphore(1);

		final Queue<Integer> queue = new LinkedList<Integer>();

		for (int i = 0; i < 1000; i++) {
			queue.add(i);
		}

		Thread[] threads = new Thread[10];
		for (int i = 0; i < 10; i++) {
			threads[i] = new Thread(new Runnable() {

				@Override
				public void run() {
					try {
						semaphore.acquire(1);
						for (int j = 0; j < 100; j++) {
							System.out.println(Thread.currentThread().getName() + ": " + queue.poll());
						}
					} catch (Exception e1) {
						e1.printStackTrace();
					} finally {
						semaphore.release(1);
					}
				}
			});
			threads[i].start();
		}
	}
}

    这个例子中,我们用到了信号量。当线程获得semaphore时,如果semaphore的内部计数值大于0,就会减少计数值并允许访问共享资源。

使用线程池

    实现线程池主要通过Executors工具类以及ThreadPoolExecutor线程池实现类。ThreadPoolExecutor是线程池的核心功能,这里先挖个坑,后面有时间再整理。Executors提供了四种线程池:

newCachedThreadPool

    该方法创建一个可缓存的线程。线程池无线大,可以复用已经执行完成的线程。

newFixedThreadPool

    该方法创建一个定长的线程。可以实现并发线程个数,超过并发线程个数的线程进入等待队列。

newScheduledThreadPool

    该方法创建一个定长的线程,支持周期性任务的执行。

newSingleThreadExecutor

    该方法创建一个单线程化的线程池,同一时间只有一个线程在执行任务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java多线程实现三种方式: 1. 继承Thread类:创建一个继承自Thread类的子类,重写run()方法,在run()方法中定义线程要执行的任务。然后创建该子类的对象,并调用start()方法启动线程。 ```java class MyThread extends Thread { public void run() { // 线程要执行的任务 } } public class Main { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); } } ``` 2. 实现Runnable接口:创建一个实现了Runnable接口的类,实现run()方法,在run()方法中定义线程要执行的任务。然后创建该类的对象,并将其作为参数传递给Thread类的构造方法,最后调用start()方法启动线程。 ```java class MyRunnable implements Runnable { public void run() { // 线程要执行的任务 } } public class Main { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start(); } } ``` 3. 使用Callable和Future:创建一个实现了Callable接口的类,实现call()方法,在call()方法中定义线程要执行的任务,并返回一个结果。然后使用ExecutorService的submit()方法提交Callable任务,得到一个Future对象,通过调用Future对象的get()方法可以获取线程执行的结果。 ```java import java.util.concurrent.*; class MyCallable implements Callable<Integer> { public Integer call() throws Exception { // 线程要执行的任务,并返回一个结果 return 42; } } public class Main { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newSingleThreadExecutor(); MyCallable myCallable = new MyCallable(); Future<Integer> future = executorService.submit(myCallable); Integer result = future.get(); System.out.println(result); executorService.shutdown(); } } ``` 这三种方式都可以实现多线程,每种方式都有自己的优势和适用场景,选择合适的方式取决于具体的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值