引言
在平时的工作中,经常能碰到程序需要对大量的数据进行相同的操作,如果只是传统的单线运行,无疑会碰到执行时间过长。尤其是在处理一些即时性要求较高的数据的时候,处理太慢无疑就是事故级别的存在了。因此,这个时候,我们就可以考虑多线程。
为什么要使用多线程
没有什么问题是多线程解决不了的,如果有,就再起一个线程。
线程作为计算机调度资源的基本单位,使用多线程,可以充分利用CPU资源,避免由于某个任务执行时间过长而导致后续任务一直处于等待的情况。
线程的运行周期:
线程总共有五种状态:
线程的生命周期.png其中:
- 阻塞存在三种情况:
- 被阻塞: 常见于抢不到锁而被堵塞;
- 等待: 线程等待另一个线程通知线程调度;
- 计时等待: 线程只等待一定的时间,超时就不再等待;
- 线程死亡:
- 自然终止: 当线程自动执行结束之后,线程资源被回收;
- 异常终止: 线程被强行通过interrupt等操作中断;
那么在java中,如何新起一个线程呢?
java的多线程
1, Thread类;
2, Runnable接口
3, Future 和 Callable
接下来我们详细介绍下Thread类
Java多线程之Thread类详解(java.lang.Thread)
构造方法:
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize, boolean inheritThreadLocals) {
init(group, target, name, stackSize, null, inheritThreadLocals);
}
参数的解析:
- group: 线程组,即new Thread 的该线程的ThreadGroup
- target: 一个Runnable的任务
- stackSize:该线程执行过程中的最大的栈深度;
- inheritThreadLocals : 是否继承原有父线程的变量,如果设置为true的话,会通过用一个ThreadLocalMap将变量存储;
start 方法
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
其中 threadStatus 表明线程的状态,各个值的含义为:
- 0: NEW
- 1: RUNNABLE
- 2: BLOCKED
- 3: WAITING
- 4:TIMED_WAITING
- 5: TERMINATED
start0() : 为一个native方法,该方法作用为产生新线程并调用run方法;
run方法:执行传入的Runnable实例中的run方法
public void run() {
if (target != null) {
target.run();
}
}
关于Thread类其他重要的方法
native方法:
- registerNative():将指定的方法,如start0,sleep,yield等本地方法绑定到函数中;
- sleep() :使该线程陷入阻塞的状态;
- yield():该方法在于将本身的状态改为就绪态;
- 备注: yield 和sleep 是有区别的yield是重新到就绪状态,而sleep是进入到阻塞的状态
其他非native方法
- exit(): 调用 threadTerminated 方法,将其他属性都置为null
- interrupt(): 通过(interrupt0)修改中断标识位,当出现阻塞的时候,线程就会抛出 InterruptedException;
- join():调用的线程等待被调用实例线程一定时间(传参数)或者一直等待到结束;
下面给出一些简单的例子来进行调用:
public·class·MyThread·extends ·Thread {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
public void run() {
Long id = currentThread().getId();
try {
System.out.println(getTime() +":开始的线程id:" + id);
Thread.sleep(5000);
System.out.println(getTime() + ":结束的线程id:" + id);
} catch (InterruptedException exp) {
System.out.println(getTime() + ":中断的线程id:" + id);
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
};
MyThread testThread1 = new MyThread(runnable);
testThread1.start();
MyThread testThread2 = new MyThread(runnable);
testThread2.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getTime()+":主线程执行完毕");
}
public MyThread(Runnable target) {
super(target);
}
public static String getTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(new Date());
}
}
/**
执行的结果为:
开始的线程id:12
开始的线程id:13
主线程执行完毕
结束的线程id:13
结束的线程id:12
*/
interrupt方法尝试:
代码片段如下:
testThread2.start();
testThread1.interrupt();
try {
输出结果如下:
2020-03-29 17:32:02:开始的线程id:13<br>
2020-03-29 17:32:02:开始的线程id:12<br>
2020-03-29 17:32:02:中断的线程id:12<br>
2020-03-29 17:32:03:主线程执行完毕 <br>
2020-03-29 17:32:07:结束的线程id:13<br>
分别尝试使用:join方法
- 添加 testThread1.join(1000); 执行结果如下:
代码片段如下:
testThread1.join(1000);
Thread.sleep(2000);
2020-03-29 17:40:04:开始的线程id:13
2020-03-29 17:40:04:开始的线程id:12
2020-03-29 17:40:07:主线程执行完毕
2020-03-29 17:40:09:结束的线程id:13
2020-03-29 17:40:09:结束的线程id:12
- 添加 testThread2.join(); 执行结果如下:
代码片段如下:
testThread2.join(1000);
Thread.sleep(2000);
2020-03-29 17:41:00:开始的线程id:13
2020-03-29 17:41:00:开始的线程id:12
2020-03-29 17:41:05:结束的线程id:12
2020-03-29 17:41:05:结束的线程id:13
2020-03-29 17:41:07:主线程执行完毕
至此,我们可以看到,利用Thread新起一个线程是如此的简便。因此,下次碰到多任务的时候,不要再用线性执行的方式了,利用多线程的技术,让你的程序效率翻倍吧。
当然,使用多线程固然会加快速度,但是一味的增加线程数是否真的高效呢?其实不是的,期间涉及到线程的调度开销,以及多线程开发中存在着线程安全的问题。因此,如何优雅的利用多线程开发,期待下次文章吧。
网友评论