学习高并发编程1
实现多线程的两个基础方式(继承Thread,实现Runable接口)
多线程的理解
对计算机来说,每一个任务就是一个进程(Process),每一个进程里至少要有一个线程(Thread),线程是程序执行的一个路径,每一个线程都有自己的局部变量表,程序计数器,以及各自的生命周期。
在我个人看来,可以用银行的例子来理解多线程。这里把银行比作一个操作系统,里面的用户存取款业务是一个进程,传统的单线程模式就好比银行只开了一个窗口来办理存取款,而来办理业务的用户只能排队等候,等前面的人办理完成了,才能轮到自己办理,如果一天有500个用户来办理,情况就很糟糕了。
而多线程就像银行开设了多个窗口,每个窗口都可以办理业务,大大节省了用户办理业务的时间,甚至用户可以在1号窗口办理一会儿,再到2号窗口办理,大大提高了业务办理的总体效率。
如果假设银行没有账务系统,个人银行金额也不会当下就同步,就有可能造成这个人取了两次钱却扣了一次的钱,这也就是之后要说的线程安全问题。
多线程的生命周期
线程的生命周期大约有5个阶段
1. NEW
2. RUNABLE
3. RUNNING
4. BLOCKED
5. TERMINATED
- NEW状态
简单理解就是new Thread(),新建了一个Thread对象,此时该线程处于new状态。 - RUNABLE状态
在创建了Thread对象后调用start方法,该线程进入runable状态,但是此时的线程并不会立即执行线程内逻辑,只不过是有了一个被执行的“资格”,需要注意这个状态下并不能进入blocked和terminated状态,因为要进入必须获得CPU的调度执行。 - RUNNING状态
当runable状态的线程被CPU通过某种方式选中,开始执行,此时进入running状态,并且此时仍可称为runable状态。
当调用stop()方法进入terminated状态。
当调用sleep()方法进入blocked状态。
当获取资源锁,进入该锁的阻塞队列。
当调用yield()方法进入runable状态。 - BLOCKED状态
当在此状态时,调用stop()方法或意外死亡将进入terminated状态。
线程阻塞结束,将进入runable状态。
线程完成指定时间的休眠,将进入runable状态。
当调用wait()方法后被其他线程调用notify()/notifyall()方法唤醒,进入runable状态。
线程获取某个资源锁,进入runable状态。
线程在blocked状态,后调用interrupt(),进入runable状态。 - TERMINATED状态
terminated状态是线程的最后状态,在该状态线程将不会进入其他状态。
多线程的其中两个基本实现方式
- 继承Thread类
实现多线程,需要类继承thread,重写run()方法,通过start()方法启动线程,直接上代码:下面代码是通过四个线程来实现1-50的递增。
public class ThreadMethod extends Thread{
//线程名称
private final String name;
//最大处理数
private static final int Max = 50;
private static int index = 1;
public ThreadMethod(String name) {
this.name = name;
}
@Override
public void run(){
while (index <= Max){
System.out.println("线程"+name+"----"+"当前的号码是"+(index++));
}
}
public static void main(String[] args) {
ThreadMethod threadMethod1 = new ThreadMethod("1号");
ThreadMethod threadMethod2 = new ThreadMethod("2号");
ThreadMethod threadMethod3 = new ThreadMethod("3号");
ThreadMethod threadMethod4 = new ThreadMethod("4号");
threadMethod1.start();
threadMethod2.start();
threadMethod3.start();
threadMethod4.start();
}
}
输出结果:
线程2号----当前的号码是1
线程2号----当前的号码是2
线程2号----当前的号码是3
线程2号----当前的号码是4
线程1号----当前的号码是1
线程2号----当前的号码是5
线程2号----当前的号码是6
线程4号----当前的号码是1
线程4号----当前的号码是2
线程4号----当前的号码是3
线程4号----当前的号码是4
线程4号----当前的号码是5
线程4号----当前的号码是6
线程2号----当前的号码是7
线程1号----当前的号码是2
线程2号----当前的号码是8
线程2号----当前的号码是9
线程4号----当前的号码是7
。。。
结果显而易见,每个线程都执行了1-50的输出,而不是共同的输出1-50,这是因为index是四个实例创建的(可以通过将index静态处理)。
- 实现RUNABLE接口
多线程的另一个实现方法是实现RUNABLE接口,但其实这么说并不准确。实现多线程只有构造Thread类,而实现Thread的执行逻辑单元有两种,一个是重写Thread的run()方法,另一个是实现runable的run()方法,并将Runable实例用作Thread的参数,那么这两种方法有什么区别呢,直接看代码,还是通过四个线程来实现1-50的递增。
public class RunableMethod implements Runnable{
private int Index = 1;
private final static int Max = 50;
@Override
public void run() {
while (Index <= Max){
System.out.println(Thread.currentThread().getName()+"当前的号码是"+(Index++));
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final RunableMethod runableMethod = new RunableMethod();
Thread thread1 = new Thread(runableMethod);
Thread thread2 = new Thread(runableMethod);
Thread thread3 = new Thread(runableMethod);
Thread thread4 = new Thread(runableMethod);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
输出结果
Thread-0当前的号码是13
Thread-2当前的号码是14
Thread-3当前的号码是15
Thread-1当前的号码是16
Thread-0当前的号码是17
Thread-2当前的号码是18
Thread-3当前的号码是19
Thread-1当前的号码是20
Thread-2当前的号码是21
Thread-0当前的号码是22
Thread-1当前的号码是23
。。。
这下结果比之前好很多,但还会出现线程安全问题(重复输出,漏输),上面代码只构造了一个实例,四个线程只执行了一个实例,所以不会出现每个线程各执行各的。
以上仅为个人学以后的个人观点,如有异议欢迎指正。