本文共 8421 字,大约阅读时间需要 28 分钟。
进程是程序的基本执行实体,进程可以容纳若干线程,是线程的容器。线程就是轻量级进程,线程的运行成本远远小于进程,所以我们用多线程去设计并发程序,而不是多进程。
jdk1.5以后,在java.lang.Thread的内部枚举类State中定义了线程的几种状态:
public enum State { /** * 新建一个线程,但还没有启动。 */ NEW, /** * 线程调用了start方法开始执行 */ RUNNABLE, /** *线程阻塞于锁(同步代码块或同步方法) */ BLOCKED, /** * 线程处于等待状态,调用了如下方法: * Object#wait(),Thread#join(),LockSupport.park() */ WAITING, /** * 线程处于一个明确时间的等待状态,调用了如下方法: * Thread.sleep,Object#wait(long), Thread#join(long) * LockSupport#parkNanos, LockSupport#parkUntil */ TIMED_WAITING, /** * 中止状态:表示线程执行完毕 */ TERMINATED;}
方法1:继承自Thread类,并重载其run方法,写成内部类就是:
Thread thread = new Thread() { @Override public void run() { System.out.println("Hello"); }};
方法2:实现Runnable接口,使用Thread构造方法传入
Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("Hello"); }});
在jdk8中推荐使用lambda表达式构建Runnable:
//单行Thread thread1 = new Thread(() -> System.out.println("Hello"));//多行Thread thread2 = new Thread(() -> { System.out.println("Hello"); System.out.println("World");});//也可以单独构建runnableRunnable task = () -> System.out.println("Hello");
启动线程,需要调用Thread#start()
方法(注意不是run
方法):
Runnable task = () -> System.out.println("Hello"); new Thread(task).start();
使用Thread#stop()方法可以立即终止一个线程,不过这个方法会把执行到一半的线程强行终止,jdk中已经不再推荐使用。
Thread thread = new Thread(() -> { while (true) { System.out.print("Hello "); try { Thread.sleep(3000);//模拟耗时操作 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("World"); }});thread.start();Thread.sleep(1000);thread.stop();
新建一个线程,要求线程每次完整的打印Hello World,当用了stop方法,你会发现只打印了Hello,造成了数据不一致。解决方法就是增加一个终止标志位,来让线程决定什么时候自己停止。
public class StopThread { volatile static boolean isStop = false;//标志位 public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { while (true) { System.out.print("Hello "); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("World"); if (isStop) { break; } } System.out.println("已终止"); }); thread.start(); Thread.sleep(1000); isStop = true;//终止 }}
上节我们使用了终止标志位来正确地终止线程,而在JDK中对线程提供了更好的中断支持,Thread类提供了三个方法,来支持中断操作:
public void interrupt() //把线程设置为中断状态(注意:并不会真正中断线程,仅改变状态)public boolean isInterrupted() //获取线程是否为中断状态public static boolean isInterrupted() 获取线程是否为中断状态,并清除中断状态
使用方法如下:
public class InterruptedTest { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { while (true) { if (Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName()+"线程被中断"); break; } System.out.println("hello world"); } },"t1"); thread.start(); Thread.sleep(2000); System.out.println(thread.getName() + "当前中断状态:" + thread.isInterrupted()); thread.interrupt(); }}
中断对Thread.sleep()的影响
Thread.sleep会让当前线程休眠,当线程休眠中调用中断方法时,会抛出一个InterruptedException方法。
public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { try { Thread.sleep(10000); } catch (InterruptedException e) { System.out.println("线程被中断:" + e.getMessage()); } }); thread.start(); Thread.sleep(1000); thread.interrupt();}
Object类提供了两个方法 wait、notify来实现线程的等待与通知
public class WaitNotify { static final Object object = new Object(); public static void main(String[] args) throws InterruptedException { Runnable task1 = () -> { System.out.println("task1 start"); synchronized (object) { System.out.println("task1 enter lock"); try { Thread.sleep(1000); System.out.println("task1 wait"); object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("task1 is notified by task2"); } }; Runnable task2 = () -> { System.out.println("task2 start"); synchronized (object) { System.out.println("task2 enter lock"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("task2 start"); object.notify(); } }; new Thread(task1).start(); new Thread(task2).start(); }}
Thread的suspend()方法会导致线程挂起,但不会释放锁
Thread的resume()方法会继续执行该线程。
由于挂起时并不会释放锁资源,这两个方法已经过时 。
public class SuspendTest { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { while (true) { System.out.println("hello:"); try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread.start(); Thread.sleep(1000); thread.suspend();//此时thread线程被挂起 System.out.println("suspend...3s");//挂起thread 3秒 Thread.sleep(3000); thread.resume();//thread线程恢复 }}
很多时候,线程之间需要互相协作来进行工作。比如下面的例子:
使用thread线程进行计算1到100的相加结果,main输出结果public class JoinTest { volatile static int i = 0; @Test public void test() { Thread thread = new Thread(() -> { for (int j = 1; j <= 100; j++) { i = i + j; } }); thread.start(); System.out.println(i);//使用thread线程进行计算1到100万的相加结果,main输出结果 }}
结果却输出0,原因是main线程没有等到thread线程执行完。
使用join方法改写上面的例子:public class JoinTest { volatile static int i = 0; @Test public void test() throws InterruptedException { Thread thread = new Thread(() -> { for (int j = 1; j <= 100; j++) { i = i + j; } }); thread.start(); thread.join();//这里main线程会停下来等待thread线程执行完 System.out.println(i);//输出5050 }}
public static native void yield();
执行这个方法后会让出CPU资源,当前线程会和其他线程一起进行CPU资源的争夺。
volatile关键字保证了不同线程对其他线程修改数据的后可见性。
线程组可以把一些线程放到同一组中,对这组线程进行统一管理:
public class ThreadGroupTest { @Test public void test1() { ThreadGroup group = new ThreadGroup("group 1"); Thread t1 = new Thread(group, () -> System.out.println("i am t1")); Thread t2 = new Thread(group, () -> System.out.println("i am t2")); t1.start(); t2.start(); //对线程组各种操作 group.stop(); group.interrupt(); //...等等 }}
守护线程是一种特殊的线程,当非守护线程全都退出时,守护线程也会退出。
public class DaemonTest { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { while (true) { System.out.println("i am Daemon thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread.setDaemon(true);//如果注释掉这行,thread将一直会循环打印下去 thread.start(); Thread.sleep(3000); }}
线程优先级使用 Thread#setPriority(int)
指定。
Thread thread = new Thread();thread.setPriority(Thread.MAX_PRIORITY);//Thread.MAX_PRIORITY = 10thread.setPriority(Thread.NORM_PRIORITY);//=5thread.setPriority(Thread.MIN_PRIORITY);//=1
值得注意的是:设置优先级并不能完全保证优先级高的线程竞争资源更有优势。
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象; 2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象; 3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;转载地址:http://bphgi.baihongyu.com/