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
...