现代操做系统调度的最小单元是线程,也叫轻量级进程(Light Weight Process),在一个进程里能够建立多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,而且可以访问共享的内存变量。html
java.lang.Thread.State
中定义了 6 种不一样的线程状态,在给定的一个时刻,线程只能处于其中的一个状态。java
如下是各状态的说明,以及状态间的联系:git
start()
方法的线程处于此状态。start()
方法的线程状态。此状态意味着,线程已经准备好了,一旦被线程调度器分配了 CPU 时间片,就能够运行线程。Object.wait()
以后输入同步块/方法或从新输入同步块/方法。Object.wait()
Thread.join()
LockSupport.park()
Thread.sleep(sleeptime)
Object.wait(timeout)
Thread.join(timeout)
LockSupport.parkNanos(timeout)
LockSupport.parkUntil(timeout)
run()
方法执行结束,或者因异常退出了 run()
方法,则该线程结束生命周期。死亡的线程不可再次复生。构造线程主要有三种方式程序员
Thread
类Runnable
接口Callable
接口经过继承 Thread 类构造线程的步骤:github
示例:数据库
public class ThreadDemo02 { public static void main(String[] args) { Thread02 mt1 = new Thread02("线程A "); // 实例化对象 Thread02 mt2 = new Thread02("线程B "); // 实例化对象 mt1.start(); // 调用线程主体 mt2.start(); // 调用线程主体 } static class Thread02 extends Thread { private int ticket = 5; Thread02(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100; i++) { if (this.ticket > 0) { System.out.println(this.getName() + " 卖票:ticket = " + ticket--); } } } } }
经过实现 Runnable 接口构造线程的步骤:安全
示例:网络
public class RunnableDemo { public static void main(String[] args) { MyThread t = new MyThread("Runnable 线程"); // 实例化对象 new Thread(t).run(); // 调用线程主体 new Thread(t).run(); // 调用线程主体 new Thread(t).run(); // 调用线程主体 } static class MyThread implements Runnable { private int ticket = 5; private String name; MyThread(String name) { this.name = name; } @Override public void run() { for (int i = 0; i < 100; i++) { if (this.ticket > 0) { System.out.println(this.name + " 卖票:ticket = " + ticket--); } } } } }
经过实现 Callable 接口构造线程的步骤:并发
示例:dom
public class CallableAndFutureDemo { public static void main(String[] args) { Callable<Integer> callable = () -> new Random().nextInt(100); FutureTask<Integer> future = new FutureTask<>(callable); new Thread(future).start(); try { Thread.sleep(1000);// 可能作一些事情 System.out.println(future.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } }
当一个线程运行时,另外一个线程能够直接经过 interrupt()
方法中断其运行状态。
public class ThreadInterruptDemo { public static void main(String[] args) { MyThread mt = new MyThread(); // 实例化Runnable子类对象 Thread t = new Thread(mt, "线程"); // 实例化Thread对象 t.start(); // 启动线程 try { Thread.sleep(2000); // 线程休眠2秒 } catch (InterruptedException e) { System.out.println("三、休眠被终止"); } t.interrupt(); // 中断线程执行 } static class MyThread implements Runnable { @Override public void run() { System.out.println("一、进入run()方法"); try { Thread.sleep(10000); // 线程休眠10秒 System.out.println("二、已经完成了休眠"); } catch (InterruptedException e) { System.out.println("三、休眠被终止"); return; // 返回调用处 } System.out.println("四、run()方法正常结束"); } } }
Thread 中的 stop 方法有缺陷,已废弃。
安全地终止线程有两种方法:
public class ThreadStopDemo03 { public static void main(String[] args) throws Exception { MyTask one = new MyTask(); Thread countThread = new Thread(one, "CountThread"); countThread.start(); // 睡眠1秒,main线程对CountThread进行中断,使CountThread可以感知中断而结束 TimeUnit.SECONDS.sleep(1); countThread.interrupt(); MyTask two = new MyTask(); countThread = new Thread(two, "CountThread"); countThread.start(); // 睡眠1秒,main线程对Runner two进行取消,使CountThread可以感知on为false而结束 TimeUnit.SECONDS.sleep(1); two.cancel(); } private static class MyTask implements Runnable { private long i; private volatile boolean on = true; @Override public void run() { while (on && !Thread.currentThread().isInterrupted()) { i++; } System.out.println("Count i = " + i); } void cancel() { on = false; } } }
run
- 线程的执行实体。start
- 线程的启动方法。setName
、getName
- 能够经过 setName()、 getName() 来设置、获取线程名称。setPriority
、getPriority
- 在 Java 中,全部线程在运行前都会保持在就绪状态,那么此时,哪一个线程优先级高,哪一个线程就有可能被先执行。能够经过 setPriority、getPriority 来设置、获取线程优先级。setDaemon
、isDaemon
- 可使用 setDaemon() 方法设置线程为守护线程;可使用 isDaemon() 方法判断线程是否为守护线程。isAlive
- 能够经过 isAlive 来判断线程是否启动。interrupt
- 当一个线程运行时,另外一个线程能够直接经过 interrupt() 方法中断其运行状态。join
- 使用 join() 方法让一个线程强制运行,线程强制运行期间,其余线程没法运行,必须等待此线程完成以后才能够继续执行。Thread.sleep
- 使用 Thread.sleep() 方法便可实现休眠。Thread.yield
- 可使用 Thread.yield() 方法将一个线程的操做暂时让给其余线程执行。在 Thread 类中能够经过 setName()
、 getName()
来设置、获取线程名称。
public class ThreadNameDemo { public static void main(String[] args) { MyThread mt = new MyThread(); // 实例化Runnable子类对象 new Thread(mt).start(); // 系统自动设置线程名称 new Thread(mt, "线程-A").start(); // 手工设置线程名称 Thread t = new Thread(mt); // 手工设置线程名称 t.setName("线程-B"); t.start(); } static class MyThread implements Runnable { @Override public void run() { for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName() + "运行,i = " + i); // 取得当前线程的名字 } } } }
在 Thread 类中能够经过 isAlive()
来判断线程是否启动。
public class ThreadAliveDemo { public static void main(String[] args) { MyThread mt = new MyThread(); // 实例化Runnable子类对象 Thread t = new Thread(mt, "线程"); // 实例化Thread对象 System.out.println("线程开始执行以前 --> " + t.isAlive()); // 判断是否启动 t.start(); // 启动线程 System.out.println("线程开始执行以后 --> " + t.isAlive()); // 判断是否启动 for (int i = 0; i < 3; i++) { System.out.println(" main运行 --> " + i); } // 如下的输出结果不肯定 System.out.println("代码执行以后 --> " + t.isAlive()); // 判断是否启动 } static class MyThread implements Runnable { @Override public void run() { for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName() + "运行,i = " + i); } } } }
在 Java 程序中,只要前台有一个线程在运行,则整个 Java 进程就不会消失,因此此时能够设置一个守护线程,这样即便 Java 进程结束了,此守护线程依然会继续执行。可使用 setDaemon()
方法设置线程为守护线程;可使用 isDaemon()
方法判断线程是否为守护线程。
public class ThreadDaemonDemo { public static void main(String[] args) { Thread t = new Thread(new MyThread(), "线程"); t.setDaemon(true); // 此线程在后台运行 System.out.println("线程 t 是不是守护进程:" + t.isDaemon()); t.start(); // 启动线程 } static class MyThread implements Runnable { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + "在运行。"); } } } }
在 Java 中,全部线程在运行前都会保持在就绪状态,那么此时,哪一个线程优先级高,哪一个线程就有可能被先执行。
public class ThreadPriorityDemo { public static void main(String[] args) { System.out.println("主方法的优先级:" + Thread.currentThread().getPriority()); System.out.println("MAX_PRIORITY = " + Thread.MAX_PRIORITY); System.out.println("NORM_PRIORITY = " + Thread.NORM_PRIORITY); System.out.println("MIN_PRIORITY = " + Thread.MIN_PRIORITY); Thread t1 = new Thread(new MyThread(), "线程A"); // 实例化线程对象 Thread t2 = new Thread(new MyThread(), "线程B"); // 实例化线程对象 Thread t3 = new Thread(new MyThread(), "线程C"); // 实例化线程对象 t1.setPriority(Thread.MIN_PRIORITY); // 优先级最低 t2.setPriority(Thread.MAX_PRIORITY); // 优先级最低 t3.setPriority(Thread.NORM_PRIORITY); // 优先级最低 t1.start(); // 启动线程 t2.start(); // 启动线程 t3.start(); // 启动线程 } static class MyThread implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep(500); // 线程休眠 } catch (InterruptedException e) { e.printStackTrace(); } // 取得当前线程的名字 String out = Thread.currentThread().getName() + ",优先级:" + Thread.currentThread().getPriority() + ",运行:i = " + i; System.out.println(out); } } } }
wait、notify、notifyAll 是 Object 类中的方法。
wait
- 线程自动释放其占有的对象锁,并等待 notify。notify
- 唤醒一个正在 wait 当前对象锁的线程,并让它拿到对象锁。notifyAll
- 唤醒全部正在 wait 前对象锁的线程。生产者、消费者示例:
public class ThreadWaitNotifyDemo02 { private static final int QUEUE_SIZE = 10; private static final PriorityQueue<Integer> queue = new PriorityQueue<>(QUEUE_SIZE); public static void main(String[] args) { new Producer("生产者A").start(); new Producer("生产者B").start(); new Consumer("消费者A").start(); new Consumer("消费者B").start(); } static class Consumer extends Thread { Consumer(String name) { super(name); } @Override public void run() { while (true) { synchronized (queue) { while (queue.size() == 0) { try { System.out.println("队列空,等待数据"); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); queue.notifyAll(); } } queue.poll(); // 每次移走队首元素 queue.notifyAll(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 从队列取走一个元素,队列当前有:" + queue.size() + "个元素"); } } } } static class Producer extends Thread { Producer(String name) { super(name); } @Override public void run() { while (true) { synchronized (queue) { while (queue.size() == QUEUE_SIZE) { try { System.out.println("队列满,等待有空余空间"); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); queue.notifyAll(); } } queue.offer(1); // 每次插入一个元素 queue.notifyAll(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 向队列取中插入一个元素,队列当前有:" + queue.size() + "个元素"); } } } } }
在线程操做中,可使用 Thread.yield()
方法将一个线程的操做暂时让给其余线程执行。
public class ThreadYieldDemo { public static void main(String[] args) { MyThread t = new MyThread(); new Thread(t, "线程A").start(); new Thread(t, "线程B").start(); } static class MyThread implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "运行,i = " + i); if (i == 2) { System.out.print("线程礼让:"); Thread.yield(); } } } } }
在线程操做中,可使用 join()
方法让一个线程强制运行,线程强制运行期间,其余线程没法运行,必须等待此线程完成以后才能够继续执行。
public class ThreadJoinDemo { public static void main(String[] args) { MyThread mt = new MyThread(); // 实例化Runnable子类对象 Thread t = new Thread(mt, "mythread"); // 实例化Thread对象 t.start(); // 启动线程 for (int i = 0; i < 50; i++) { if (i > 10) { try { t.join(); // 线程强制运行 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Main 线程运行 --> " + i); } } static class MyThread implements Runnable { @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println(Thread.currentThread().getName() + " 运行,i = " + i); // 取得当前线程的名字 } } } }
直接使用 Thread.sleep()
方法便可实现休眠。
public class ThreadSleepDemo { public static void main(String[] args) { new Thread(new MyThread("线程A", 1000)).start(); new Thread(new MyThread("线程A", 2000)).start(); new Thread(new MyThread("线程A", 3000)).start(); } static class MyThread implements Runnable { private String name; private int time; private MyThread(String name, int time) { this.name = name; // 设置线程名称 this.time = time; // 设置休眠时间 } @Override public void run() { try { Thread.sleep(this.time); // 休眠指定的时间 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.name + "线程,休眠" + this.time + "毫秒。"); } } }
ThreadLocal,不少地方叫作线程本地变量,也有些地方叫作线程本地存储,其实意思差很少。可能不少朋友都知道 ThreadLocal 为变量在每一个线程中都建立了一个副本,那么每一个线程能够访问本身内部的副本变量。
ThreadLocal 的主要方法:
public class ThreadLocal<T> { public T get() {} public void remove() {} public void set(T value) {} public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {} }
get() 源码实现
get 源码
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
ThreadLocalMap 源码实现
ThreadLocalMap 源码
ThreadLocalMap 是 ThreadLocal 的一个内部类。
ThreadLocalMap 的 Entry 继承了 WeakReference,而且使用 ThreadLocal 做为键值。
setInitialValue 源码实现
private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
若是 map 不为空,就设置键值对;为空,再建立 Map,看一下 createMap 的实现:
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
ThreadLocal 源码小结
至此,可能大部分朋友已经明白了 ThreadLocal 是如何为每一个线程建立变量的副本的:
ThreadLocal 最多见的应用场景为用于解决数据库链接、Session 管理等问题。
示例 - 数据库链接
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { public Connection initialValue() { return DriverManager.getConnection(DB_URL); } }; public static Connection getConnection() { return connectionHolder.get(); }
示例 - Session 管理
private static final ThreadLocal threadSession = new ThreadLocal(); public static Session getSession() throws InfrastructureException { Session s = (Session) threadSession.get(); try { if (s == null) { s = getSessionFactory().openSession(); threadSession.set(s); } } catch (HibernateException ex) { throw new InfrastructureException(ex); } return s; }
管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不一样之处在于,它主要用于线程之间的数据传输,而传输的媒介为内存。 管道输入/输出流主要包括了以下 4 种具体实现:PipedOutputStream、PipedInputStream、PipedReader 和 PipedWriter,前两种面向字节,然后两种面向字符。
public class Piped { public static void main(String[] args) throws Exception { PipedWriter out = new PipedWriter(); PipedReader in = new PipedReader(); // 将输出流和输入流进行链接,不然在使用时会抛出IOException out.connect(in); Thread printThread = new Thread(new Print(in), "PrintThread"); printThread.start(); int receive = 0; try { while ((receive = System.in.read()) != -1) { out.write(receive); } } finally { out.close(); } } static class Print implements Runnable { private PipedReader in; Print(PipedReader in) { this.in = in; } public void run() { int receive = 0; try { while ((receive = in.read()) != -1) { System.out.print((char) receive); } } catch (IOException e) { e.printStackTrace(); } } } }
run() 方法是线程的执行体。
start() 方法会启动线程,而后 JVM 会让这个线程去执行 run() 方法。
能够直接调用 Thread 类的 run() 方法么?
参考阅读:Java 线程中 yield 与 join 方法的区别 参考阅读:sleep(),wait(),yield()和 join()方法的区别
thread.setPriority(Thread.MAX_PRIORITY)
的方式设置,默认优先级为 5。参考阅读:Java 中守护线程的总结
Java 的每一个对象中都有一个锁(monitor,也能够成为监视器) 而且 wait(),notify()等方法用于等待对象的锁或者通知其余线程对象的监视器可用。在 Java 的线程中并无可供任何对象使用的锁和同步器。这就是为何这些方法是 Object 类的一部分,这样 Java 的每个类都有用于线程间通讯的基本方法
当一个线程须要调用对象的 wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态直到其余线程调用这个对象上的 notify()方法。一样的,当一个线程须要调用对象的 notify()方法时,它会释放这个对象的锁,以便其余在等待的线程就能够获得这个对象锁。因为全部的这些方法都须要线程持有对象的锁,这样就只能经过同步来实现,因此他们只能在同步方法或者同步块中被调用。
免费Java资料须要本身领取,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo/Kafka、Hadoop、Hbase、Flink等高并发分布式、大数据、机器学习等技术。
传送门: https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q