第一章 多线程技能
摘自《Java多线程编程核心技术》一书
线程具有随机特性——使用多线程技术时,代码运行结果与代码执行顺序是无关的。
线程还有不可预知性。
- CPU随机调用run方法
- 注意:如果多次调用start方法会出现异常
start()方法通知“线程规划器”,准备就绪,等调用run()方法。
运行顺序:创建对象实例化–>构造方法–>start()–>run()
使用例子:
public class MyThread extends Thread{
//构造函数
public MyThread(){
super();
}
@Override
public void run(){
super.run();
}
}
public class Run{
public static void main(String[] args){
MyThread a = new MyThread();
a.start();
}
}
自定义线程类中的实例变量对其他线程有共享和不共享
- 不共享数据—-多个线程不会同时访问同一个实例变量
共享数据—-多个线程可以同时访问同一个变量
- 使用关键字上锁synchronized,使抢到的线程完成整个run的操作之后才能被其他线程去抢
- 不用关键字可能在赋值过程中被插队造成数据混乱
- 用法:
synchronized public void run()
非线程安全
多个线程对同一个对象的同一个实例变量进行操作,导致值被更改、值不同步的情况出现。
i++与println()异常
例子:
run()方法里面
System.out.println("i=" + (i--) + "threadName=" + Thread.currentThread().getName());
println()在内部是同步的,但是i–会在println()之前发生,所以有概率出现非线程安全问题。
currentThread()方法
获取当前线程的名字Thread.currentThread().getName()
当用start()方法时,run()会自动调用
在main方法中使用run,会由main主线程调用
例:
public static void main(String[] args){
MyThread mythread = new MyThread();
myThread.run();
}
isAlive()方法
判断当前是否处于活动状态——已经启动,尚未终止
currentThread()和this的差异
currentThread()是指当前代码段正由那个线程调用
- MyThread的构造函数由main主线程调用
- run()是由thread线程调用的,默认名为Thread-0
例子:
public class MyThread02 extends Thread {
public MyThread02() {
System.out.println("init curr: " + Thread.currentThread().getName());
System.out.println("init this: "+this.getName());
}
@Override
public void run() {
System.out.println("run curr: " + Thread.currentThread().getName());
System.out.println("run this: "+this.getName());
}
}
public class Test02 {
public static void main(String[] args) {
MyThread02 target = new MyThread02();
Thread thread = new Thread(target);
thread.setName("A");
thread.start();
}
}
运行结果
init curr: main
init this: Thread-0
run curr: A
run this: Thread-0
this其实是Thread类中有一个构造函数:
//传入target,以thread-0为默认初始值
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
public void run() {
if (target != null) {
target.run();
}
}
当用Runnable对象作为参数去实例化一个Thread对象时,即在执行run方法中,其实是target在执行的。 this引用的是target
this.getName() = target.getName()–>Thread-0
sleep()方法
在指定毫秒内让正在执行的线程——this.currentThread()休眠
getId()方法
获取线程的id值
线程的停止
interrupt()方法–使线程暂时停止
判断线程是否停止:
interrupted()–判断当前线程是否停止状态
- 清除状态的功能
- 第一次用的时候,会把中断状态清除,所以第二次用会返回false
isInterrupted()–判断线程thread对象是否停止状态
- 没有清除状态的功能
注意:
- 用stop()是不安全的做法
- 使用interrupt()只是使线程暂时停止
解决语句继续运行方法–用catch语句停止线程(推荐)
在run方法中用try-catch语句
try{
for(...){
if(this.interrupted()){
System.out.println("已经是停止状态,下一句就是退出了!");
throw new InterruptedException();
}
}
} catch (InterruptedException e){
System.out.println("被catch了!");
e.printStackTrace();
}
sleep中停止
在sleep中停止线程(遇到interrupt)会进入catch语句,并清除状态
- 另一种情况:先interrupt()再sleep,就是先贴上了中断标志,线程还是继续运行的,直到遇到sleep就被catch了。
暴力停止–stop()
可能会抛出java.lang.ThreadDeath异常,可能导致请理性工作得不到完成,或对锁定对象“解锁”,导致数据等不到同步处理,出现数据不一致问题。
- stop()释放锁可能会导致数据不一致
- 在JDK被表明是“作废/过期”的方法
使用return停止线程
public void run(){
while(true){
if(this.isInterrupted()){
System.out.println("停止了!");
return;//此处的return为终止后面的代码执行
}
System.out.println("timer="+ System.currentTimeMillis());
}
}
main方法:
Thread.sleep(2000);
thread.interrupt();
等了2秒之后,贴上中止标志,然后进入if语句,遇到return,线程就停止了。
暂停线程
suspend()暂停线程–过期作废了
resume()恢复线程
- 这俩方法的缺点
- 独占
- 用suspend暂停上锁synchronized的方法时,其他线程无法访问该方法
- 不同步
- 独占
yield()方法
放弃当前CPU资源,但是不确定放弃多久
划分优先级
优先级有1~10,如果超出范围会抛出异常throw new IllegalArgumentException()
设置优先级–setPriority()
获取优先级–getPriority()
1、优先级具有继承性
在A线程中启动B线程,B的优先级与A一样
2、优先级具有规则性
哪个线程先执行完,和代码调用顺序无关
3、优先级具有随机性
尽量让优先级高的线程先执行,但不是让它执行完再让另一个线程去执行。
优先级高的运行的快
守护线程
当进程中没有非守护线程,守护线程就会自动销毁。
典型的守护线程——垃圾回收线程,最典型的应用——GC(垃圾回收器)
(仅为个人读书总结,有错欢迎指正)