用户工具

站点工具


分享:技术:多线程:多线程编程核心技术整理

这是本文档旧的修订版!


多线程编程核心技术整理

前言

2016年5月12日东哥在微信读书APP上送给我一本书《JAVA多线程编程核心技术》,作者高洪岩。在接下来的7天内读完了,感觉这本书写的挺好,作者基本把所有的知识点都结合代码例子来讲解,容易被程序员接受。其实在读的过程当中,就想着要把这本书的知识点做个整理,用于备忘也用于更好的掌握多线程。可能在我尝试整理知识点的时候,某些我觉得比较不常用的,我会比较简单的整理。

多线程基础

进程和多线程的概念及线程的优点

  • 进程

比如WINDOWS任务管理器里,看到的QQ.EXE,就是一个进程

  • 线程

比如QQ中的视频,发送文件,发送文字图片等,都是独立在运作的“线程”

  • 线程的优点

多线程或者说多任务操作系统,可以最大限度地利用CPU的空闲时间在不同的任务之间不停的切换,由于切换的速度非常快,使使用者感觉多个任务是在同时进行的。当然后来也出现多个CPU,或者一个CPU多核就是为了加快机器运算。

使用多线程

打印主线程的名字

Test.java
package com.gxx.threads.study.test1;
 
/**
 * 测试类-打印主线程的名字
 * @author Gxx
 */
public class Test {
	/**
	 * main方法
	 * @param args
	 */
	public static void main(String[] args) {
		/**
		 * 打印main方法当前线程名字
		 * 输出:main
		 */
		System.out.println(Thread.currentThread().getName());
	}
}

输出

main

两种实现线程类的方法

  • 继承Thread实现线程类
MyThread1.java
package com.gxx.threads.study.test2;
 
/**
 * 继承Thread实现线程类
 * @author Gxx
 */
public class MyThread1 extends Thread {
	/**
	 * 覆盖Thread中的run方法
	 */
	@Override
	public void run() {
		System.out.println("MyThread1.run()");
	}
}
  • 实现Runnable实现线程类
MyThread2.java
package com.gxx.threads.study.test2;
 
/**
 * 实现Runnable实现线程类
 * @author Gxx
 */
public class MyThread2 implements Runnable {
	/**
	 * 实现Runnable中的run方法
	 */
	@Override
	public void run() {
		System.out.println("MyThread2.run()");
	}
}
  • 两种实现的区别

本质上并没有什么区别,但是由于JAVA只支持单继承,如果继承了Thread就不能继承其它的父类了,所以绝大多数情况下是由于这个,而Thread类其实也实现了Runnable接口。

测试start()、run()和多次start()

  • start()

start()通过“线程规划器”此线程已经准备就绪,等待调用run(),CPU在不确定的时间调用run()

  • run()

直接执行run,就不是“线程规划器”来调度了,而是执行run()的当前线程直接调用方法,如果是main()中调用,则当前线程就是main

  • 多次start()

多次start()会抛出异常java.lang.IllegalThreadStateException,报线程状态异常

MyThread.java
package com.gxx.threads.study.test3;
 
/** 
 * 自定义线程类
 * @author Gxx
 */
public class MyThread extends Thread {
	/**
	 * 构造方法
	 */
	public MyThread(){
		System.out.println("线程[" + Thread.currentThread().getName() + "]执行构造方法");
	}
 
	/**
	 * 覆盖run方法
	 */
	@Override
	public void run() {
		System.out.println("线程[" + Thread.currentThread().getName() + "]执行run");
	}
}
Test.java
package com.gxx.threads.study.test3;
 
/**
 * 测试类-测试start和run和多次start
 * @author Gxx
 */
public class Test {
	/**
	 * main方法
	 * @param args
	 */
	public static void main(String[] args) {
		MyThread thread = new MyThread();
		thread.setName("线程A");//设置线程名字
		thread.run();
		thread.start();
		thread.start();
	}
}

输出

