创建多线程
1.继承Thread类,重写run方法是覆盖Thread类run方法
2.创建Runnable,重写run方法,是Thread的target
定时器 (java.util.Timer)
schedule:调度
3.线程互斥
线程安全:多个线程同时操作一个共享对象。
对象锁(必须是同一个锁) 类锁
4.线程互斥和通信
将控制台日志输出到文件
要用到共同数据(包括锁)要设计到同一个方法上,是高内聚低耦合的一个体现。以下是例子
题目:子线程循环10次,接着主线程执行100次,接着又回到子线程执行10次,接着再回到主线程执行100次,如此循环50次,请写出程序。
public class TraditionalThreadCommunication {
/**
* @param args
*/
public static void main(String[] args) {
final Business business = new Business();
new Thread(
new Runnable() {
@Override
public void run() {
for(int i=1;i<=50;i++){
business.sub(i);
}
}
}
).start();
for(int i=1;i<=50;i++){
business.main(i);
}
}
}
class Business {
private boolean bShouldSub = true;
public synchronized void sub(int i){
while(!bShouldSub){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int j=1;j<=10;j++){
System.out.println("sub thread sequence of " + j + ",loop of " + i);
}
bShouldSub = false;
this.notify();
}
public synchronized void main(int i){
while(bShouldSub){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int j=1;j<=100;j++){
System.out.println("main thread sequence of " + j + ",loop of " + i);
}
bShouldSub = true;
this.notify();
}
}
5.线程范围内的数据是独立的,线程内共享数据,线程外独立数据。---典型ThreadLocal
6.ThreadLocal等同Map,放数据threadLocal.set(data);
取数据threadLocal.get();
一个ThreadLocal代表一个变量,故其中只能放一个数据。你有两个变量都要线程范围内共享,则要定义两个ThreadLocal对象。如果有一百个变量要共享,那么先定义一个对象装这一百个变量,然后在ThreadLocal中存储这一对象。
单例模式中将实例添加到ThreadLocal,代码实现更优雅。
class MyThreadScopeData{
private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
private MyThreadScopeData(){}
public static MyThreadScopeData getThreadInstance(){
MyThreadScopeData instance = map.get();
if(instance == null){
instance = new MyThreadScopeData();
map.set(instance);
}
return instance;
}
虚拟机对应Runntime类
addShutdowhook():注册回调思想。
可以捕获线程死亡前事件:回调,清理ThreadLocal中对象
8.多个线程访问共享数据方式,可以封装Runnable对象。
题目:设计4个线程,其中2个每次对j加1,另外2个线程对j每次j减1,写出程序。
public class ThreadTest2 {
private int j;
public static void main(String[] args) throws Exception {
ThreadTest2 tt = new ThreadTest2();
Inc inc = tt.new Inc();
Dec dec = tt.new Dec();
for(int i=0;i<2;i++) {
Thread t = new Thread(inc);
t.start();
t.join();
t = new Thread(dec);
t.start();
t.join();
}
}
private synchronized void inc() {
j++;
System.out.println(Thread.currentThread().getName()+"-inc:"+j);
}
private synchronized void dec() {
j--;
System.out.println(Thread.currentThread().getName()+"-dec:"+j);
}
class Inc implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++) {
inc();
}
}
}
class Dec implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++) {
dec();
}
}
}
}
10.线程池框架
Executors
创建固定大小的线程池
创建缓存线程池
创建单一线程池(线程死后会重新启动一个新的线程)
关闭线程池
shutdown和shutdownNow的区别
shutdown会等待线程执行完后关闭线程池
shutdownNow不等待直接关闭
用线程池启动定时器
10.Callable和Future:获得线程返回或执行结果
Callable要采用ExecutorService的submit方法提交,返回的Future对象可以取消任务。
Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的。
ExecutorService.execute(Runnable) void
ExecutorService.submit(Callable) Future
CompletionService用于提交一组Callable任务,其take方法返回已经完成的一个Callable任务对应的Future对象。
11.Lock&Condtion:实现线程互斥和通信
Lock比Synorchized更面向对象,两个线程执行的代码片段要是实现同步互斥的效果,它们必须用同一个Lock对象。锁是上在代表要操作的资源的类的内部方法中,而不是线程代码中。
Condtion功能类似传统工程中Object.wait()和Object.notify()功能。在等待Condtion时,允许发生虚假唤醒,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为Condtion应该总是在一个循环中被等待,并测试正被等待的状态生命。某个实现可以随意的移除虚假唤醒,但建议应用程序总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。
一个锁内部可以有多个Condtion,既有多路等待和通知。object的wait、notify只能一路等待。
static class Outputer{
Lock lock = new ReentrantLock();
public void output(String name){
int len = name.length();
lock.lock();
try{
for(int i=0;i<len;i++){
System.out.print(name.charAt(i));
}
System.out.println();
}finally{
lock.unlock();
}
}
读写锁:分为读锁和写锁。多个读锁不互斥,读锁与写锁互斥。写锁与写锁互斥。
class Queue3{
private Object data = null;//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。
ReadWriteLock rwl = new ReentrantReadWriteLock();
public void get(){
rwl.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to read data!");
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName() + "have read data :" + data);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.readLock().unlock();
}
}
public void put(Object data){
rwl.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " be ready to write data!");
Thread.sleep((long)(Math.random()*1000));
this.data = data;
System.out.println(Thread.currentThread().getName() + " have write data: " + data);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
rwl.writeLock().unlock();
}
}
}
锁降级:在释放写锁前,挂一把读锁。
设计一个缓存系统
public class CacheDemo {
private Map<String, Object> cache = new HashMap<String, Object>();
private ReadWriteLock rwl = new ReentrantReadWriteLock();
public Object getData(String key){
rwl.readLock().lock();
Object value = null;
try{
value = cache.get(key);
if(value == null){
rwl.readLock().unlock();
rwl.writeLock().lock();
try{
if(value==null){
value = "aaaa";//实际查询库queryDB();
}
}finally{
rwl.writeLock().unlock();
}
rwl.readLock().lock();
}
}finally{
rwl.readLock().unlock();
}
return value;
}
}
Condtion的使用
static class Business {
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
private boolean bShouldSub = true;
public void sub(int i){
lock.lock();
try{
while(!bShouldSub){
try {
condition.await();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int j=1;j<=10;j++){
System.out.println("sub thread sequence of " + j + ",loop of " + i);
}
bShouldSub = false;
condition.signal();
}finally{
lock.unlock();
}
}
public void main(int i){
lock.lock();
try{
while(bShouldSub){
try {
condition.await();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for(int j=1;j<=100;j++){
System.out.println("main thread sequence of " + j + ",loop of " + i);
}
bShouldSub = true;
condition.signal();
}finally{
lock.unlock();
}
}
}
多路Condtion情况实现可阻塞缓冲队列
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();
}
}
}
14.java5 Semaphore实现信号灯,Semaphore可以维护当前访问自身的线程的个数,并提供了同步机制。使用Semaphore可以同时访问资源的线程个数,例如:实现一个文件允许的并发访问数。
单个信号量的Semaphore对象可以实现互斥锁功能,并且可以是由一个线程获得了锁,再由另一个线程释放锁,这可以用于死锁回复的一些场合。
15.其他同步工具类
CyclicBarrier:表示大家彼此等待,大家集合好后才出发,分散活动后又在指定的地点集合。比如:旅游
CountDownLatch:犹如倒计时计数器,调用CountDownLatch的countDown()方法就将计数器减1,当计数到达0时,则所有等待者或单个等待着开始执行。
可以向CountDownLatch对象设置一个初始的数字作为计数值,任何调用这个对象上的await()方法都会阻塞,直到这个计数器的计数值被其他的线程减为0为止。
例如:百米赛跑。裁判吹哨,3个运动员跑。在终点裁判等待结果,总共是3,来一个计数器减1,都到后计数器减为0,裁判开始公布成绩。
可以实现一个人等多个人,或多个人等一个人
Exchanger:用于实现两个人之间的数据交换,每个人在完成一定的事务后想与对方交换数据,第一个先拿出数据的人将一直等待第二人拿着数据到来,才能彼此交换数据。
例如:卖东西的用东西交换钱,买东西的用钱交换东西。卖东西先到等待,买东西先到也等待。两者同时到达后开始交换数据。
18.并发库下集合
阻塞队列:顶级接口BlockingQueue
SynchronousQueue:同步队列,每个插入操作必须等待另一个线程的对应移除操作
队列包含固定长度的队列和不固定长度的队列。
阻塞队列:ArrayBlockingQueue
阻塞队列和Semaphore有些类似,但也不同。阻塞队列是一方存数据,另一方释放数据。Semaphore通常则是由同一方设置和释放信号量。
匿名构造方法:没有名字的构造方法,每创建一个对象就执行一次。
{
Collections.synchronizedMap(null);
try {
System.out.println("xxxxxdfsdsafdsa");
queue2.put(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
static构造方法:类加载时执行一次,且只执行一次。
死锁:两个线程去拿同一把锁,线程1先拿到,却因某种原因阻塞在那里,线程2在等待拿锁。程序看上去卡死在里。
数组与链表区别:数组是连续的内存空间,链表不连续
并发集合
hashmap
hashset:hashset的内部使用的是hashmap。是hashmap的key。
java5前集合问题:1.线程不安全 2.迭代集合(ArrayList)时添加、删除都有问题
copyOnWriteArrayList:写时拷贝,解决了集合迭代时添加和删除的问题。
wait和sleep区别
wait会释放锁
sleep不会释放