java多线程解决了什么问题?
并发通常可以解决“速度”和“设计可管理性问题”。
“并行”和“并发”的区别在于,“并行”是多个进程同时执行。而“并发”表面上看是多个线程同时执
行,而实际上是多个线程在轮换执行,也就是说某一时刻只有一个线程在运行。但是由于处理器的运行速度非常之快,所以给人的感觉是多个线程在同时运行。
线程/进程 | 开销 | 资源 |
---|---|---|
线程 | 小 | 共享所在进程中的资源 |
进程 | 大 | 彼此间资源独立 |
说多线程解决“速度问题”有一个前提。
就是在多cpu的情况下。
当在单cpu时,多线程只能起到同时处理多个任务的作用,而并不会提高运行速度。
我们可以想象一种情境,就是如果我们需要同时给100个人打电话通知他们尽快回校参加培训。
如果在单线程的情况下,就等同于用同一台电话拨打100次通话。那么这时候如果该线路出现问题,后面的人都无法得到通知。
如果是多线程的情况下,如果某一个线路出现了阻塞,并不影响其他线路工作。
实现多线程的方式
- 通过实现Runnable接口
- 继承自Thread类
//通过实现Runnable
public class RocketFacotry1 implements Runnable {
//事实上任何线程的执行流程都是在Runnable接口的run()方法中执行
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "号生产线完成了一量火箭的组装!");
}
public static void main(String[] args) {
RocketFacotry1 facotry1 = new RocketFacotry1();
new Thread(facotry1).start();
new Thread(facotry1).start();
}
}
运行结果
Thread-1号生产线完成了一量火箭的组装!
Thread-0号生产线完成了一量火箭的组装!
//通过继承Thread
public class RocketFactory2 extends Thread {
@Override
public void run() {
super.run();
System.out.println(Thread.currentThread().getName() + "号生产线完成了一量火箭的组装!");
}
public static void main(String[] args) {
new RocketFactory2().start();
new RocketFactory2().start();
}
}
运行结果
Thread-1号生产线完成了一量火箭的组装!
Thread-0号生产线完成了一量火箭的组装!
Thread内部也是通过执行Runnable接口的run()来运行线程
@Override
public void run() {
//此处的target是Runnable
if (target != null) {
target.run();
}
}
Daemon线程
Daemon线程即守护线程,后台线程的特点是优先级比较低。当jvm中的非守护线程都运行结束的时候,那么Daemon守护线程也跟着结束。所以在使用守护线程的时候要慎重些
jvm的垃圾回收线程就是典型的(Daemon)守护线程。当jvm中没有其他的线程在运行时,垃圾回收线程也就跟着结束了
setDaemon一定要在线程的start之前设置,否则抛出IllegalThreadStateException异常
public static void main(String[] args) { Thread thread = new Thread(() -> { int num = 0; while (true) { try { Thread.sleep(20); System.out.println(Thread.currentThread().getName() + " is running!timestamp is " + System.currentTimeMillis() + " ,Daemon线程运行了" + num++ + "次"); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread.setDaemon(true); thread.start(); try { Thread.sleep(500); Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("JVM运行结束,timestamp is " + System.currentTimeMillis()))); } catch (InterruptedException e) { e.printStackTrace(); } }
Thread-0 is running!timestamp is 1489134646030 ,Daemon线程运行了0次
Thread-0 is running!timestamp is 1489134646050 ,Daemon线程运行了1次
Thread-0 is running!timestamp is 1489134646070 ,Daemon线程运行了2次
Thread-0 is running!timestamp is 1489134646090 ,Daemon线程运行了3次
Thread-0 is running!timestamp is 1489134646117 ,Daemon线程运行了4次
Thread-0 is running!timestamp is 1489134646137 ,Daemon线程运行了5次
Thread-0 is running!timestamp is 1489134646157 ,Daemon线程运行了6次
Thread-0 is running!timestamp is 1489134646177 ,Daemon线程运行了7次
Thread-0 is running!timestamp is 1489134646197 ,Daemon线程运行了8次
Thread-0 is running!timestamp is 1489134646217 ,Daemon线程运行了9次
Thread-0 is running!timestamp is 1489134646237 ,Daemon线程运行了10次
Thread-0 is running!timestamp is 1489134646257 ,Daemon线程运行了11次
Thread-0 is running!timestamp is 1489134646277 ,Daemon线程运行了12次
Thread-0 is running!timestamp is 1489134646297 ,Daemon线程运行了13次
Thread-0 is running!timestamp is 1489134646317 ,Daemon线程运行了14次
Thread-0 is running!timestamp is 1489134646337 ,Daemon线程运行了15次
Thread-0 is running!timestamp is 1489134646357 ,Daemon线程运行了16次
Thread-0 is running!timestamp is 1489134646377 ,Daemon线程运行了17次
Thread-0 is running!timestamp is 1489134646397 ,Daemon线程运行了18次
Thread-0 is running!timestamp is 1489134646417 ,Daemon线程运行了19次
Thread-0 is running!timestamp is 1489134646437 ,Daemon线程运行了20次
Thread-0 is running!timestamp is 1489134646457 ,Daemon线程运行了21次
Thread-0 is running!timestamp is 1489134646477 ,Daemon线程运行了22次
Thread-0 is running!timestamp is 1489134646497 ,Daemon线程运行了23次
JVM运行结束,timestamp is 1489134646511Process finished with exit code 0
无论运行多少次,daemon线程中的时间戳永远小于主线程的,由此可见,当jvm中没有user线程在运行时,daemon线程也就跟着关闭了
Thread基本状态
在调用Thread实例start()方法后,基本状态为可执行状态(Runnable)、被阻塞状态(Blocked)、执行中(Running)几种状态
实例化Thread并执行start方法后,线程进入Runnable状态,此时线程尚未真正开始执行run()方法,必须等待排班器(Scheduler)插入cpu执行,线程才会执行run方法。
调用Thread.sleep()、进入synchronized前竞争对象锁定的阻断、调用wait()方法的阻断等、等待输入输出完成夜壶进入Blocked状态
安插线程
System.out.println("主线程开始运行");
Thread threadB = new Thread(() -> {
System.out.println("Thread B开始执行...");
for (int i = 0; i < 5; i++) {
System.out.println("Thread B执行中...");
}
System.out.println("Thread B将结束...");
});
threadB.start();
threadB.join();
System.out.println("主线程将结束....");
输出为:
主线程开始运行
Thread B开始执行…
Thread B执行中…
Thread B执行中…
Thread B执行中…
Thread B执行中…
Thread B执行中…
Thread B将结束…
主线程将结束….
从上面的代码中,我们可以看到存在两个线程:主线程和线程Thread B
主线程调用线程Thread B的Join方法,导致主线程阻塞,直到Thread B线程执行完毕,才返回到主线程中。
简单理解,在主线程中调用Thread B.join(),也就是在主线程中加入了Thread B线程的代码,必须让Thread B线程执行完毕之后,主线程(调用方)才能j正常执行。
一些Deprecated的api
1. stop方法被遗弃
1.即刻停止run()方法中剩余的全部工作,包括在catch或finally语句中,并抛出ThreadDeath异常(通常情况下此异常不需要显示的捕获),因此可能会导致一些清理性的工作的得不到完成,如文件,数据库等的关闭。
2.会立即释放该线程所持有的所有的锁,导致数据得不到同步的处理,出现数据不一致的问题。
public class Main{
public static void main(String [] args) throws Exception{
TestObject testObject = new TestObject();
Thread t1 = new Thread(){
public void run(){
try {
testObject.print("1", "2");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
t1.start();
Thread.sleep(1000);
t1.stop();
System.out.println("first : " + testObject.getFirst() + " " + "second : " + testObject.getSecond());
}
}
class TestObject{
private String first = "ja";
private String second = "va";
public synchronized void print(String first, String second) throws Exception{
this.first = first;
Thread.sleep(10000);
this.second = second;
}
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getSecond() {
return second;
}
public void setSecond(String second) {
this.second = second;
}
}
输出first : 1 second : va
2. resume和suspend方法被遗弃
suspend()和resume()必须要成对出现,否则非常容易发生死锁。
因为suspend方法并不会释放锁,如果使用suspend的目标线程对一个重要的系统资源持有锁,那么没任何线程可以使用这个资源直到要suspend的目标线程被resumed,如果一个线程在resume目标线程之前尝试持有这个重要的系统资源锁再去resume目标线程,这两条线程就相互死锁了,也就冻结线程。
public class Main{
public static void main(String [] args) throws Exception{
TestObject testObject = new TestObject();
Thread t1 = new Thread(){
public void run(){
testObject.print();
}
};
t1.setName("A");
t1.start();
Thread.sleep(1000);
Thread t2 = new Thread(){
public void run(){
System.out.println("B已启动,但进入不到print方法中");
testObject.print();
}
};
t2.setName("B");
t2.start();
}
}
class TestObject{
public synchronized void print(){
if(Thread.currentThread().getName().equals("A")){
System.out.println("A 线程 独占该资源了");
Thread.currentThread().suspend();
}
}
}
3. destroy方法被遗弃
当线程处于suspend状态下,此时如果调用destroy方法销毁线程,并不会释放锁,那么其他线程将不再能获取到它锁定的资源.
停止线程
stop()方法不推荐使用了,那么我如果想要认为的让线程停止怎么办呢?
public class ThreadCustom implements Runnable {
private boolean isContinue = true;
@Override
public void run() {
while (isContinue) {
System.out.println(Thread.currentThread().getName() + " is running!");
}
}
public void stop() {
isContinue = false;
}
public static void main(String[] args) throws InterruptedException {
ThreadCustom r = new ThreadCustom();
Thread thread = new Thread(r);
thread.start();
Thread.sleep(500);
r.stop();
}
}
线程安全
- synchronized的使用