线程[main]执行构造方法
线程[main]执行run
线程[线程A]执行run
Exception in thread "main" java.lang.IllegalThreadStateException
	at java.lang.Thread.start(Thread.java:705)
	at com.gxx.threads.study.test3.Test.main(Test.java:17)

不共享变量,线程安全

MyThread.java
package com.gxx.threads.study.test4;
 
/** 
 * 自定义线程类
 * @author Gxx
 */
public class MyThread extends Thread {
	/**
	 * 计数
	 */
	int count = 10;
 
	/**
	 * 覆盖run方法
	 */
	@Override
	public void run() {
		System.out.println("线程[" + Thread.currentThread().getName() + "]执行count=" + (count--));
	}
}
TestSafe.java
package com.gxx.threads.study.test4;
 
/**
 * 测试类-不共享变量,线程安全
 * @author Gxx
 */
public class TestSafe {
	/**
	 * main方法
	 * @param args
	 */
	public static void main(String[] args) {
		MyThread thead1 = new MyThread();
		MyThread thead2 = new MyThread();
		MyThread thead3 = new MyThread();
		MyThread thead4 = new MyThread();
		MyThread thead5 = new MyThread();
		MyThread thead6 = new MyThread();
		MyThread thead7 = new MyThread();
		MyThread thead8 = new MyThread();
		MyThread thead9 = new MyThread();
		MyThread thead10 = new MyThread();
		thead1.start();
		thead2.start();
		thead3.start();
		thead4.start();
		thead5.start();
		thead6.start();
		thead7.start();
		thead8.start();
		thead9.start();
		thead10.start();
	}
}

输出

线程[Thread-0]执行count=10
线程[Thread-2]执行count=10
线程[Thread-1]执行count=10
线程[Thread-3]执行count=10
线程[Thread-4]执行count=10
线程[Thread-5]执行count=10
线程[Thread-6]执行count=10
线程[Thread-7]执行count=10
线程[Thread-8]执行count=10
线程[Thread-9]执行count=10

共享变量,线程不安全

TestNotSafe.java
package com.gxx.threads.study.test4;
 
/**
 * 测试类-共享变量,线程不安全
 * @author Gxx
 */
public class TestNotSafe {
	/**
	 * main方法
	 * @param args
	 */
	public static void main(String[] args) {
		MyThread thread = new MyThread();
		Thread thead1 = new Thread(thread);
		Thread thead2 = new Thread(thread);
		Thread thead3 = new Thread(thread);
		Thread thead4 = new Thread(thread);
		Thread thead5 = new Thread(thread);
		Thread thead6 = new Thread(thread);
		Thread thead7 = new Thread(thread);
		Thread thead8 = new Thread(thread);
		Thread thead9 = new Thread(thread);
		Thread thead10 = new Thread(thread);
		thead1.start();
		thead2.start();
		thead3.start();
		thead4.start();
		thead5.start();
		thead6.start();
		thead7.start();
		thead8.start();
		thead9.start();
		thead10.start();
	}
}

输出

线程[Thread-2]执行count=10
线程[Thread-3]执行count=9
线程[Thread-1]执行count=10
线程[Thread-4]执行count=8
线程[Thread-5]执行count=7
线程[Thread-6]执行count=6
线程[Thread-7]执行count=5
线程[Thread-8]执行count=4
线程[Thread-9]执行count=3
线程[Thread-10]执行count=2

共享变量,使用synchronized使得线程安全

Tools.java
package com.gxx.threads.study.test5;
 
/** 
 * 工具类
 * @author Gxx
 */
public class Tools {
	/**
	 * 计数
	 */
	int count = 10;
 
