Java线程是Java执行的基本单元,单线程程序编写比较简单,但是处理效率不高。随着CPU核心数量的增加和对程序高性能的要求,多线程编程也成为必然的趋势。
Java提供了至少三种实现线程的方式,一种是Runnable,一种是Thread,还有一种是线程池的方式。
1.Runnable方式:
Thread t = new Thread(new Runnable(){
public void run(){
//do something...
}
});
t.start();
2.Thread方式:
Thread t = new Thread(){
public void run(){
//do something...
}
}
t.start();
3.线程池的方式:
public class ThreadPoolEx {
private static BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(100);
private static ThreadPoolExecutor executors = new ThreadPoolExecutor(10,10,1000,TimeUnit.SECONDS,queue);
public static void main(String[] args) {
for(int i = 0;i<20;i++){
executors.execute(new RunnableTask(i));
}
}
/**
* 执行单元
*/
public static class RunnableTask implements Runnable{
private int index;
public RunnableTask(int index){
this.index = index;
}
@Override
public void run() {
System.out.println(index + "线程执行");
}
}
}
多线程在执行过程中,需要比单线程考虑很多问题。比如说线程安全;线程协同工作;线程停止,暂停,继续等工作。
一、线程安全
线程安全就是在多线程程序执行过程中,多个线程会同时访问修改同一变量(全局变量),会导致变量状态不一致,导致运行错误。线程安全要保证共享变量在执行过程中的读写有序,串行执行,保证同一时刻所有线程看到的变量状态一致。
保证线程安全的方法有很多,最常见的有三种,synchronized同步方法或者同步块,ReentrantLock显示加锁和读写锁,使用线程安全的原子对象操作变量的读写,例如AtomicInteger等。同步相关的操作这个不做介绍,参考其他文章。
二、线程协同工作
在编程中会遇到多个线程同时完成一个事情,在这个过程中需要线程之间协同工作,例如等待其他线程完成,触发其他线程执行等。协同工作有CountDownLatch,CyclicBarrier等常用功能。
1.CountDownLatch
适合场景是若干个线程全部执行完成后,再执行其他任务。
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(4);
new TestThread(latch).start();
new TestThread(latch).start();
new TestThread(latch).start();
new TestThread(latch).start();
try {
latch.await();
System.out.println("执行完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static class TestThread extends Thread{
CountDownLatch latch;
public TestThread(CountDownLatch latch){
this.latch = latch;
}
@Override
public void run() {
try {
Thread.sleep(1000);
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2.CyclicBarrier
适合场景是多个线程执行到同一位置后,同时执行,如果到达这个位置的线程数量不够则线程等待。
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3);
new ThreadTest(barrier,1000).start();
new ThreadTest(barrier,2000).start();
new ThreadTest(barrier,3000).start();
System.out.println("主线程执行完成");
}
public static class ThreadTest extends Thread{
private CyclicBarrier cb;
private int waittime;
public ThreadTest(CyclicBarrier cb,int waittime){
this.cb = cb;
this.waittime = waittime;
}
public void run(){
try {
Thread.sleep(waittime);
cb.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("线程结束");
}
}
三、线程的停止、暂停、继续
Java线程在设计之初没有考虑周全,Thread提供的stop,suspend,resume,destroy等方法都是线程不安全的,现在已经不推荐使用了。
1.stop方法会直接抛出ThreadDeath异常,导致线程终止,还有释放线程中持有的所有锁,如果线程对于共享变量进行了修改,使其处于不一致状态,其他线程再去访问这个对象就会产生意想不到的结果,甚至导致程序崩溃。
2.suspend,resume方法会导致线程的死锁,它会挂起线程持有的锁,如果一直这样可能导致其他需要该锁的线程死锁。
3.destroy方法压根就没有实现。
4.interrupt方法用于在线程没有响应或者处于阻塞状态时停止线程用的,它会抛出InterruptedException。在一些情况下可能不会响应interrupt方法,可以关闭相关资源方法促使线程结束,例如关闭Socket以结束线程。
5.线程停止的正确方法是定义一个volatile变量来标示线程是否停止,在线程执行中轮训这个变量来停止这个线程。例如下面这个例子。
public class ThreadStopEx extends Thread{
//标示线程是否停止标志
private volatile boolean ISSTART = true;
@Override
public void run() {
while(ISSTART){
synchronized (this) {
try {
Thread.sleep(1000);
System.out.println("线程执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void stopThread() {
ISSTART = false;
this.interrupt();
}
public static void main(String[] args) throws InterruptedException {
ThreadStopEx thread = new ThreadStopEx();
thread.start();
Thread.sleep(2500);
thread.stopThread();
}
}
6.线程的暂停和继续解决方法。线程的暂停和继续使用suspend/resume方法会有产生线程死锁问题,建议使用wait和notify来进行线程的暂停和继续。下面是例子。
public class ThreadSuspendResumeEx {
private static Object waitObject = new Object();
public static void main(String[] args) throws InterruptedException {
new ThreadTest(1).start();
new ThreadTest(2).start();
new ThreadTest(3).start();
//等待1000毫秒
Thread.sleep(1000);
synchronized (waitObject) {
waitObject.notify();
}
System.out.println("主线程1000毫秒后唤醒一个线程。");
//等待2000毫秒
Thread.sleep(2000);
System.out.println("主线程3000毫秒后唤醒全部线程。");
synchronized(waitObject){
waitObject.notifyAll();
}
}
public static class ThreadTest extends Thread{
//线程编号
private int index;
public ThreadTest(int index){
this.index = index;
}
@Override
public void run() {
synchronized(waitObject){
try {
waitObject.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(index + "线程执行");
}
}
}
源码见附件。