Loading...
墨滴

nil632

2021/11/01  阅读:33  主题:橙蓝风

wufly学Java——多线程

Java语言内置了多线程支持:一个java程序就是一个JVM进程,JVM进程用一个主线程来执行main函数,在main函数内部我们又可以启用多个线程。

1 创建新线程

  • 方法一: 从Thread派生一个自定义类,然后重写run方法
public class Test {

    public static void main(String[] args) {
        MyThread t = new MyThread();
        t.start();
        System.out.println("this is the main thread");
    }
}


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

最终结果:

this is the main thread
this is a new thread
  • 方法二:创建Thread实例类时传入Runable实例
public class Runnable {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t = new Thread(myRunnable);
        t.start();
        System.out.println("this is the main thread");
    }
}

class MyRunnable implements java.lang.Runnable {

    @Override
    public void run() {
        System.out.println("this is a new thread");
    }
}

最终结果:

this is the main thread
this is a new thread
  • lambda语法简写
public class TestLambda {
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("this is a new thread");
        }
        );
        t.start();

        System.out.println("this is the main thread");
    }
}

最终结果:

this is the main thread
this is a new thread

2 线程的状态

在java程序中,一个线程对象只能执行一次start方法启动新线程,并在新线程中执行run方法,一旦run方法执行结束了,因此,Java线程有以下几种状态:

  • New:新创建的线程,尚未执行
  • Runnable:运行中的线程,正在执行run方法中的java代码
  • Blocked:运行中的线程,因为某些原因被阻塞挂起
  • Waiting:运行中的线程,因为某些操作(如wait/sleep/join/locksupport.lock等)在等待中
  • Timed Waiting: 类似Waiting,带超时时间
  • Terminated: 线程已终止,run方法已执行完毕

可参考?java线程的6种状态及切换(透彻讲解)

思考问题怎么查看线程状态?

3 线程操作

3.1 中断线程 interrupt

如果线程需要执行一个长时间任务,那么就有可能有中断线程的需求。 应用场景:取消下载、web请求超时控制

中断一个线程非常容易,只能在其他线程中调用interrupt方法

public class TestInterrupt {

    public static void main(String[] args) throws InterruptedException {
        MyInterruptThread t = new MyInterruptThread();
        t.start();

        Thread.sleep(1);

        t.interrupt();

        t.join();

        System.out.println("end");
    }
}

class MyInterruptThread extends Thread {
    @Override
    public void run() {
        Integer n = 0;
        while (!isInterrupted()) {
            n++;
            System.out.println(n + " hello!");
        }
    }
}

对目标线程使用interrupt())方法后,目标线程通过检查isInterrupted()标志来判断自身是否已中断,如果目标线程处于Waiting状态,该线程会捕获到InterruptedException异常。

3.2 判断线程是否已中断 isInterrupted()

可通过isInterrupted()方法来判断当前线程是否已中断

3.3 等待线程 join()

t.join() // 使调用t线程在此之前执行完毕
t.join(1000) // 等待t线程,等待时间为1000毫秒

目标线程只要捕获到join()方法抛出的InterruptedException,就说明有其他线程对其调用了interrupt()方法,通常情况下该线程应该立刻结束运行。

3.4 守护线程 setDaemon(true)

守护线程是指为了其他线程服务的线程。在JVM中,所有非守护线程执行完毕后,无论是否还有守护线程,虚拟机都会自动退出。

 MyInterruptThread t = new MyInterruptThread();
 t.setDaemon(true);
 t.start();

3.5 线程同步 synchronized

当多个线程同时操作同一个变量时,就可能会出现数据不一致的问题.

public class TestSynchronized {
    public static void main(String[] args) throws InterruptedException {
        AddThread addThread = new AddThread();
        DecThread decThread = new DecThread();
        addThread.start();
        decThread.start();

        addThread.join();
        decThread.join();
        System.out.println(Counter.count);
    }
}


class Counter {
    public static Integer count = 0;
}

class AddThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            Counter.count += 1;
        }
    }
}

class DecThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            Counter.count -= 1;
        }
    }
}

这段程序每次执行的结果都不一样,这是因为Counter.count += 1是非原子性操作。它实际上是执行了三个步骤:

  • ILOAD
  • IADD
  • ISTORE

在线程A执行ILOAD操作后,该线程可能会被系统中断,切换到其他线程线程B,此时线程B加载的值跟线程A加载的值一样,然后被两个线程的ISTORE写入,最终最有一个ISTORE存入是正确的

解决方案

某一个线程执行时,其他线程必须等待——synchronized

修改程序为:

public class TestSynchronized {
    public static void main(String[] args) throws InterruptedException {
        AddThread addThread = new AddThread();
        DecThread decThread = new DecThread();
        addThread.start();
        decThread.start();

        addThread.join();
        decThread.join();
        System.out.println(Counter.count);
    }
}


class Counter {
    public static Integer count = 0;
    public static final Object locker = new Object();
}

class AddThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            synchronized (Counter.locker) {
                Counter.count += 1;
            }

        }
    }
}

class DecThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            synchronized (Counter.locker) {
                Counter.count -= 1;
            }
        }
    }
}

这样,程序每次执行的结果都是0了

nil632

2021/11/01  阅读:33  主题:橙蓝风

作者介绍

nil632