XhstormR


On a dark desert highway Cool wind in my hair


Java Thread

Updated on 2016-10-24

Thread

Thread.State

Object.wait() | Object.notifyAll()

  • 进程:一块包含了某些资源的内存区域。
    • 一个进程中至少包含一个或多个线程(执行单元)。
    • 进程的内存区域仅能被它所包含的线程访问。
  • 线程:进程的顺序执行流,系统中最小的执行单元。
    • 线程只能归属于一个进程并且只能访问该进程的资源。
    • 进程的所有线程共享同一块内存空间。
  • 并发:OS 的线程调度机制将时间划分为很多时间片(分时),尽可能均匀分配给正在运行的线程(抢占),获得 CPU 时间片的线程得以被执行,其他则等待;而 CPU 则在这些线程上来回切换运行(上下文切换)。
    • 微观上走走停停,宏观上都在运行。

创建线程

public class A extends Thread {     通过继承 Thread 类
    public static void main(String[] args) {
        Thread a = new A();     实例化线程
        a.start();     启动线程
    }

    @Override
    public void run() {     执行逻辑
        for (int i = 0; i < 5; i++) {
            System.out.println("123");
        }
    }
}

----

public class A implements Runnable {     通过实现 Runnable 接口(推荐,因为单继承,多实现)
    public static void main(String[] args) {
        Thread a = new Thread(new A());     实例化线程并传入线程体
        a.start();     启动线程
    }

    @Override
    public void run() {     执行逻辑
        for (int i = 0; i < 5; i++) {
            System.out.println("123");
        }
    }
}

----

public class A {     通过创建匿名内部类
    public static void main(String[] args) {
        Thread a = new Thread() {     Thread 方式
            @Override
            public void run() {     执行逻辑
                for (int i = 0; i < 5; i++) {
                    System.out.println("123");
                }
            }
        };
        a.start();     启动线程

        Thread b = new Thread(new Runnable() {     Runnable 方式
            @Override
            public void run() {     执行逻辑
                for (int i = 0; i < 5; i++) {
                    System.out.println("123");
                }
            }
        });
        b.start();     启动线程
    }
}

操作线程

线程信息

public class A implements Runnable {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = Thread.currentThread();     获取当前线程对象(这里获得了主线程对象)
        System.out.println("标识符::名称::优先级::状态::是否处于活动状态::是否标记为中断");
        System.out.println(thread.getId() + "::" + thread.getName() + "::" + thread.getPriority() + "::" + thread.getState() + "::" + thread.isAlive() + "::" + thread.isInterrupted());

        System.out.println();
        Thread a = new Thread(new A(), "MyThread_1");     实例化线程并传入线程体
        System.out.println(a.getId() + "::" + a.getName() + "::" + a.getPriority() + "::" + a.getState() + "::" + a.isAlive() + "::" + a.isInterrupted());
        a.start();     启动线程
        System.out.println(a.getId() + "::" + a.getName() + "::" + a.getPriority() + "::" + a.getState() + "::" + a.isAlive() + "::" + a.isInterrupted());
        Thread.sleep(1000);     当前线程进入阻塞状态 1 秒(1000 毫秒 = 1 秒)
        System.out.println(a.getId() + "::" + a.getName() + "::" + a.getPriority() + "::" + a.getState() + "::" + a.isAlive() + "::" + a.isInterrupted());

        System.out.println();
        Thread b = new Thread(new A(), "MyThread_2");     实例化线程并传入线程体
        System.out.println(b.getId() + "::" + b.getName() + "::" + b.getPriority() + "::" + b.getState() + "::" + b.isAlive() + "::" + b.isInterrupted());
        b.start();     启动线程
        System.out.println(b.getId() + "::" + b.getName() + "::" + b.getPriority() + "::" + b.getState() + "::" + b.isAlive() + "::" + b.isInterrupted());
        Thread.sleep(1000);     当前线程进入阻塞状态 1 秒(1000 毫秒 = 1 秒)
        System.out.println(b.getId() + "::" + b.getName() + "::" + b.getPriority() + "::" + b.getState() + "::" + b.isAlive() + "::" + b.isInterrupted());
    }

    @Override
    public void run() {
    }
}
----
输出:
标识符::名称::优先级::状态::是否处于活动状态::是否标记为中断
1::main::5::RUNNABLE::true::false

