多线程笔记

1.多线程的创建方式有两种

a 实现Runnable的接口 实现他的run的方法 建议使用这种 因为接口可以实现多继承

b 集成Thread 的抽象类,重写父类的 run的方法。

2.run() 与start()的区别

调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。

把需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run()方法,这是由jvm的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void.

3.多线程的Synchronized

a Synchronized 需要锁住对象,不然的话还是会互斥。

b 静态方法和实例方法同步的话需要锁住 class本身,(静态方法需要同步只有锁住字节码的对象才可以 Object.class)

c 要用到共同数据,最好全部在同一个类中实现,既可以达到高内聚,也可以实现同步锁。

4.sleep和wait的区别

a sleep来自Thread类,和wait来自Object类。

b 最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。

c wait等待的线程可以用 notify 唤醒。这个可以用while 循环一起使用可以防止伪唤醒,因为有时候没用调用notify的时候也可以唤醒线程。

d 在wait()/notify()机制中,不要使用全局对象,字符串常量等。应该使用对应唯一的对象。

public void method(){

while(true){

wait();

........

notify();

}

}

5.线程共享变量

a ThreadLocal 不是用来解决共享对象的多线程访问问题的,一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。

b 也可以用HashMap类来实现这个功能,map.put(当前线程,需要该线程共享的值);

c ThreadLocal 的具体实现是 ThreadLocalMap 这个map的具体key是当前线程。该类的set方法与下面类似用来取值;

d 一个ThreadLocal只能放一个变量 如果有多个的话可以用一个实体封装。或者新建多个ThreadLocal对象;

public T get() {

//获取当前执行线程

Thread t = Thread.currentThread();

//取得当前线程的ThreadLocalMap实例

ThreadLocalMap map = getMap(t);

//如果map不为空,说明该线程已经有了一个ThreadLocalMap实例

if (map != null) {

//map中保存线程的所有的线程本地变量,我们要去查找当前线程本地变量

ThreadLocalMap.Entry e = map.getEntry(this);

//如果当前线程本地变量存在这个map中,则返回其对应的值

if (e != null)

return (T)e.value;

}

//如果map不存在或者map中不存在当前线程本地变量,返回初始值

return setInitialValue();

}

6.Thread.stop()  方法是不安全的.

释放该线程所持有的所有的锁,那么被保护数据就有可能呈现不一致性,其他线程在使用这些被破坏的数据时,有可能导致一些很奇怪的应用程序错误。

7.如何正确停止线程

关于如何正确停止线程,这篇文章(how to stop thread)给出了一个很好的答案, 总结起来就下面3点(在停止线程时):

1. 使用violate boolean变量来标识线程是否停止

2. 停止线程时,需要调用停止线程的interrupt()方法,因为线程有可能在wait()或sleep(), 提高停止线程的即时性

3. 对于blocking IO的处理,尽量使用InterruptibleChannel来代替blocking IO

总结:

1.局部变量中的基本数据类型(8种)永远是线程安全的。

2.局部变量中的对象类型只要不会被其他线程访问到,也是线程安全的。

3.一个对象实例被多个线程同时访问时,他的成员变量就可能是线程不安全的。

===========================================================================

java.util.concurrent 线程并发包

(compareAndSet)CAS同步的原理:http://www.blogjava.net/syniii/archive/2010/11/18/338387.html?opt=admin

CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

利用CPU的CAS指令,同时借助JNI来完成Java的非阻塞算法。

CAS看起来很爽,但是会导致“ABA问题”。

CAS算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化。

比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。如果链表的头在变化了两次后恢复了原值,但是不代表链表就没有变化。因此前面提到的原子操作AtomicStampedReference/AtomicMarkableReference就很有用了。这允许一对变化的元素进行原子操作。

java.util.concurrent.AtomicInteger :

AtomicInteger,一个提供原子操作的Integer的类。在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字。而AtomicInteger则通过一种线程安全的加减操作接口。

