Java多线程
1. 进程和线程分别是什么?
进程:受操作系统管理的最小运行单元,任务管理器中的一个个exe可以理解成一个进程。
线程:进程中独立运行的子任务。如qq.exe运行时有很多线程在同时运行,发表情、好友视频、上传文件、聊天、听音乐等。
2. 使用多线程的优点?
- 提高系统的运行效率:
最大限度的利用cpu的空闲时间来处理其他的任务。一边编辑文档一边让系统处理打印机打印的数据。
注意:多线程异步,代码顺序不是执行顺序。
3.start()和run()的区别
- 专业回答:start()方法是异步的,而run()方法是同步的。
- start()方法会通知“线程规划器”该线程已经准备就绪,等待调用线程对象的run()方法。Thead.currentThread是该线程。
- run()方法则会绕过“线程规划器”,由main线程直接调用run()方法。Thread.currentThread是main线程。
比喻:假设我们是在酒店的厨房,run()方法体内容是一道菜的材料,main线程是老板,该线程是厨师,老板雇佣这个厨师(相当于main线程实例化一个该线程对象),老板使用start()方法就是老板通知这个厨师,让厨师去烧这道菜。老板使用run()方法,就是老板拿着这些菜的材料(run方法体),自己烧!start()方法是老板点菜,厨师烧菜,俩人,异步的!run()方法是老板点菜,点完自己烧,一个人,同步的!
* 注意:main线程中如果多个线程启动,它们的start()顺序不代表执行顺序。*
4.使用那种方式开发多线程好?
- 有两种方式开发多线程,一种继承Threadl,一种实现Runnable接口。实现Runnable接口的方法会好些。
- 原因:继承Thread的方法具有“局限性”,Java是“单根继承”,为了改变这种限制,可以使用多态,即Runnable接口。
- 另外,源码中查看,Thread是实现了Runnable接口的,所以构造函数Thread(runnable target)中可以传入一个Thread类的对象。
5.什么是非线程安全?
- 非线程安全:指多个线程对同一个对象中的同一个实例变量进行操作时会出现“值被更改”,“值不同步”的情况,进而影响程序的执行流程。
6.如何设计线程安全?
- 给操作实例变量的run方法上加同步锁,synchronized关键字。
- 这样就能保证每次只有一个线程对该实例变量操作。换句话就是,式多个线程在执行run()方法时,以排队的方式进行处理。
synchronized可以在任意的对象及方法上加锁,被锁住的这段代码被称为“互斥区”,“临界区”。
7.currentThread()方法
返回代码段正在被哪个线程调用。
8.isAlive()方法
判断当前的线程是否处于活动状态。
活动状态:线程已经启动,尚未终止。线程处于正在运行,或者准备运行的状态。
9.sleep()方法
Thread.sleep(1000);//sleep静态方法,当前线程休眠(暂停执行)1s
10.getId()
取得线程的唯一标志,
main线程Id为1
2017/11/21 10:08:29
停止线程
多线程开发中,对线程的停止进行处理需要一些技巧哦。
Java中有3种方式可以终止运行的线程:
1. 退出标志
2. stop()
3. interrupt()
使用退出标志,线程正常退出,run方法完成后线程终止。
stop()方法属于暴力停止,和suspend(),resume()方法一样属于过期作废的方法,不推荐使用。使用他们可能产生不可预料的结果。
大多数情况下我们使用interrupt()停止一个线程。
1.使用interrupt()终止线程的过程?
在代码中使用myThread.interrupt()方法并不会直接停止线程,只是在当前时间给myThread打上一个状态标志=停止。
所以,我们需要在myThread的run方法中对这个状态标志进行判断,是否标志=停止,然后做处理。
2.这个状态标志要如何判断?
Thread.java中提供两种方式:
1) this.interrupted():测试当前线程是否已经中断
2) this.isInterrupted():测试线程是否已经中断。
两者区别:第一个是 static 方法,第二个不是。造成的影响是:假设在main中初始化并start了myThread线程。然后你用
myThread.interrupt();
system.out.println("是否停止: "+myThread.interrupted());
然后发现打印出来的是:
是否停止:false
啊哦~,不是你想要的true。原因就是interrupted()方法是静态的,不管你写的是mythread.interrupted()还是Thread.interrupted(),程序都只会去判断currentThread的状态,就是判断这里的main线程是否停止。
所以,咱可以把输出语句里的判断改为:
myThread.interrupt();
system.out.println("是否停止: "+myThread.isInterrupted());
结果就是true。
还有个事情,停止当前线程用:
Thread.currentThread().interrupt();
system.out.println("是否停止1: "+Thread.interrupted());
system.out.println("是否停止2: "+Thread.interrupted());
然后结果为:
是否停止1:true
是否停止2:false
1和2结果怎么会不一样嘞?interrupted()方法调用,有清除状态的功能。2变false。
科研质量办的人真是盛气凌人啊。bula~bula~bula~好讨厌哦
3.具体停止线程的方法?
3.1异常法
MyThread.java:
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();
}
}
}
Run.java:
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=66316
i=66317
i=66318
i=66319
已经是停止状态了!我要退出了!
进MyThread.java类run方法中的catch了!
java.lang.InterruptedException
end!
at exthread.MyThread.run(MyThread.java:11)
可以throw new InterruptedException()终止程序。
3.2 沉睡中停止
线程在sleep()时遭遇了myThread.interrupt(),会抛出 InterruptedException。
3.3 stop()暴力停止
stop后抛出java.lang.ThreadDeath异常。此异常不需要显示捕捉。
前头说了,为什么不喜欢用stop(),因为:
如果强制让线程停止则有可能使一些清理性的工作得不到完成。另外锁定的对象得以“解锁”,数据得不到同步的处理,出现数据不一致的情况。
举个栗子:我有三个类,SynObject,MyThread,Run类
SynObject是一个普通的类,有属性:用户名=“a”;密码=”aa”,有可以设置用户名和密码的方法:print(String username,String password)
MyThread类的构造方法中实例化这个SynObject,并且在run方法中调用SynObject的方法来设置用户名=“b”和密码=”bb”。
Run类是我的测试类,创建了一个SynObject(),并且初始化了一个MyThread(synObject)类。
假设SynObject的这个方法里,设置用户名和密码,中间需要20S。而我在Run类main函数里,初始化了线程类,这个线程跑了5s中就被我强制stop()了,最后我在main函数结尾输出的时候,发现SynObject的用户名=”b”和密码=”aa”,我只成功的修改了用户名。数据不一致了。
3.4 return和interrupt()结合停止线程
public class MyThread extends Thread {
@Override
public void run() {
while (true) {
if (this.isInterrupted()) {
System.out.println("停止了!");
return;
}
System.out.println("timer=" + System.currentTimeMillis());
}
}
}
这个和抛异常法相比,还是抛异常好些。在catch块中我们将异常上抛,使线程停止的事件得以传播。
暂停线程
suspend()暂挂线程,resume()恢复。
1.suspend()和resume()有什么缺点?
缺点一:独占。使用不当,容易造成公共的同步对象的独占,使其他线程无法访问公共的同步对象。举个栗子:
System.out.println()方法,就是一个同步的方法。
public void println(long x) {
synchronized (this) {
print(x);
newLIne();
}
}
如果我们在一个线程中不停歇地打印,假设打印1-500000。
我们在main方法中写:
MyThread thread = new MyThread();
thread.start();
Thread.sleep(1000);
thread.suspend();
System.out.println("main end!");
线程暂挂了,但是main end!也不会被打印出来,因为println()方法被mythread独占,main线程无法访问。
缺点二:不同步。
假如公共对象没有synchronized锁,不同步。万一暂挂了,run方法只设置完用户名属性,还没来得及设置密码的值,数据自然不一致啦。
2.yield方法干什么?
放弃当前的cpu资源,让给其他任务,但是放弃的时间不确定,有可能刚放弃就又获得CPU时间片。
线程的优先级
1.为什么要给线程设置优先级?
设置优先级有助于“线程规划器”确定下一次选择那一个线程优先执行。优先级较高的线程得到的CPU资源较多。在Java中优先级分1~10这10个等级。常量有:
public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;//一般默认优先级为5
public final static int MAX_PRIORITY = 10;
使用setPriority()设置优先级。myThread.getPriority()获取优先级。
2.线程优先级有哪些特性?
- 继承性:A线程启动B线程,则B线程的优先级与A是一样的。
- 规则性:高优先级的线程总是大部分先执行完。但是不代表高优先级的全部先执行完。
- 随机性:优先级较高的线程不一定每一次都先执行完。
- 优先级高的线程运行的更快。
3.什么是守护线程?
Java线程中有两种线程:用户线程和守护线程
当进程中不存在任何用户线程时,守护线程自动销毁。典型的守护线程就是垃圾回收线程,典型应用就是GC(垃圾回收器)。
比喻:任何一个守护线程都是整个JVM中所有的非守护线程的保姆,只要有一个非守护线程,守护线程就会继续工作。只有当最后一个非守护线程结束是,守护线程才会随着JVM一同结束工作。
PS:本篇内容属于读书笔记,非常感谢高洪岩老师的《Java多线程编程的核心技术》。这本书非常实用,易懂。如果涉及任何侵权行为,请尽快告诉我。