多线程编程理解
1、引发线程问题的核心是因为资源共享。
2、线程分为两种:
2.1、应用线程,也就是通常我们理解的线程。
2.2、守护线程,是一种特殊的线程,在所有应用线程结束后才会停止运行。如JVM的GC线程。定义和使用都是一样,需要注意的是默认的线程都是应用线程。需要在线程执行前设定线程为守护线程,否则会报错。
3、线程的定义方式:
3.1、继承Thread类。
3.2、实现Runable接口。
区别是:继承和接口实现的区别。
4、每个线程在执行前都能计算出所需的栈大小,计算单位:槽。索引占位符和基本类型(除8个字节的long,double)都占用1个槽,long和double占用2个槽。线程的内存分配在虚拟机栈中,线程数取决于虚拟机栈大小。
5、类只有实例变量才存在线程问题,方法变量不存在线程问题。
6、锁是Object拥有,在对象的对象头中会有专门的标识,识别该对象是否被其他线程占用。
7、类锁、方法锁、块锁实质都是获取对象锁,也是共用的,Synchronize获取对象锁是可重入的:一个线程在获取对象锁后,在未释放对象锁时可以同时再获取该对象锁,同步方法中可以调用其他同步方法。
8、多线程只有在对象标识了同步的方法、块内才会同步,其他则是采用异步。
9、Synchronized是基于虚拟机指令:monitorenter和monitorexit指令,可以有多次(重入锁),但指令都是成双成对出现。这里特别注意:在出现异常时,会自动释放对象锁。
########################################################################################################################################################################
10、如何一个线程停止另外一个线程:
10.1、每个线程对象都有一个中断标识,一个线程不能停止另外一个线程,只能通过线程本身根据中断标识来决定自己知否停止(这里的停止并不是stop这样的强行停止,而是通过中断标识来跳过某些执行操作,线程还是会继续执行下去,只是跳过了中断标识决定的部分:if(中断标识){跳过的部分} ; 仍然会执行的部分),可以在另外一个线程内,设置这个中断标识,这样就达到了,在一个线程中停止另外一个线程。
10.2、线程判断自己是否拥有中断标识方法:
10.2.1、threadInstance.interrupted():检测线程实例知否有中断标识,如果有返回true,并且会清除该线程的中断标识。
10.2.2、threadInstance.isInterrupted():检测线程实例是否有中断标识,返回true or false,不会清楚该线程的中断标识。
10.2.3、threadInstance.interrupt():给线程实例添加中断标识,在外部线程添加中断标识后,该线程立马可见。中断标识只是起标识作用,具体线程是否停止由线程自己决定。想要不执行后面的代码也可以通过抛出异常,然后通过添加异常抓捕方式来实现跳跃。
案例:
/**
* 实现线程A中断线程B
*/
public class ThreadInterruptTest {
public static void main(String[] args) throws InterruptedException {
ThreadB b = new ThreadB();
b.setName("线程B");
ThreadA a = new ThreadA(b);
b.start();
Thread.sleep(10*1000);
a.start();
}
public static class ThreadA extends Thread{
private Thread thread_B;
public ThreadA(Thread b) {
this.thread_B = b;
}
@Override
public void run() {
super.run();
thread_B.interrupt();
}
}
public static class ThreadB extends Thread{
@Override
public void run() {
super.run();
for (int i = 0; i < 10000000; i++) {
if (this.isInterrupted()) {
System.out.println("被A线程干掉了");
break;
}
System.out.println(Thread.currentThread().getName()+"偷了"+i+"块钱");
}
}
}
}
使用上面这种情况需要线程B不断的判断是否有中断标识,如果线程B睡着了sleep().sleep()方法使线程暂停一段时间,并不会释放对象锁,同时他还会不断的轮询当前线程是否有中断标识,如果有中断标识就会抛出InterruptedException,这也是我们为什么在使用sleep()方法时需要添加异常捕捉或抛出异常。
案例:
/**
* 实现线程A中断线程B
*/
public class ThreadInterruptTest {
public static void main(String[] args) throws InterruptedException {
ThreadB b = new ThreadB();
b.setName("小懒虫");
ThreadA a = new ThreadA(b);
b.start();
Thread.sleep(10*1000);
a.start();
}
public static class ThreadA extends Thread{
private Thread thread_B;
public ThreadA(Thread b) {
this.thread_B = b;
}
@Override
public void run() {
super.run();
thread_B.interrupt();
}
}
public static class ThreadB extends Thread{
@Override
public void run() {
super.run();
try {
Thread.sleep(200*1000);
} catch (InterruptedException e) {
System.out.println("小懒虫睡觉被叫醒了!");
}
}
}
}
以上两种情况均能知道线程B运行到了哪里被停止了。
10.3、threadInstance.stop()方法同样能够停止线程,暴力停止,随时,强制。弊端是不能够确定线程运行到哪里就被停止了,jdk1.7后的版本均@deprecated注释,不建议使用。
stop()方法,会抛出ThreadDeath 这个方法被VM自行处理:
// The VM can handle all thread states
stop0(new ThreadDeath()); 这个为native标识的本地方法。
这里有并没有显示要求抓捕这个异常,但是却可以显示的抓捕到异常。猜测:try-cath抓捕的异常只有使用throw new Exception() 和 throws Exception 的方式编译器要求需要显示抓捕。
10.4、suspend() 和 resume() 是挂起线程和恢复线程,都是独占锁,在挂起时不会释放对象锁。注意:System.out.println()这个方法是线程安全的,会让当前线程获取锁。
部分源码:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
10.5、

浙公网安备 33010602011771号