【java多线程编程核心技术】-- 第一章:java多线程技能
第一章总结
本章介绍了Thread类的API,在使用这些API的过程中,会出现一些意想不到的情况,其实这也是多线程具有不可预知性的一个体现。学习并掌握这些常用情况,也就掌握了多线程开发的命脉与习性,是学习多线程更深层知识的基础。
第一章知识点
进程与多线程的概念及线程的优点
**进程:**操作系统管理的基本运行单元
**线程:**可以理解成为进程中独立运行的子任务,例如在QQ.exe运行时,就有很多子任务在同时允许。如:下载文件线程、传输数据线程等。
**多线程的优势:**可以充分的利用计算机cpu的资源。相较于单任务环境,多线程可以来回在多个线程任务间进行切换,避免了在单任务环境下某个任务等待自己所期待的事件而不能运行时长时间的占用cpu.
如图1-4,CPU可以在任务1和任务2之间来回切换,使任务2不必等到10秒再运行。
非线程安全会在多个线程对同一个对象的实例变量进行进行并发访问时产生,产生的后果就是“脏读”,也就是取到的数据是被更改过的;线程安全就是获得的实例变量的值是经过同步处理的,不会出现脏读的现象。
使用多线程
继承Thread类
public class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("extends Thread");
}
}
public static void main(String[] args) {
MyThread mythread = new MyThread();
mythread.start();
System.out.println("运行结束!");
}
实现Runnable接口
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("implements Runnable!");
}
}
public static void main(String[] args) {
Runnable runnable=new MyRunnable();
Thread thread=new Thread(runnable);
thread.start();
System.out.println("运行结束!");
}
说明:
1.代码的运行结果与代码执行顺序或调用顺序是无关的
2.如果多次调用start()方法,则会出现异常Exception in thread “main” java.lang.IllegalThreadStateException
3.调用start()方法,通知“线程规划器”此线程已经准备就绪,等待调用线程对应的run()方法,如果直接在main()方法里调用run()方法,那就是main线程去执行run方法里的代码,需等run方法执行完以后才能去执行后面的代码,不具备异步执行的效果。
4.执行start()方法的顺序不代表线程启动的顺序
5.Thread类也实现了Runnable接口
Thread类的8种构造方法
public Thread( );
public Thread(Runnable target);
public Thread(String name);
public Thread(Runnable target, String name);
public Thread(ThreadGroup group, Runnable target);
public Thread(ThreadGroup group, String name);
public Thread(ThreadGroup group, Runnable target, String name);
public Thread(ThreadGroup group, Runnable target, String name, long stackSize);
8个构造方法中有两个构造函数不光可以传递Runnable接口,还可以传入一个Thread类的对象,说明构造函数支持传入一个Runnable接口的对象,完全可以将一个Thread对象中的run方法交由其他的线程进行调用。
实例变量和线程安全
只有当多线程访问同一个实例变量时才存在线程安全的情况。
synchronized关键字:可以在任意对象及方法上加锁,加锁的这段代码称为“互斥区”或临界区。当一个线程想要执行同步方法里面的代码时,线程首先尝试去拿这把锁,如果能够拿到这把锁,那么这个线程就能够执行synchronized里面的代码,如果拿不到这把锁,那么这个线程就会不断尝试拿这把锁,直到能够拿到为止,而且是有多个线程同时去争抢这把锁。
常用API介绍
currentThread()
返回代码段正在被哪个线程调用的信息 Thread.currentThread().getName()
isAlive()
判断当前的线程是否处于活动状态。
sleep()
方法sleep()的作用是在指定的毫秒数让当前“正在执行的线程”休眠,但是不会释放锁,这个“正在执行的线程”是指this.currentThread()返回的线程。
getId()
取得线程的唯一标识。
停止线程()
interrupt():在当前线程中打了一个停止的标记,并不是真正的停止线程
interrupted():静态方法,测试当前线程是否已经中断,执行后具有将状态标志清除为false的功能
isInterrupted():非静态方法,测试线程是否已经中断,但不清除状态标志
异常法-停止线程
public class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 500000; i++) {
if (this.interrupted()) {
System.out.println("已经是停止状态了!我要退出了!");
throw new InterruptedException();
}
System.out.println("i=" + (i + 1));
}
System.out.println("我在for下面");
} catch (InterruptedException e) {
System.out.println("进MyThread.java类run方法中的catch了!");
e.printStackTrace();
}
}
}
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(2000);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
运行结果
i=146942
i=146943
i=146944
i=146945
i=146946
i=146947
i=146948
end!
已经是停止状态了!我要退出了!
进MyThread.java类run方法中的catch了!
java.lang.InterruptedException
at sort.MyThread.run(MyThread.java:12)
在沉睡中停止
public class MyThread extends Thread {
@Override
public void run() {
super.run();
try {
System.out.println("run begin");
Thread.sleep(200000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("在沉睡中被停止!进入catch!当前线程状态:"+this.isInterrupted());
e.printStackTrace();
}
}
}
public class Run {
public static void main(String[] args) {
try {
MyThread thread = new MyThread();
thread.start();
Thread.sleep(200);
thread.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end!");
}
}
运行结果
run begin
end!
在沉睡中被停止!进入catch!当前线程状态:false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at sort.MyThread.run(MyThread.java:9)
暴力停止线程-stop()
stop()已经作废,可能会出现一些意想不到的结果.
用return停止线程
public class MyThread extends Thread {
@Override
public void run() {
while (true) {
if (this.isInterrupted()) {
System.out.println("停止了!");
return;
}
System.out.println("timer=" + System.currentTimeMillis());
}
}
}
暂停线程
suspend()方法:暂停线程
resume():恢复线程
缺点:
1.独占:极易造成公共的同步对象的独占,使其他线程无法访问公共同步对象
2.不同步
建议不要使用
yield()
yield()方法的作用是放弃当前的CPU资源,将它让给其它的任务去占用CPU执行时间,调用之后重新进入就绪状态,参与到CPU的竞争的当中。
线程的优先级
设置方法-setPriority()
优先级分为1~10,默认值为5 (NORM_PRIORITY=5)
线程优先级的继承特性:例如A线程启动B线程,则B线程的优先级与A是一样的
优先级具有规则性:CPU尽量将执行资源让给优先级比较高的线程
优先级具有随机性:优先级较高的线程并不一定每一次都先执行完run()方法中的任务,线程的优先级与调用run()方法顺序无关
守护线程
Java中有两种线程,用户线程与守护线程,典型的守护线程就是垃圾回收器,当进程中不存在非守护线程了,则守护线程自动销毁。