初识多线程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了初识多线程相关的知识,希望对你有一定的参考价值。

多线程对于有一定开发经验的程序员来说肯定不会陌生,不过相信很多人跟我一样,平时其实没有那么多的多线程实例去做,即使有可以直接使用spring来实现简单的使用,更多的是在面试中,一般都会涉及,特别是大公司的面试。

在阿里的初面中,我就被刷下来,失落感是显而易见的,虽然面试官也坦诚,如果我去了阿里也是做应用,面试问的这些东西也不会有太多涉及,面试门槛高,所以为了去大公司也得耐心的了解。当然面试官也说了,如果你知道这些偏僻的内容,对工作某个点是有用的,我同意这个说法。

所以,对于多线程这部分耐心看下来,对于找工作或者技术提升都是有用的。与君共勉!

文章借鉴于《Java多线程编程实战指南(设计模式篇)》 黄文海/著,以及极客学院多线程视频http://search.jikexueyuan.com/course/?q=%E5%A4%9A%E7%BA%BF%E7%A8%8B。


一,为什么要有多线程?

多线程的前提是各种设备有多个cpu,这样多个cpu能够并行的进行计算。

好处是:

1,吞吐量变大(即能够同时接收多个请求)

2,提高响应性(一个线程比较慢时,其他线程可以继续工作)

3,充分利用cpu资源。可以简单的理解,就是以前一个人做的事情,可以多个人独立分担去做了。


由于是多个线程独立的去做,那么肯定也会产生一些问题。

1,线程安全的问题:多个线程共享数据时,如果没有做相应的措施,那么就会产生数据一致性的问题,如读取脏数据(过期数据),丢失更新(某些线程做的更新被其他线程做的更新覆盖)

2,线程生命特征问题:单线程时一个线程串行执行不会产生问题,但是多个线程就会产生调度的问题,包括死锁,活锁(由于资源有限,调度问题,某个线程一直得不到资源)

3,上下文切换开销:一个线程的状态变更,调度停止线程的执行时,会保存该线程的上下文,即当时的环境状态,当重新执行时,需要恢复当时的环境状态,这个过程会产生开销。

4,可靠性 :线程之间是独立的,一方面一个线程意外终止了不会影响其他线程。另外一方面由于线程共享同一个进程中的资源,如果一个线程内存泄漏导致jvm崩溃停止,那么其他的线程也会停止。


通俗的来说:

有一个土堆要挖土,单线程的情况就是1个挖土机自己慢慢的挖,多线程就是10个挖土机同时来挖,好处就是快,坏处就是10个机器就会有调度的问题,保证不会抢夺资源、摩擦等问题,需要合理的处理调度。


二,线程的状态:

NEW:一个线程刚被创建,还未start(),一个线程只能一次处于此状态。

RUNNABLE:包括READY和RUNNING,线程执行start()之后就是READY状态,此时等待调度系统,被调用中就是RUNNING状态。如果执行yield()方法或者调度系统的原因,RUNNING状态也会变成READY状态,两者是能相互转化的。

BLOCKED:阻塞状态,即线程请求资源被其他线程调用,当其他资源使用完成之后,线程可以变为RUNNABLE状态。

WAITTING:无限制的等待其他线程先执行,才能执行。和BLOCKED的区别在于BLOCKED是线程获得不了资源被动的等待,WAITTING是主动的被调用者等待,相同点是需要等待别人执行完成才能执行。Object.wait() Thread.join() LockSupport.park()执行时会编程WAITTING状态,调用Object.notify() Object.notifyAll() LockSupport.unpark()能够编程RUNNABLE状态。

TIME_WAITTING:有闲时间的等待,比如等待1秒。

TERMINATED:终止状态,正常执行结束,或者跑出异常之后,都会编程这个状态。

总而言之:一个线程一定有一个NEW TERMINATED状态,其他状态可能都会有相互转化。(状态图见附件)


三,线程的实现

我们对线程的操作,基本基于对java.lang.Thread类来操作,Thread也是实现了Runnable接口。

线程实现 ① 实现Runnable接口,实现run方法,Thread类有一个传入Runnable实现的构造器,获得Thread ② 集成Thread类,重写run方法,获得Thread。

public class Instance {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyRunnables());
        t1.start();
        Thread t2 = new MyThreads();
        t2.start();
    }
}
 
class MyRunnables implements Runnable{
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
class MyThreads extends Thread{
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}


四,线程常用方法

1,t.start()启动

2,t.isAlive()启动前是false,启动后是true

3,Thread.currentThread()获得当前线程

4,Thread.sleep(1000)睡眠

5,t.join()强行执行

6,t.yield()礼让


五,线程同步

多个线程占用相同的资源,为了避免脏数据的问题。

public class Synchronization {
 
    public static void main(String[] args) {
        MyRunable r = new MyRunable();
        Thread t1 = new Thread(r,"窗口1");
        Thread t2 = new Thread(r,"窗口2");
        Thread t3 = new Thread(r,"窗口3");
 
        t1.start();
        t2.start();
        t3.start();
    }
 
}
 
 
class MyRunable implements Runnable{
 
    private Integer ticket = 5;
 
    public void run() {
        synchronized (this){
            while (ticket > 0){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "卖出一张,剩余车票:" + (--ticket));
            }
        }
 
    }
}

打印结果:

窗口1卖出一张,剩余车票:4
窗口3卖出一张,剩余车票:3
窗口2卖出一张,剩余车票:2
窗口3卖出一张,剩余车票:1
窗口1卖出一张,剩余车票:0


对比没有家synchronized关键字结果:

窗口2卖出一张,剩余车票:4
窗口3卖出一张,剩余车票:3
窗口1卖出一张,剩余车票:2
窗口2卖出一张,剩余车票:1
窗口3卖出一张,剩余车票:0
窗口1卖出一张,剩余车票:-1
窗口2卖出一张,剩余车票:-2


对共享的变量或者方法上加上synchronized,也可以在代码块上加上这个关键字。


六,线程优先级

t1.setPriority(Thread.MAX_PRIORITY);
t1.setPriority(Thread.NORM_PRIORITY);
t1.setPriority(Thread.MIN_PRIORITY);

目前理解不深,之后补充。


以上是关于初识多线程的主要内容,如果未能解决你的问题,请参考以下文章

初识多线程之基础知识与常用方法

初识多线程

初识多线程

进程线程区别,和线程初识

多线程 Thread 线程同步 synchronized

Java核心知识---初识线程