java多线程和线程池

1.创建线程

方法有两种:继承Thread实现run()方法,实现runnable实run()方法,其实thread类也是实现了runnable接口的,
TestThread t=new TestThread();
启动线程t.start();

同一个线程对象只能启动一次,调用多次start()是无效的,出现异常。

实现Runnable接口相对于继承Thread类来说,有如下显著的优势:
(1)、适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码、数据有效分离,较好地体现了面向对象的设计思想。

(2)、可以避免由于Java的单继承特性带来的局限。开发中经常碰到这样一种情况,即:当要将已经继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么就只能采用实现Runnable接口的方式了。

(3)、增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程可以操作相同的数据,与它们的代码无关。当共享访问相同的对象时,即共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。

2 线程的状态和生命周期

1.七种状态

i.刚创建,尚未启动

ii.Runnable 可执行,start(),yield()

iii.Blocked 受阻塞的线程,notify(),notifyall,synchronized(),join()

iv.Waiting 等待线程无期限wait()

v.Timed_waiting的有期限等待,sleep()

vi.Running正在执行的线程

vii.Terminated 已退出,interrupt()

这里写图片描述

线程的几个重要的方法:

1.sleep():很常见的,让当前线程让出cpu时间片,同时进入睡眠状态,该方法不会释放对象的机锁,如果在synchroized()代码块或者方法内,其他线程依然是无法访问被同步的对象。

2.wait(),当前线程进入被同步对象的等待池内,同时释放对象的机锁,其他线程可以访问被锁对象,可以指定睡眠时间或者调用notify()或者notifyAll()唤醒在等待池的线程。

wait(),notify()和notifyAll()必须在synchronized 块内调用,否则出现异常。

3.join(),在当前线程使用t.join(),即使当前线程在t线程执行完之后继续执行。

4.yield(),当前线程让出时间片,和其他线程一起竞争下一次执行时间。

5.Interrupt(),线程被阻塞时,用t.interrupt(),向线程抛出一个异常,让其捕获从而提早将线程阻塞状态结束

6.终止线程,线程终止后不能再次start(),会出现异常

2.1线程的属性

1.Set,线程名称,优先级,是否为daemon

2.Get,线程id,名称,优先级,状态,线程池,是否为活动,是否daemon,是否中断

3.currentThread()获取当前线程

3. daemon线程和用户线程

线程优先级和调度
1.1,5,10三个等级,一般为5
2.优先级高,只是执行的概率高,不具有先后严格的顺序
3.setPriority,设置优先级

1.只有当所有的用户线程终止,进程才算终止。

2.Deamon线程是服务线程,优先级低,如服务器侦听,jvm垃圾回收,是个无限循环,所有用户线程都结束了,deamon自动结束

3.进程中只有后台线程运行时,进程就会结束。

4.线程池和线程组

线程组

1.线程组管理线程,设置优先级,等属性,安全控制。

2.线程组必须从属于其他线程组,默认是系统主线程组。

3.将线程加入到线程组需要先创建线程组对象,将其作为线程构造函数参数。

4.List()输出线程树,enumerate()复制线程组中所有线程到一个线程数组中

线程组:线程组存在的意义,首要原因是安全。java默认创建的线程都是属于系统线程组,而同一个线程组的线程是可以相互修改对方的数据的。但如果在不同的线程组中,那么就不能“跨线程组”修改数据,可以从一定程度上保证数据安全。

线程池

线程池:线程池存在的意义,首要作用是效率。线程的创建和结束都需要耗费一定的系统时间(特别是创建),不停创建和删除线程会浪费大量的时间。所以,在创建出一条线程并使其在执行完任务后不结束,而是使其进入休眠状态,在需要用时再唤醒,那么 就可以节省一定的时间。如果这样的线程比较多,那么就可以使用线程池来进行管理。保证效率。

==一般情况下,线程越多占用内存也越大,并发量也越大。==

线程组和线程池共有的特点:
1,都是管理一定数量的线程

2,都可以对线程进行控制—包括休眠,唤醒,结束,创建,中断(暂停)但并不一定包含全部这些操作。

线程池的相关类和方法

1.Callable

其功能和Runable类似,但是个泛型接口,有一个返回值的call()方法,

2.Future

future 是指定了线程管理规范的接口,具有取消,查询是否完成,获取执行结果,设置结果等操作,

3.Futuretask

futureTask 是future的实现类,同时也实现了Runnable接口,所以具备了管理线程的能力,还包装了Callable接口,其实Runnable最终也会被传为Callable接口,FutureTask执行的任务类型都是Callable,

FutureTask即可以有Thread来执行也可以提交给ExecuteService执行,

ExcutorService接口

线程池都实现了该接口包括submit()execute(),shutdown()等方法,线程池的声明周期包括:运行,关闭,终止。创建后便进入运行状态,调用shutdown()进入关闭状态,此时不在接受 新任务,但一进提及哦啊的任务继续运行。当所有已提交任务执行完进入终止状态。

一个实现类ThreadPoolExecutor的构造方法:

public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

解释一下BlockingQueue,阻塞队列即队列满时加入阻塞,有链式和数组实现的,优先级的,还有SynchronousQueue

其他参数解释参见另一文章。== ==