采用的是compareAndSet CAS来保证数据的同步,是通过CPU来实现的,比起用synchronized, 这里的排他时间要短的多. 所以在多线程情况下性能会比较好.

=========================================================================================================

线程池 参考文档:http://www.oschina.net/question/565065_86540

线程池的作用:线程池作用就是限制系统中执行线程的数量。根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。

为什么要用线程池:1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。

ExecutorService 真正的线程池接口。

ScheduledExecutorService 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。

ThreadPoolExecutor ExecutorService的默认实现。

ScheduledThreadPoolExecutor 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

1. newSingleThreadExecutor

创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

2.newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

3. newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,

那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

4.newScheduledThreadPool

创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

以上的所有底层实现都是用ThreadPoolExecutor这个来做的。具体的可以查看源代码

=================================================================================================================

Callable 与 Future 的应用

配合使用可以得到每个线程的返回值 Callable返回 Future接收

=================================================================================================================

java.util.concurrent.Lock :

共同点:Lock能够完成synchronized所实现的所有功能

不同点:Lock有比synchronized更精确的线程和更好的性能,并且提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。

synchronized是在JVM层面上实现的,在代码执行时出现异常,JVM会自动释放锁定.但Lock不能,只能手动释放,并且在finally从句中释放

性能:在jdk1.5中Synchronized是使用的独占悲观锁,资源竞争不激烈的情况下synchronized性能略高,资源竞争激烈的情况下性能要远低于Lock。

jdk1.6后synchronized和lock一样都是使用CAS乐观锁操作,所以性能差不多,对于具体使用哪一个要看具体的系统应用需要,Synchronized相对简单易用,若需要精细的灵活控制则可以考虑选择lock。

Lock 也是采用compareAndSetState CAS 机制来实现锁的

ReentrantLock 互斥锁, 指的是一次最多只能有一个线程持有的锁. 在jdk1.5之前, 我们通常使用synchronized机制控制多个线程对共享资源的访问。而现在Lock提供了比synchronized机制更广泛灵活的锁定操作。

=================================================================================================================

ReadWriteLock 读写锁

ReentrantReadWriteLock 和 ReentrantLock 不是继承关系,但都是基于 AbstractQueuedSynchronizer 来实现。

注意:在同一线程中,持有读锁后,不能直接调用写锁的lock方法 ,否则会造成死锁。

读 写

读 允许 不允许

写 不允许 不允许

=================================================================================================================

Condition  可以实现可阻塞队列。可参考api里的经典列子

Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用

Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。

class BoundedBuffer {

final Lock lock = new ReentrantLock();

final Condition notFull  = lock.newCondition();

final Condition notEmpty = lock.newCondition();

final Object[] items = new Object[100];

int putptr, takeptr, count;

public void put(Object x) throws InterruptedException {

lock.lock();

try {

while (count == items.length)

notFull.await();

items[putptr] = x;

if (++putptr == items.length) putptr = 0;

++count;

notEmpty.signal();

} finally {

lock.unlock();

}

}

public Object take() throws InterruptedException {

lock.lock();

try {

while (count == 0)

notEmpty.await();

Object x = items[takeptr];

if (++takeptr == items.length) takeptr = 0;

--count;

notFull.signal();

return x;

} finally {

lock.unlock();

}

}

}

=================================================================================================================

自旋锁 :类似于while死循环,不断的判断表达式,判断是否还持有该把锁对象.

会导致死锁。如果长期持有自旋锁

独占悲观锁:synchronized

CAS乐观锁 :Lock

多线程笔记,布布扣,bubuko.com

时间: 2025-02-01 11:41:19

多线程笔记的相关文章

QT多线程笔记