11::MyThread_1::5::NEW::false::false
11::MyThread_1::5::RUNNABLE::true::false
11::MyThread_1::5::TERMINATED::false::false

12::MyThread_2::5::NEW::false::false
12::MyThread_2::5::RUNNABLE::true::false
12::MyThread_2::5::TERMINATED::false::false

线程优先级

public class A implements Runnable {
    public static void main(String[] args) {
        Thread a = new Thread(new A(), "MyThread_1");
        Thread b = new Thread(new A(), "MyThread_2");
        a.setPriority(Thread.NORM_PRIORITY);     默认优先级 5
        a.setPriority(Thread.MIN_PRIORITY);     设置为最小优先级 1(降低线程获得时间片的几率)
        b.setPriority(Thread.MAX_PRIORITY);     设置为最大优先级 10(提高线程获得时间片的几率)
        a.start();     启动线程
        b.start();     启动线程
    }

    @Override
    public void run() {
        Thread thread = Thread.currentThread();     获取当前线程对象
        for (int i = 0; i < 5; i++) {
            System.out.println(thread.getName());
        }
    }
}
----
输出:
MyThread_2
MyThread_2
MyThread_2
MyThread_2
MyThread_2
MyThread_1
MyThread_1
MyThread_1
MyThread_1
MyThread_1

守护线程

public class A implements Runnable {
    public static void main(String[] args) {
        Thread a = new Thread(new A(), "Daemon");
        a.setDaemon(true);     设置为守护线程(特点:当进程中只剩下守护线程时,所有守护线程将被强制终止)
        a.start();     启动线程
    }