	/**
	 * 打印count
	 * @throws Exception
	 */
	public synchronized void printCount() {
		try {
			count--;
			/**
			 * 睡眠1秒
			 */
			Thread.sleep(1000);
			System.out.println("线程[" + Thread.currentThread().getName() + "]执行count=" + count);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
MyThread.java
package com.gxx.threads.study.test5;
 
/** 
 * 自定义线程类
 * @author Gxx
 */
public class MyThread extends Thread {
	/**
	 * 工具类
	 */
	Tools tools;
 
	/**
	 * 构造方法
	 * @param tools
	 */
	public MyThread(Tools tools){
		this.tools = tools;
	}
 
	/**
	 * 覆盖父类的run方法
	 */
	@Override
	public void run() {
		tools.printCount();
	}
}
Test.java
package com.gxx.threads.study.test5;
 
/**
 * 测试类-共享变量,使用synchronized使得线程安全
 * @author Gxx
 */
public class Test {
	/**
	 * main方法
	 * @param args
	 */
	public static void main(String[] args) {
		Tools tools = new Tools();
		/**
		 * 共用同一个tools
		 */
		MyThread thread1 = new MyThread(tools);
		MyThread thread2 = new MyThread(tools);
		thread1.start();
		thread2.start();
	}
}

输出

线程[Thread-0]执行count=9
线程[Thread-1]执行count=8

count--和System.out.println()

注意:

  • count–不是同步的,分三步1.获取count的值;2.count-1;3.再赋值给count;不同步则有时间差
  • System.out.println()是同步的
println.java
public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

getId()、isAlive()和sleep()

  • getId 是取得线程的唯一标识
  • isAlive是判断当前线程是否处于活动状态
  • sleep是指this.currentThread()休眠多少毫秒
MyThread.java
package com.gxx.threads.study.test6;
 
/** 
 * 自定义线程类
 * @author Gxx
 */
public class MyThread extends Thread {
	/**
	 * 覆盖run方法
	 */
	@Override
	public void run() {
		System.out.println("线程 run 开始 时间=" + System.currentTimeMillis());
		System.out.println("线程 getId()=" + this.getId());
		System.out.println("线程 run 中 isAlive()=" + this.isAlive());
		System.out.println("线程 run 结束 时间=" + System.currentTimeMillis());
	}
}
Test.java
package com.gxx.threads.study.test6;
 
/**
 * 测试类-测试getId()、isAlive()和sleep()
 * @author Gxx
 */
public class Test {
	/**
	 * main方法
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("main主线程 开始 时间=" + System.currentTimeMillis());
		System.out.println("main主线程 getId()=" + Thread.currentThread().getId());
		MyThread thread = new MyThread();
		System.out.println("线程 start前 isAlive()=" + thread.isAlive());
		thread.start();
		System.out.println("线程 start后 isAlive()=" + thread.isAlive());
		System.out.println("main主线程 结束 时间=" + System.currentTimeMillis());
 
		try {
			Thread.sleep(1000);
			System.out.println("main主线程 sleep(1秒)后 线程 isAlive=" + thread.isAlive());
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

输出

main主线程 开始 时间=1464492826078
main主线程 getId()=1
线程 start前 isAlive()=false
线程 start后 isAlive()=true
main主线程 结束 时间=1464492826080
线程 run 开始 时间=1464492826080
线程 getId()=8
线程 run 中 isAlive()=true
线程 run 结束 时间=1464492826080
main主线程 sleep(1秒)后 线程 isAlive=false

停止线程

interrupt、isInterrupted、interrupted

  • interrupt

给线程打个中断的标记,线程继续运行

  • isInterrupted

判断线程是否有中断的标记,如果有返回true,多次调用都返回true

  • interrupted

静态方法,Thread.interrupted(),判断当前线程是否有中断的标记,如果有返回true,并清空该标记,下次再执行,则返回false

MyThread.java
package com.gxx.threads.study.test7;
 
/** 
 * 自定义线程类
 * @author Gxx
 */
public class MyThread extends Thread {
	/**
	 * 覆盖run方法
	 */
	@Override
	public void run() {
		System.out.println("run 开始");
		this.interrupt();
		System.out.println("执行this.interrupt()只是打个线程中断标记,线程还是继续往下走");
		System.out.println("this.isInterrupted()=" + this.isInterrupted());
		System.out.println("this.isInterrupted()=" + this.isInterrupted());
		System.out.println("执行静态方法Thread.interrupted()=" + Thread.interrupted() + "后清空中断状态");
		System.out.println("再执行静态方法Thread.interrupted()=" + Thread.interrupted());
		System.out.println("this.isInterrupted()=" + this.isInterrupted());
		System.out.println("run 结束");
	}
}
Test.java
package com.gxx.threads.study.test7;
 
/**
 * 测试类-测试interrupt、isInterrupted、interrupted
 * @author Gxx
 */
public class Test {
	/**
	 * main方法
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("main 开始");
		MyThread thread = new MyThread();
		thread.start();
		System.out.println("main 结束");
	}
}

输出

main 开始
main 结束
run 开始
执行this.interrupt()只是打个线程中断标记,线程还是继续往下走
this.isInterrupted()=true
this.isInterrupted()=true
执行静态方法Thread.interrupted()=true后清空中断状态
再执行静态方法Thread.interrupted()=false
this.isInterrupted()=false
run 结束

测试线程interrupt()然后sleep()

MyThread.java
package com.gxx.threads.study.test8;
 
/** 
 * 自定义线程类
 * @author Gxx
 */
public class MyThread extends Thread {
	/**
	 * 覆盖run方法
	 */
	@Override
	public void run() {
		System.out.println("run 开始");
		System.out.println("run 准备执行sleep(1000) 睡眠1秒");
		try {
			sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("run 结束");
	}
}
Test.java
package com.gxx.threads.study.test8;
 
/**
 * 测试类-测试线程interrupt()然后sleep()
 * @author Gxx
 */
public class Test {
	/**
	 * main方法
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("main 开始");
		MyThread thread = new MyThread();
		thread.start();
		thread.interrupt();
		System.out.println("main 执行thread.interrupt(),给thread打个中断标记");
		System.out.println("main 结束");
	}
}

输出

main 开始
main 执行thread.interrupt(),给thread打个中断标记
main 结束
run 开始
run 准备执行sleep(1000) 睡眠1秒
run 结束
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.gxx.threads.study.test8.MyThread.run(MyThread.java:16)

测试线程sleep()然后interrupt()

MyThread.java
package com.gxx.threads.study.test9;
 
/** 
 * 自定义线程类
 * @author Gxx
 */
public class MyThread extends Thread {
	/**
	 * 覆盖run方法
	 */
	@Override
	public void run() {
		System.out.println("run 开始");
		System.out.println("run 准备执行sleep(10000) 睡眠10秒");
		try {
			sleep(10000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("run 结束");
	}
}
Test.java
package com.gxx.threads.study.test9;
 
/**
 * 测试类-测试线程sleep()然后interrupt()
 * @author Gxx
 */
public class Test {
	/**
	 * main方法
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("main 开始");
		MyThread thread = new MyThread();
		thread.start();
		System.out.println("main 睡眠1秒");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("main 执行thread.interrupt(),给thread打个中断标记");
		thread.interrupt();
		System.out.println("main 结束");
	}
}

输出

main 开始
main 睡眠1秒
run 开始
run 准备执行sleep(10000) 睡眠10秒
main 执行thread.interrupt(),给thread打个中断标记
main 结束
run 结束
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.gxx.threads.study.test9.MyThread.run(MyThread.java:16)

抛出异常或者return来停止线程

建议使用这两种方式

if(this.isInterrupted()){
	throw new Exception();
}
if(this.interrupted()){
	return;
}

stop()暴力停止,@Deprecated作废

不建议使用,因为stop()停止线程有可能使一些清理性的工作得不到完成。另外会对锁定的对象强制解锁,导致数据得不到同步的处理,出现数据不一致的问题!比如:

synchronized public void setUserNameAndPassword(String userName, String password){
	this.userName = userName;
	Thread.sleep(1000000);//正好外面执行stop(),导致数据不一致
	this.password = password;
}
分享/技术/多线程/多线程编程核心技术整理.1464617467.txt.gz · 最后更改: 2016/05/30 22:11 由 gxx