常用的线程池有四类:fixedThreadPool、ScheduledThreadPool、cachedThreadPool、singleThreadPool

Android的asyncTask默认使用的是singleThreadPool线程池

线程同步

线程安全:多个线程同时调用同一个对象的方法时,对另一线程产生负面影响,如果一个类不受这个影响则是线程安全类。

为了保证数据的一致性,同步保证同一时刻只有一个线程能访问数据,对其修改后将修改后的值更新到内存,是其他线程能获得新数据。

1.synchronized同步

java对象自带了对象锁属性,可以对其进行多线程的访问同步,获取指定对象(可以为object)的互斥锁,就可以进行锁定

1).同步方法synchronized void f(),锁的是该方法所在类的对象

2).synchronized 同步代码块(临界区),锁指定对象,如果是Class对象,则锁定整个类,

1.Synchronized同步时会产生死锁

2.当一个线程获得一个对象的同步锁,而等待另一个资源时可以用wait()将当前线程阻塞,释放其同步锁,然后调用notify()通知其他正在等待该对象锁的线程,然后另一个线程获得同步锁后提供前一个线程的资源,调用notify()通知其他等待线程

3.Notify()唤醒一个等待的咸亨,随机一个,notifyall()全部唤醒

4.生产消费者模型

a)同一个容器的get,和put方法,都进行同步

b)一个生产者,一个消费者线程使用同一个容器,但条件不满足时wait(),执行完后notify()通知其他的等待的线程

c)必须在类P中定义一个新的成员变量bFull来表示数据存储空间的状态,当Consumer线程取走数据后,bFull值为false,当Producer线程放入数据后,bFull值为true。只有bFull为true时,Consumer线程才能取走数据,否则就必须等待Producer线程放入新的数据后的通知;反之,只有bFull为false,Producer线程才能放入新的数据,否则就必须等待Consumer线程取走数据后的通知。

volatile 同步

是一种弱同步,保证每次更新都会更新到内存。当一个变量定义成volatile之后, 保证了此变量对所有线程的可见性,也就是说当一条线程修改了这个变量的值,新的值对于其它线程来说是可以立即得知的.此时,该变量的读写操作直接在主内存中完成.

==Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。==

原子性:即一个线程对变量操作时,另一个线程不能对该变量进行操作。

Volatile variables share the visibility features of synchronized, but none of the atomicity features.

虽然增量操作(x++)看上去类似一个单独操作,实际上它是一个由==读取-修改-写入==操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。

可重入锁–ReentrantLock和condition

和synchronized比较:

1.获取和释放的灵活性

2.轮训锁和定时锁

3.公平锁。

reentrantlock的相关方法:

lock() 获取锁;

tryLock(),tryLock(long timeout,TimeUnit )尝试获取锁,超时尝试获取锁;

unlock()解锁

newCondition()获取锁 的condition,作用类似object的wait()和notify()

lock和unlock必须成对出现,否则可能发生死锁,在finally代码块中unlock(),而Synchronized代码块不需要手动释放锁。因为ReentrantLock只是普通的java类。而synchronized则包括了锁定的信息。

信号量,Semaphore

信号量本质是ReetrantLock,信号量维护了一个信号量许可集的个数,规定了最大允许多少线程同时访问数据,通过acquire()获得许可,一个线程获得许可,信号量的可用许可减一,通过release()释放许可,释放之后可用许可加1。

如果acquire没有可用许可,那么线程会阻塞。

构造方法:Semaphore(int size)

例如:食堂的5个窗口,有10个人打饭菜,那么最大只有5人同时进行打饭菜。

循环栅栏 CyclicBarrier

是一个同步辅助类,允许一组线程等待,释放等待线程之后可重用。
CyclicBarrier(int size,Runnable runnable);

在线程内调用cyclicBarrier的await()方法,当调用该方法的线程数达到指定size个数时,等待的线程才会往下执行。

闭锁CountDownLatch

CountDownLatch(int size)

也是线程同步的辅助类,指定的一个或多个线程等待其他线程执行完成后执行。

在需要等待的线程内调用await(),该线程就会暂停执行,在被等待的线程调用countdown(),countDown计数减一,直到0,等待线程继续执行。

countDownLath无法被重置,循环栅栏可以。

循环栅栏是要执行的线程增加,countDownLatch是执行完成的线程增加。

同步集合

copyOnWriteArraylist,copyOncWriteArraySet

内部使用ReentrantLock进行同步,读时不需要枷锁,写入时加锁,将array中的元素复制,在新数组中添加,再将新数组写入到原数组中。

ConcurrentHashMap,HashTable

HashTable在高并发情况下,效率低下原因是,所有线程公用一把锁,get()也会被加锁。

concurrentHashMap使用锁分段技术,对数据分段枷锁,减少了锁的粒度,提高了效率。

BlockingQueue

阻塞队列满时添加元素线程将被阻塞,队空时取元素的线程也进入等待。当需要实现生产消费者模型问题时(还可以用wait()和ReentrantLock),使用BlockingQueue,避免了手动判断条件。

具体的api参见java文档:http://docs.oracle.com/javase/8/docs/api/
其实现类包括:ArrayBlockingQueue,LinkedBlockingQueue等,参考api文档

参考:

http://www.blogjava.net/syniii/articles/338254.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章