1.QT多线程涉及到主线程和子线程之间交互大量数据的时候,使用QThread并不方便,因为run()函数本身不能接受任何参数,因此只能通过信号和槽的交互来获取数据,如果只是单方面简单交互数据还过得去,如果涉及多次主.子线程之间的交互的时候,这种方式是很不方便的,这里采取另外一种方式,建一个继承自QObject的类,进行movetoThread()操作: class FileThread :public QObject { Q_OBJECT public: QString m_path; QFil

Java基础知识强化之多线程笔记01:多线程基础知识(详见Android(java)笔记61~76)

1. 基础知识: Android(java)学习笔记61:多线程程序的引入    ~    Android(java)学习笔记76:多线程-定时器概述和使用 

Java多线程笔记

1.并发通常可以提高单处理器上程序的性能 其实,在单处理器上并发的执行程序锁用的开销大于顺序执行.然而顺序执行时,程序有时会因为某些条件(通常是I/O问题)导致不能继续执行,称为线程阻塞,如果没有并发,程序将停止不前.而使用了并发,一个任务阻塞,其他任务还可以继续执行,这就保证了程序的完成.所以,如果确定没有任务会出现阻塞,在单处理器上并发执行程序是不必要的. 2.并发在单处理器上性能提高最常见的实例是--事件驱动的编程 并发最吸引人的额就是可以产生可响应的用户界面,比如某个程序将长期的运行某个

多线程笔记--原子操作Interlocked系列函数

前面写了一个多线程报数的功能,为了描述方便和代码简洁起见,只输出最后的报数结果来观察程序运行结果.这非常类似一个网站的客户访问统计,每个用户登录用一个线程模拟,线程运行时将一个表示计数的变量递增.程序在最后输出这个计数的值表示今天有多少用户登录.如果这个值不等于我们启动的线程个数,那这个程序就是有问题的. #include <stdio.h> #include <process.h> #include <Windows.h> volatile long g_nLogin

多线程笔记 - day01

1.基本概念 01-进程 进程是指在系统中正在运行的一个运行程序.每个进程之间是独立的,每个进程均在其专用且受保护的内存空间内. 02-线程 2-1 基本概念 1进程要执行任务,一定要有线程(每个进程至少一个线程),线程是进程的基本执行单位,一个进程(程序)的所有任务都在线程中执行. 2-2 线程的串行 1个线程中任务的执行是串行的,如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务.也就是说,在同一时间内,1个线程只能执行1个任务. 03 多线程 3-1 基本概念 即1个进程

java多线程笔记(二)

      java多线程的难点是在:处理多个线程同步与并发运行时线程间的通信问题.java在处理线程同步时,常用方法有: 1.synchronized关键字. 2.Lock显示加锁. 3.信号量Semaphore.   线程同步问题引入:       创建一个银行账户Account类,在创建并启动100个线程往同一个Account类实例里面添加一块钱.在没有使用上面三种方法的情况下: 代码: import java.util.concurrent.ExecutorService; import

java 多线程笔记

一.先简单粗暴解释一下一些与线程有关的概念 1.并行与并发 并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时. 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时. 2.资源共享 多个线程调用资源,是同一个或多个资源. 3.线程安全 在并发的情况之下,代码经过多线程使用,线程的调度顺序不影响最后结果,则是线程安全的. 3.同步 Java中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全.例如使用@synchronized.

多线程笔记小记

多线程编程:1. 调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor(锁.监视器),然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁):2. 如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法). 调用某个对象的noti

Java基础知识强化之多线程笔记05:Java中继承thread类 与 实现Runnable接口的区别

1. Java中线程的创建有两种方式:  (1)通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中. (2)通过实现Runnable接口,实例化Thread类. 2. 在实际应用中,我们经常用到多线程,如车站的售票系统,车站的各个售票口相当于各个线程.当我们做这个系统的时候可能会想到两种方式来实现,继承Thread类或实现Runnable接口,现在看一下这两种方式实现的两种结果. 继承thread类 1 package com.threadtest; 2 clas