基本概念
线程与进程
线程:系统资源分配的最小单位,使用独立的数据空间
进程:程序执行的最小单位,线程共享进程的数据空间
并发与并行
并发:同一时间段,多个任务都在执行 (单位时间内不一定同时执行);
并行:同单位时间内多个任务同时执行
并发的好处与坏处
JVM的内存模型与JMM(Java Memory Model)
JMM定义了程序中变量的访问规则。所有的共享变量都存储在主内存中,每个线程有自己的工作内存,工作内存保存的是共享变量的副本。线程对变量的读写操作必须在自己的工作内存中进行,而不能直接读写主内存中的变量
线程的基本操作
线程的创建方式
- 实现Runnable接口
// 创建任务Runnable task = () -> {...}// 运行任务new Thread(task).start()
- 继承Thread类
class Task extends Thread{public void run(){...}}Task task = new Taskl();task.start();
线程的状态(Thread.State)
from 《Java 并发编程艺术》
Thread.run() 和 Thread.start()的区别
- Thread.run()
public void run() {if (target != null) {// target的类型是Runnable,这里调用的是Runnable的run方法target.run();}}
- Thread.start()
public synchronized void start() {// 如果线程的状态不是0(not yet started),则会抛出运行异常,即一个线程不能够多次调用start方法if (threadStatus != 0)throw new IllegalThreadStateException();group.add(this);boolean started = false;try {start0();started = true;} finally {try {if (!started) {group.threadStartFailed(this);}} catch (Throwable ignore) {/* do nothing. If start0 threw a Throwable thenit will be passed up the call stack */}}}
直接调用run方法是串行执行对象的run方法体.调用start方法是开启一个线程并使得线程处于RUNNABLE状态,当分配到时间片后就会执行run方法体.
wait(等待) and notify(通知)
@Testpublic void waitAndNotify() {final String flag = "";// 线程1等待直到被唤醒后打印消息Runnable task1 = () -> {try {// 去掉synchronized,抛出java.lang.IllegalMonitorStateExceptionsynchronized (flag) {flag.wait();}System.out.println("I am notified!!");} catch (InterruptedException e) {e.printStackTrace();}};// 线程2等待2s后唤起线程1Runnable task2 = () -> {try {Thread.sleep(2000);// 去掉synchronized,抛出java.lang.IllegalMonitorStateExceptionsynchronized (flag) {flag.notify();}} catch (InterruptedException e) {e.printStackTrace();}};// 启动Thread t1 = new Thread(task1);Thread t2 = new Thread(task2);t1.start();t2.start();// 下面这段代码在junit测试中是必须的,如果是在main中则没有必要try {// 将线程1加入到当前线程,即当前测试线程会阻塞等待线程1运行完毕t1.join();} catch (InterruptedException e) {e.printStackTrace();}}
- wait和notify是Object中的方法
- wait和notify执行之前都需要获得目标对象的监视器(使用synchronized),执行后会释放监视器.
- wait和sleep的区别和联系:
- 联系:两者都可以暂停线程的执行
- 区别:
- sleep没有释放锁,而wait释放了锁;
- wait方法被调用后,线程不会自动苏醒,需要别的线程调用同一对象上的notify或notifyAll方法.而sleep执行完成后,线程会自动苏醒;
- 所以,wait常被用于线程之间的通信.sleep常被用于暂停执行
suspend(挂起) and resume(继续执行)
- suspend 的线程必须等到resume才能够继续执行,另外,suspend的线程在挂起时不会释放锁资源,因此可能会造成死锁(一直占用锁资源不释放)
- 这两个方法被废弃了
@Testpublic void suspendAndResume() throws InterruptedException {Object obj = new Object();Runnable task = () -> {synchronized(obj){Thread thread = Thread.currentThread();System.out.println(thread.getName() + " will be suspended!");thread.suspend();System.out.println(thread.getName() + " was resumed!");}};Thread t1 = new Thread(task, "task1");t1.start();// 如果注释掉part1则可能会造成死锁.由于resume先于suspend被调用,也就是一直处于被挂起状态// 记为part1 >>>try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}// <<<t1.resume();t1.join();}
join and yield
- join是谁加入时?谁等待?
public static void main(String[] args){int count = 0;Thread t = new Thread(() -> {for(int i = 0; i < 1000; i++){count++;}});t.join(); // 将t这一线程加入到main(当前线程),所以main线程等待t完成}
stop and interrupt
- stop和interrupt的不同:
Thread.interrupt()被调用时不会使目标线程立即退出,只是通知目标线程中断,也就是设置中断标志位。而Thread.stop()是立即终止线程(操作过程中立即终止可能会造成数据不一致)。 - Thread.sleep过程如果中断会抛出异常,并且它会清除中断标志(所以如有需要应重新加上中断标志)
- 常用方法
- void interrupt()
- boolean isInterrupted() 判断是否被中断,不会清除标志位
- static boolean interrupted() 判断是否被中断,并清除中断标志位
- stop方法被废弃了
@Testpublic void stopAndInterrupt() throws InterruptedException {@ToStringclass People {String name = "001";String id = "001";}People p1 = new People();People p2 = new People();Runnable t1 = () -> {Thread thread = Thread.currentThread();while(!thread.isInterrupted()){System.out.println(thread.getName());p1.name = "002";try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}p1.id = "002";}};Runnable t2 = () -> {Thread thread = Thread.currentThread();while(!thread.isInterrupted()){System.out.println(thread.getName());p2.name = "002";try {Thread.sleep(3000);} catch (InterruptedException e) {// Thread.sleep由于抛出异常它会清除中断标志,所以需要重新加上thread.interrupt();}p2.id = "002";}};/** 使用stop暴力停止线程:数据会不一致 **/Thread thread1 = new Thread(t1, "thread1");thread1.start();Thread.currentThread().sleep(1000);thread1.stop();System.out.println(p1);/** 使用interrupt中断线程 **/Thread thread2 = new Thread(t2, "thread2");thread2.start();Thread.currentThread().sleep(1000);thread2.interrupt();thread2.join(); // 必须阻塞,否则也不能获得正确的结果(因为thread2还没有修改 )System.out.println(p2);}
结果
thread1People(name=002, id=001)thread2People(name=002, id=002)
