多线程学习笔记(一)基本概念和方法
1实现多线程的两种方式
(1)继承Thread类,重写run方法
(2)实现Runnable接口,重写run方法
Thread类也实现了Runnable接口 。这就意味着构造函数Thread(Runnable target)不光可以传入Runnable接口的对象,还可以传入一个Thread类的对象,这样做完全可以将一个Thread对象中的run()方法交由其他线程进行调用。
2线程调用
(1)多线程的代码执行順序与调用順序无关,执行start()方法的順序不代表线程启动的順序
(2)Thread的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法。具有异步执行的效果,
如果调用代码thread.run()就还是同步执行。
3多线程间的变量共享
(1)静态变量和实例变量可能多个线程共享
类变量:用static修饰
实例变量:没有static修饰的成员变量,是全局变量,包括基本数据类型和对象,保存在堆中。
局部变量:方法中的变量。每个线程有自己独立的存储空间,无法共享。
根据JMM的设计,系统存在一个主内存(Main Memory),Java中所有实例变量都储存在主存中,对于所有线程都是共享的。
每条线程都有自己的工作内存(Working Memory),工作内存由缓存和堆栈两部分组成,缓存中保存的是主存中变量的拷贝,
缓存可能并不总和主存同步,也就是缓存中变量的修改可能没有立刻写到主存中;
堆栈中保存的是线程的局部变量,线程之间无法相互直接访问堆栈中的变量。
小结:实例变量存放在堆中,保存在系统的主内存中,每个线程拷贝到自己的缓存中,1可以共享。2缓存可能并不总和主存同步,也就是缓存中变量的修改可能没有立刻写到主存中;
局部变量存放在线程的栈中,线程之间无法相互直接访问堆栈中的变量。
非线程安全:多个线程对同一对象中的实例变量进行并发访问,出现脏读(取到的数据时被修改过的)。
非线程安全存在于实例变量中,如果是方法内部的私有变量,则不存在非线程安全问题。
线程共享示例:
这种情况,每个线程都有自己的实例对象,此时变量不共享。
这种情况,多个线程共享一个实例, 共享该实例的实例变量。
这时需要对多个线程之间进行同步。可以通过synchronized关键字实现。synchronized关键字可以在任意对象和方法上加锁,加锁的这段代码称“互斥区”和“临界区”。
4多线程几个基础方法
(1)currentThread()方法:返回代码段正在被哪个线程调用的信息。
this则指所在的线程。
例如:
(2)isAlive()方法:判断线程是否处于活动状态(已经启动且尚未终止)
(3)sleep()方法:让当前“正在执行的线程”休眠(暂停执行)指定毫秒数。这个正在执行的线程是指this.currentThread()返回的线程。相当于(Thread.currentThread())。
(4)getId()方法:取得线程的唯一标识。
5停止线程
(1)使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
(2)使用stop方法强行终止。会抛出java.lang.ThreadDeath异常,不建议使用。
因为:如果强制停止线程可能使一些清理性工作得不到完成。
对锁定的对象进行了“解锁”,导致数据得不到同步处理,出现数据不一致的问题。
(3)判断线程是否是停止状态
interrupted:测试当前线程是否已经中断,当前线程指运行interrupted方法的线程(不是调interrupted方法的那个线程,是interrupt在哪个线程里面运行就是哪个线程)。执行后具有将状态标志清除为false的功能
isInterrupted:测试线程Thread对象(调用isInterrupted方法的线程)是否已中断,不清除状态标志。
(4)interrupt,interrupted判断结合主动抛异常
(5)interrupt结合sleep
如果在sleep()状态下,interrupt某一线程会自动抛异常,并清除停止状态值,使之变为false
如果先interrupt某一线程,遇到了sleep方法,也会自动抛异常。停止线程。
(6)interrupt,interrupted判断结合return
不过还是建议使用抛异常法,使用异常流能更好地控制程序的运行流程,不至于代码中出现很多个return;造成污染
6suspend()与resume()
7yield()
放弃当前cpu资源,将其让给其他的任务去占用cpu执行时间。放弃时间不确定。
8线程优先级
(1)1-10级
setPriority()方法设置
(2)线程优先级具有继承性
比如a线程启动b线程,则b线程的优先级与a是一样的。
9守护线程
当进程中不存在非守护线程了,则守护线程自动销毁。典型的是GC
2