Java多线程概念总结

来源:互联网 发布:乐乎 爱唐晶的小海胆 编辑:程序博客网 时间:2024/05/20 06:24

Java多线程概念总结

进程:进程是操作系统结构的基础;是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的活动;是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
线程:在进程中独立运行的子任务,故也被称为轻量级进程。
所以多线程是运行在一个进程(一次程序)中的多个子任务。

并发程序相对于串行程序能够极大地简化复杂系统的开发。
随着电脑处理器数量的增长,多线程开发能够有效的发挥多处理器系统的强大计算能力。

提出问题:
虽然多线程开发有很多优点,但是他的开发难度大大增加。
由于同一个进程中的所有线程都将共享进程的内存地址空间,因此这些线程都能访问相同的变量并在同一堆上分配对象,这就需要实现一种比在进程间共享数据粒度的数据共享机制。如果没有明确的同步机制来协同对共享数据的访问,那么当一个线程正在使用某个变量时,另一个线程可能同时访问这个变量。这将造成不可预测的结果。

那么作为程序猿,我们需要要做的就是做出一个良好的线程协同机制。
由于我们要做好协同机制,我们就需要明确到底哪里出现问题。

那么线程带来的问题有以下三种:
1.安全性问题
例子:

class UnsafeExample {    private int num;    public void setNum(String tag){        num++;        System.out.println("程序"+tag+num);    }}class MyThread extends Thread{    private UnsafeExample unsafeExample;    private String tag;    public MyThread(UnsafeExample unsafeExample,String tag){        super();        this.unsafeExample=unsafeExample;        this.tag=tag;    }    public void run(){        unsafeExample.setNum(tag);    }}public  class Test{    public static void main(String[] args){        UnsafeExample unsafeExample=new UnsafeExample();        Thread thread1=new MyThread(unsafeExample,"A");        Thread thread2=new MyThread(unsafeExample,"B");        thread1.start();        thread2.start();    }}

上面的程序我们在main函数中依次启动thread1,thread2,按照我们设想的正确的输出顺序应该是A1,B2.
但是实际输出确是
这里写图片描述

那么为什么呢?
答:这是因为num++并非是单个操作,它包含三个独立的操作,读value,将value++,并将计算结果写入value.并且我们要清楚,虽然我们在主程序中先启动线程thread1,再启动线程thread2,但是多个线程之间的操作是交替执行的,可能会同时进行读操作,那么得到的值就有可能是相同的。

所以多线程对于对象的并发访问存在着不可预测的安全性问题,所以需要我们去写好一个好的协同机制。

2.活跃性问题 :
活跃性问题主要针对的是单线程
活跃性问题表现形式之一就是无意中造成无限循环,从而使循环之后的代码无法得到执行。线程还有其他活跃性问题,例如,如果线程A在等待线程B释放其持有的资源,而线程B永远都不释放该资源,那么A就会永远等待下去。还有就是死锁,饥饿以及活锁。与大多数错误一样,导致活跃性问题的错误同样难以分析,因为它依赖于不同线程的事件发生时序,因此在开发或者测试中并不能够发现。

3.性能问题
并发程序开发是为了提高程序的性能,但是写多线程程序也会带来额外的开销,当一个多线程程序中,频繁的挂起活跃线程并转而运行另一个线程,那么就会频繁地出现上下文切换工作,这种操作会使得我们开发多线程程序的本意打破,相反的增加了额外的性能开销。

并发程序首先我们遇到的是安全性问题,即并发程序中使用和共享对象出现的问题。我们有以下的几个策略。
1.线程封闭。线程封闭的对象只能由一个线程拥有,对象被封闭在该线程中,并且只能由这个线程修改。
2.只读共享。在没有额外同步的情况下,共享的只读对象可以由多个线程并发访问,但任何线程都不能修改它。共享的只读对象包括不可变对象和事实不可变对象。
3.线程安全共享。线程安全的对象在其内部实现同步,因此多个线程可以通过对象的公有接口来进行访问而不需要进一步的同步。
4.保护对象。被保护的对象只能通过持有特定的锁来访问,保护对象包括封装在其他线程安全对象中的对象,以及已发布的并且由某个特定锁保护的对象。

活跃性问题则需要我们做好线程通信的机制。

性能问题则需要我们在实际中具体去判断,不可为了多线程而写并发程序,过度依赖可能就会适得其反。

好了,这里我只是理清楚概念,接下来我会依次描述具体的问题。

                                                              ——我不希望半路就后退。
1 0
原创粉丝点击