本文内容基于《Java多线程编程核心技术》,高洪岩著。
一、基础
1. 进程
- 进程是操作系统结构的基础,是一次程序的执行,是一个程序及其数据在处理机上顺序执行时所发生的活动,是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
2. 线程
- 线程可以理解成是在进程中独立运行的子任务,使用多线程技术后,可以在同一时间内运行更多不同种类的任务。
3. 实现多线程编程的方式
- 继承Thread类;
public class MyThread extends Thread {
@Override
public void run() {
super.run();
//do something
}
}
- 实现Runnable接口;
public class MyThread implements Runnable {
@Override
public void run() {
//do something
}
}
4. start()方法
- 通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法,异步执行;
- 如果多次调用start()方法,则会出现异常java.lang.IllegalThreadStateException;
- 执行start()方法的顺序并不代表线程启动的顺序;
5. run()方法
- 此线程对象不交给“线程规划器”来进行处理,而是由main主线程来调用run()方法,同步执行;
6. currentThread()方法
- 返回代码段正在被哪个线程调用的信息;
7. isAlive()方法
- 判断当前的线程是否处于活动状态;
- 活动状态就是线程已经启动且尚未终止;
public class MyThread extends Thread {
public MyThread() {
System.out.println(Thread.currentThread().getName()); //main //main
System.out.println(Thread.currentThread().isAlive()); //true //true
System.out.println(this.getName()); //Thread-0 //Thread-0
System.out.println(this.isAlive()); //false //false
}
@Override
public void run() {
super.run();
System.out.println(Thread.currentThread().getName()); //Thread-0 //Thread-1
System.out.println(Thread.currentThread().isAlive()); //true //true
System.out.println(this.getName()); //Thread-0 //Thread-0
System.out.println(this.isAlive()); //true //false
}
}
注:
- 第一种运行结果的调用方式是:new MyThread1().start();
- 第二种运行结果的调用方式是:new Thread(new MyThread1()).start();
- 说明在构造方法里,Thread表示初始化该线程的线程,this表示该线程;
- 说明在run()方法里,Thread表示调用该线程run()方法的线程,this表示该线程;
8. sleep()方法
- 在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行),这个“正在执行的线程”就是Thread.currentThread()方法返回的线程;
- 不释放锁;
9. getId()方法
- 取得线程的唯一标识;
10. 停止线程的3种方式
- 使用退出标志,使线程正常退出,也就是当run()方法完成后线程终止;
- 使用interrupt()方法中断线程;
public class MyThread extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 500000; i++) {
if (this.interrupted()) { //判断中断标志
System.out.println("interrrupt");
//break; //还会执行循环外面的代码,线程没有马上停止
//return; //线程马上停止,不推荐,因为可以在catch块中做一些处理等
throw new RuntimeException("interrupt"); //线程马上停止,推荐
}
System.out.println(i);
}
System.out.println("end");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Main{
public static void main(String[] args) {
try {
MyThread myThread = new MyThread();
myThread.start(); //启动线程
Thread.sleep(1000); //让线程执行1秒
myThread.interrupt(); //中断线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 使用stop()方法强行终止线程,不推荐使用;
11. interrupt()方法
- 在当前线程中打一个停止的标记,并不是真的停止线程;
- this.interrupted()方法:测试当前线程是否已经中断,当前线程是指运行this.interrupted()的方法,执行后具有将状态标志清除为false的功能;
- this.isInterrupted()方法:测试某个线程是否已经中断,但不清除状态标志;
- 被打上停止标志的线程如果遇上sleep()方法或者正在sleep()的线程被打上停止标志,则会进入catch语句,并且将状态标志清除为false;
12. stop()方法
- 强行停止线程,已废弃方法;
- 因为如果强制让线程停止则可能使一些清理性的工作得不到完成,另一个情况是会释放锁,可能导致数据出现不一致问题;
13. 暂停线程
- 可以使用suspend()方法暂停线程,使用resume()方法恢复线程的执行;
- 如果使用不当,极易造成公共的同步对象的独占,使得其他线程无法访问公共同步对象;
- 也容易出现因为线程的暂停而导致数据不同步的情况;
14. yield()方法
- 放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间,但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。
15. 线程的优先级
- 操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务;
- 设置线程优先级有助于帮”线程规划器“确定在下一次选择哪一个线程来优先执行;
- 设置线程的优先级使用setPriority()方法;
- Java中,线程的优先级分为1~10个等级,如果小于1或大于10,则会抛出IllegalArgumentException;
- Java中,线程的优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A线程一样;
- 高优先级的线程总是大部分先执行完,但不代表高优先级的线程全部先执行完;
16. 守护线程
- 守护线程是一种特殊的线程,它的特性有陪伴的含义,当进程中不存在非守护线程了,则守护线程自动销毁;
- 典型的守护线程就是垃圾回收线程;
- 守护线程的作用是为其他线程的运行提供便利服务;
- 通过thread.setDaemon(true)方法设置守护线程;
二、 对象及变量的并发访问
1. 非线程安全
- 指多个线程对同一个对象中的同一个实例变量进行操作时会出现指被更改、值不同步的情况,进而影响程序的执行流程。
2. synchronized同步方法
- 直接将synchronized关键字加在需要同步的方法上;
- synchronized同步方法是对当前对象进行加锁;
3. synchronized同步代码块
- synchronized(this){}形式的同步代码块也是对当前对象进行加锁;
- synchronized(非this){}形式的同步代码块是对某一个非tihs对象进行加锁;
- synchronized(*.class){}形式的同步代码块是对Class类进行加锁,效果等同于synchronized同步静态方法;
4. synchronized同步静态方法
- 将synchronized关键字加在需要同步的static方法上;
- 相当于对当前的*.java文件对应的Class类进行加锁;
- Class锁可以对类的所有对象实例起作用;
5. 总结
- 已知Service类与Main类;
public class Service {
public void testMethod1() {
try {
System.out.println(Thread.currentThread().getName() + " 进入异步方法1 " + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " 离开异步方法1 " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public void testMethod2() {
try {
System.out.println(Thread.currentThread().getName() + " 进入synchronized同步方法2 " + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " 离开synchronized同步方法2 " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void testMethod3() {
System.out.println(Thread.currentThread().getName() + " 进入方法3 " + System.currentTimeMillis());
synchronized (this) {
try {
System.out.println(Thread.currentThread().getName() + " 进入方法3中的synchronized(this)同步代码块 " + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " 离开方法3中的synchronized(this)同步代码块 " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 离开方法3 " + System.currentTimeMillis());
}
public void testMethod4(Object lock) {
System.out.println(Thread.currentThread().getName() + " 进入方法4 " + System.currentTimeMillis());
synchronized (lock) {
try {
System.out.println(Thread.currentThread().getName() + " 进入方法4中的synchronized(非this)同步代码块 " + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " 离开方法4中的synchronized(非this)同步代码块 " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 离开方法4 " + System.currentTimeMillis());
}
synchronized public static void testMethod5() {
try {
System.out.println(Thread.currentThread().getName() + " 进入synchronized同步static静态方法5 " + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " 离开synchronized同步static静态方法5 " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void testMethod6() {
System.out.println(Thread.currentThread().getName() + " 进入方法6 " + System.currentTimeMillis());
synchronized (Service.class) {
try {
System.out.println(Thread.currentThread().getName() + " 进入方法6中的synchronized(*.class)同步代码块 " + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " 离开方法6中的synchronized(*.class)同步代码块 " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 离开方法6 " + System.currentTimeMillis());
}
}
public class Main {
public static void main(String[] args) {
Service service1 = new Service();
Service service2 = new Service();
Runnable runnable1 = new Runnable() {
@Override
public void run() {
service1.testMethod1();
}
};
Runnable run