多线程学习(一)

来源:互联网 发布:淘宝免费装修模板 编辑:程序博客网 时间:2024/05/29 10:43

最近,学习了下多线程,虽然工作中用不上,但还是多了解点为好,技多不压身。

什么是线程:先了解什么是进程,进程想必大家都知道,一个exe 打开后,在任务管理器里面有这个文件的进程,一个进程肯定有多个服务协同工作组成,这个服务就是类似于线程的概念,

首先呢:多线程是为了解决一类问题,而产生了多线程,例如:在某个场景下,一个业务操作多个数据库,并且各个数据不相互影响,执行时间大致是2分钟,这么长时间,客户肯定是等不及的,这时候,多线程就该商场表演了,由于每个服务相互不影响,所以多个线程执行的时间,肯定小于一个主线程执行业务的时间,稍后为大家带来feature模式,在此先稍微提下。

线程的优点:某些场景下执行时间快,大大提高了性能。

线程的缺点:操作不正确的情况下,会造成数据的不一致,引起脏读。

1:说到线程,不得不提synchronized 现在企业级开发根本不用,因为效率针对企业级开发来说效率太差,虽然sun对synchronized性能优化了一大堆。synchronized有两种,一是同步方法,二是同步代码块,都能保证线程的安全。但是,性能不太高。

2:volatile  保证了数据的可见性,但保证不了数据的原子性,什么意思呢:jdk5(jdk1.5)之后,sun公司针对多线程进行了优化,每个线程在共享区拿到数据之后,都会在自己的私有区域会建立一个类似于副本的概念,共享区数据发生变化,但是每个线程自己的私有区域有值得话,就不回去共享区拿数据,这样会有了数据不一致的情况,所以,在这种情况下,volatile关键字就诞生啦,jetty框架里面采用了大量的volatil关键字,所以性能肯定是非常好的。volatile 关键字加在变量上的意思就是:一旦共享区数据发生了变化,会强制要求每个线程更新自己的私有区域,跟共享区保持一致,这就是所谓的保证了数据的可见性,但是呢多个线程操作这个共享区的话,还是会引起脏读。那到底什么能保证数据的原子性呢,下面说

3:jdk下面有个包,里面有个atomic类,不了解的同学可以百度百度,atomic关键字会保证数据的原子性,但是一个方法内,二次合二次以上的调用atomic的话,也会引起问题

4:wait notify 在某些情况下可以保证线程安全,之前阿里有个面试题:

  • public class MyStack {    
  •        private List<String> list = new ArrayList<String>();    
  •        
  •        public synchronized void push(String value) {    
  •            synchronized (this) {    
  •                list.add(value);    
  •                notify();    
  •            }    
  •        }    
  •        
  •        public synchronized String pop() throws InterruptedException {    
  •            synchronized (this) {    
  •                if (list.size() <= 0) {    
  •                    wait();    
  •                }    
  •                return list.remove(list.size() - 1);    
  •            }    
  •        }    
  •    }
    问题:  这段代码大多数情况下运行正常,但是某些情况下会出问题。什么时候会出现什么问题?如何修正?

    主要考察的是wait和notify的缺点:wait是立即释放锁,而notify会在方法执行完后释放锁,稍微解释下上面的例子为什么会有问题

    三个线程执行:a ,b ,c  a负责add 。b跟c 负责pop 。b先执行,发现没元素,wait  然后a执行,添加了个元素,这时候方法执行完,释放锁,b跟c 相互抢,c先抢到,执行了remove 这时候,b在执行remove ,执行报错。当然别把阿里面试题想的这么简单,下面会问,怎么解决,解决的办法很简单,if判断换成while循环就行。

    多线程还有其他的方法,suspend和resume ,会引起的独占问题,stop方法太暴力,不建议使用,sleep, yield 和join的区别


    ----------------------------------------------------------------------------------------------------

    二 线程的容器

    线程的容器可以分为:同步类容器和并发类容器。

        同步类容器就是synchronized的那种方式,并发类容器我知道的就是 currentMap 跟queue。currentMap里面有个类似于段的概念,就是segment。currentMap里面分为16个段,每一个段都是一个同步的HashMap,这种方式提升了点并发类的性能,目前的话,没什么缺点,copyonwrite 应用场景就是读多写少的场景,因为 遇到发生了写的操作的话,线程先把容器的数据copy一份,不影响其他线程的读,写好了再把原来的引用指向新的引用,里面实现是用reentrentLock实现,可以百度下锁重入,这样达到了同步。下面重点说书queue,queue分为堵塞队列跟非堵塞队列,堵塞队列就是带有blocking的单词的队列,类似于 ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue,DelayQueue, priorityBlockingQueue ..每个queue都有各自的场景业务 ArrayBlockingQueue 有界队列,LinkedBlockingQueue无界队列,DelayQueue带有延时时间的queue,priorityBlockingqueue,具有优先级的queue。这里小小举例下:

        一条马路上,一个路口 , 早晨8点到10点是早高峰,10点到 下午4点正常,晚上12点后通畅,这三种情况好比三种任务,任务多的话,可以用ArrayBlockingQueue限制任务的数目,ListedListBlockingQueue 无界队列,随意加任务,没有界限,晚上12点后,我想任务直接执行,不通过容器,那就用synchronousQueue。在这里就不详细讲了。

  • 原创粉丝点击