并发
并行与并发是两个相似却又不同的概念,并行指的是多个事件在同一时刻发生,而并发指的是多个事件在同一时间间隔发生。时刻与时间间隔的差距在于,以单CPU为例,每一个时间点只能有一个任务占用CPU资源,但通过分时地交替执行多个任务,由于交替的频率非常之快,制造了多个任务同时进行的“假象”,这就是并发。而并行是指,多个任务每个任务各自占用一个CPU资源,是真正意义上的同时执行。
并发由进一步分为了线程和进程两种不同类型,区别在于每一个进程,操作系统都会为它分配独立的私有空间。而线程则是在一个进程下,共享一个运行时的存储空间,每一个线程独立拥有自己的调用方法栈和特定寄存器。显而易见的是,线程间的交互是较为容易的,毕竟可以共享变量直接调用。还有一种消息传递的方法,同时适用于线程和进程间的交互。
消息传递
进程间的消息传递,需要操作系统或是服务器层次参与。进程在发送消息之前,先在自己的内存空间设置一个发送区,把想要发送的消息填入。然后再用发送系统函数将其发送出去。而接收进程则在接收消息之前,在自己的内存空间内开辟相应的接收区,然后用接收过程接收消息。
当有许多进程参与,信息传递的频率过高,这个时候直接的消息传递就不再适用,所以提出了消息队列的方法。也就是将接受到的信息以队列的形式缓存在特定的存储空间中。
- 当不使用消息队列服务器的时候,用户的请求数据直接写入数据库,在高并发的情况下数据库压力剧增,使得响应速度变慢。而在使用消息队列后,用户的请求数据发送给消息队列之后立即 返回,所以用户端进程相应时间就会大大缩短。消息队列的优势之一,将短时间高并发产生的访问消息存储在消息队列中,平稳的输出给接受方进程。
- 在客户与服务端交互的过程中,用户的请求往往涉及多种流水线服务。如果不通过消息队列直接交互,假如服务端某一个子系统出现了故障,则所有的用户请求都无法得到相应。转变成基于消息队列的方式后,当某个子系统故障时,其所要处理的消息被缓存在消息队列中,用户的访问可以正常响应,知道该系统恢复,提升系统的可用性。
Java多线程
Java中调用多线程有两种方法。
- 直接继承Thread类,并重写run方法,重写的内容即为线程所需执行的代码块。在主线程中使用thread.start()方法开启线程。
- 实现Runnable接口,实现run()方法。在主线程中使用Thread(实现了Runnable接口的类的实例化).start()开启线程。
两者实现的效果一样,但Runnable接口的方法较为常用。其一,因为Java不允许多继承,如果继承了Thread类就无法继承其他服务,而实现了Runnable接口的类就没有这样的限制。其二,Runnable便于实现资源共享。如果使用Thread继承方法,则对于对于如下代码
public class MyThread extends Thread {
int num = 3;
@Override
public void run() {
synchronized (this) {
num--;
System.out.println(num); }
}
}
}
public class Test {
public static void main(String[] args) {
new MyThread().start();//线程1
new MyThread().start();//线程2
}
}
获得的结果是
2
2
可以看出两个线程之间并没有共享num变量,这是因为两次MyThread实例化分别开辟了num内存。如果使用Runnable方法,
public class MyThread2 implements Runnable{
public int num = 3;
@Override
public void run() {
synchronized (this) {
num--;
System.out.println(num); }
}
}
public static void main(String[] args) {
MyThread2 t = new MyThread2();
new Thread(t).start();
new Thread(t).start();
}
}
获得的结果是
2
1
可以发现对于Runnable方法实现的多线程,能够方便共享变量。