    @Override
    public void run() {
        Thread thread = Thread.currentThread();     获取当前线程对象
        for (int i = 1; true; i++) {
            System.out.println(thread.getName() + "::" + i);
            try {
                Thread.sleep(1000);     当前线程进入阻塞状态 1 秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
----
输出:
(无)

阻塞线程

sleep

public class A {
    public static void main(String[] args) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("hh:mm:ss");
        while (true) {
            System.out.println(simpleDateFormat.format(new Date()));
            try {
                Thread.sleep(1000);     当前线程进入阻塞状态 1 秒(不会释放锁),之后重新进入 Runnable 状态,等待获得时间片
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
----
输出:
11:44:23
11:44:24
11:44:25
11:44:26
11:44:27
11:44:28
...

join

public class A implements Runnable {     使用 join 方法
    public static void main(String[] args) {
        Thread a = new Thread(new A(), "MyThread_1");
        a.start();     启动线程
        try {
            a.join();     当前线程进入阻塞状态,等待调用 join 方法的线程的线程体(run 方法)结束
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("—————————————");
    }

    @Override
    public void run() {
        Thread thread = Thread.currentThread();
        for (int i = 0; i < 5; ) {
            System.out.println(thread.getName() + "::" + ++i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
----
输出:
MyThread_1::1
MyThread_1::2
MyThread_1::3
MyThread_1::4
MyThread_1::5
—————————————

-------------------------------------------------------

public class A implements Runnable {     未使用 join 方法
    public static void main(String[] args) {
        Thread a = new Thread(new A(), "MyThread_1");
        a.start();     启动线程
        System.out.println("—————————————");
    }

    @Override
    public void run() {
        Thread thread = Thread.currentThread();
        for (int i = 0; i < 5; ) {
            System.out.println(thread.getName() + "::" + ++i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
----
输出:
—————————————
MyThread_1::1
MyThread_1::2
MyThread_1::3
MyThread_1::4
MyThread_1::5

yield

public class A implements Runnable {     使用 yield 方法
    public static void main(String[] args) {
        Thread a = new Thread(new A(), "MyThread_1");
        Thread b = new Thread(new A(), "MyThread_2");
        a.start();     启动线程
        b.start();     启动线程
    }

    @Override
    public void run() {
        Thread thread = Thread.currentThread();
        for (int i = 0; i < 5; i++) {
            System.out.println(thread.getName());
            Thread.yield();     强制当前线程主动让出 CPU 时间片,回到 Runnable 状态,等待获得时间片
        }
    }
}
----
输出:
MyThread_1
MyThread_2
MyThread_1
MyThread_2
MyThread_1
MyThread_2
MyThread_1
MyThread_2
MyThread_1
MyThread_2

-------------------------------------------------------

public class A implements Runnable {     未使用 yield 方法
    public static void main(String[] args) {
        Thread a = new Thread(new A(), "MyThread_1");
        Thread b = new Thread(new A(), "MyThread_2");
        a.start();     启动线程
        b.start();     启动线程
    }

    @Override
    public void run() {
        Thread thread = Thread.currentThread();
        for (int i = 0; i < 5; i++) {
            System.out.println(thread.getName());
        }
    }
}
----
输出:
MyThread_1
MyThread_1
MyThread_1
MyThread_1
MyThread_2
MyThread_2
MyThread_2
MyThread_2
MyThread_2
MyThread_1

停止线程

public class A implements Runnable {     通过调用 interrupt 方法(实质为设置退出旗标)
    public static void main(String[] args) {
        Thread a = new Thread(new A());
        a.start();     启动线程
        try {
            Thread.sleep(1000);     当前线程进入阻塞状态 1 秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        a.interrupt();     标记线程为中断状态
    }

    @Override
    public void run() {
        while (!Thread.interrupted()) {     判断线程是否为中断状态
            System.out.println("Running...");
            try {
                Thread.sleep(200);     当前线程进入阻塞状态 0.2 秒
            } catch (InterruptedException e) {     阻塞状态下中断线程将引发该异常,且中断状态将被清除
                break;     跳出循环
            }
        }
        System.out.println("Stop!");     收尾工作
    }
}
----
输出:
Running...
Running...
Running...
Running...
Running...
Stop!

-------------------------------------------------------

public class A implements Runnable {     通过设置退出旗标
    private volatile boolean mRunning = true;     volatile 关键字保证了多线程同步中的可见性,使线程能够正确获得变量的最新值

    public static void main(String[] args) {
        Runnable runnable = new A();
        Thread a = new Thread(runnable);
        a.start();     启动线程
        try {
            Thread.sleep(1000);     当前线程进入阻塞状态 1 秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        ((A) runnable).mRunning = false;     标记为 false
    }

    @Override
    public void run() {
        while (mRunning) {     判断是否继续运行
            System.out.println("Running...");
            try {
                Thread.sleep(200);     当前线程进入阻塞状态 0.2 秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Stop!");     收尾工作
    }
}
----
输出:
Running...
Running...
Running...
Running...
Running...
Stop!

同步线程

  • 异步一一一一:多线程并发,各干各的。
  • 同步(互斥):有先后顺序,你干完我再干。
    • synchronized 给修饰的代码块或方法加上 同步锁,保证了 可见性原子性
      • 同步锁:同时只允许一个线程持有,且此时只有该线程能够执行该同步锁修饰的代码块或方法。
        • 静态方法锁:锁对象是类对象。Man.class
        • 非静态方法锁:锁对象是类的对象。new Man()
      • 线程在进入同步代码块或方法时获得锁。
      • 线程在退出同步代码块或方法时释放锁。
  • 可见性:对变量的读操作,总是能看到对这个变量最后的写操作。
    • happens-before 关系:确保程序语句之间的排序在内存中的写对其他语句都是可见的。
    • volatile修饰的变量强制线程每次读写的时候都需要与主内存进行同步,保证了 可见性
      • 读:直接读取 主内存中的值。
      • 写:立即刷新 主内存中的值。
  • 原子性:操作不可分割,视作一个整体,且 不会被线程调度机制打断,则称为原子操作。

happens-before 关系
-------------------------------------------------------
定义变量
----
private int i1 = 1;
private int i2 = 2;
private int i3 = 3;
private volatile boolean mBoolean = false;     用 volatile 修饰的变量

读
----
(错)System.out.println(i1);     JVM 的重排序对普通变量的读操作不会排在对 volatile 变量的读操作之前,只会排在对 volatile 变量的读操作之后
System.out.println(mBoolean);
System.out.println(i1);
System.out.println(i2);
System.out.println(i3);

写
----
i1 = 4;
i2 = 5;
i3 = 6;
mBoolean = true;
(错)i1 = 7;     JVM 的重排序对普通变量的写操作不会排在对 volatile 变量的写操作之后,只会排在对 volatile 变量的写操作之前
public class A implements Runnable {
    public int mInt = 0;     值(应当守恒)
    public volatile boolean mRunning = true;     退出旗标

    @Override
    public void run() {
        while (mRunning) {     无限循环
            synchronized (this) {     同步锁(锁对象是类的对象)
                mInt++;
                mInt--;
            }
        }
    }
}

----

public class Initial {
    public static void main(String[] args) throws InterruptedException {
        A a = new A();     线程体
        for (int i = 0; i < 3; i++) {     启动 3 个线程
            new Thread(a).start();     传入同一个线程体,执行其中的 run 方法
        }
        Thread.sleep(1000);     当前线程进入阻塞状态 1 秒
        a.mRunning = false;     标记为 false
        System.out.println(a.mInt);     输出值
    }
}
----
输出:
0
public class A implements Runnable {
    private final int[] mInts;     能量数组(锁对象应该用 final 修饰)

    public A(int[] ints) {     构造函数
        mInts = ints;
    }

    @Override
    public void run() {     线程体
        Random random = new Random();
        while (true) {     无限循环
            int from = ((int) (mInts.length * random.nextDouble()));     随机
            int to = ((int) (mInts.length * random.nextDouble()));     随机
            int i = ((int) (1000 * random.nextDouble()));     随机
            transfer(from, to, i);     转移能量
            try {
                Thread.sleep(10);     阻塞 10 毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void transfer(int from, int to, int i) {     转移能量
        if (mInts[from] < i) {     能量不足
            return;
        }
        synchronized (mInts) {     同步锁(多个需要同步的线程的同步锁应该是同一个锁对象的引用,否则无法得到同步效果)
            mInts[from] -= i;     减少
            mInts[to] += i;     增加
            DecimalFormat decimalFormat1 = new DecimalFormat("00");     格式化
            DecimalFormat decimalFormat2 = new DecimalFormat("000");     格式化
            System.out.printf("%s\t 从 %s 转移 %s 到 %s \t%d\n", Thread.currentThread().getName(), decimalFormat1.format(from), decimalFormat2.format(i), decimalFormat1.format(to), getAll());
        }
    }

    private int getAll() {     获得总能量(应当守恒)
        int sum = 0;
        for (int i : mInts) {
            sum += i;
        }
        return sum;
    }
}

----

public class Initial {
    public static void main(String[] args) {
        int[] ints = new int[100];     初始化能量数组(共 100 个单元,每个单元 1000 能量)
        for (int i = 0; i < ints.length; i++) {
            ints[i] = 1000;
        }

        DecimalFormat decimalFormat = new DecimalFormat("000");
        for (int i = 0; i < ints.length; ) {     启动 100 个线程
            Thread thread = new Thread(new A(ints), "A_" + decimalFormat.format(++i));     传入同一个数组,对其进行操作,并作为锁对象
            thread.start();     启动线程
        }
    }
}
----
输出:
A_042	 从 39 转移 209 到 49 	100000
A_056	 从 22 转移 017 到 31 	100000
A_044	 从 29 转移 055 到 30 	100000
A_068	 从 28 转移 717 到 66 	100000
A_069	 从 85 转移 020 到 16 	100000
A_070	 从 00 转移 380 到 54 	100000
A_072	 从 67 转移 783 到 25 	100000
A_034	 从 58 转移 377 到 02 	100000
A_073	 从 04 转移 710 到 98 	100000
A_074	 从 30 转移 499 到 57 	100000
A_075	 从 95 转移 299 到 81 	100000
...
TOP