多线程
创建线程的两种方式
1.继承Thread,覆盖父类Run方法
2.实现Runable接口,重写Run方法
多线程机制并没有提高程序的运行效率,只是因为开启了多个线程,占用了服务器更多的带宽。
定时器
Timer
TimerTask
new Timer().schedule(new TimerTask(){
@override
public void run(){
}
},多少秒后执行run,前面执行后每隔多少秒再执行一次run);
线程间的互斥与同步通信
1.方法签名上添加Synchronized
2.需要同步的代码封装到Synchronized块中
Synchronized(该同步块所在类的字节码对象){
}
经验:
要用到共同数据(包括同步锁)的若干个方法,都应该将这些方法归在同一个类中,这种设计
符合高内聚,和程序的健壮性,便于维护。
经验:
有些时候,线程会发生伪唤醒的情况,此时在同步块内使用while(){}判断,会比使用if更具有健壮性。
因为if只会判断一次,符合条件就进入。而while在循环结束后,还会判断一次。
经验:
代码编写过程中,某些运算符,本身包含逻辑的处理,如三元运算,本身就有true和false的判断,
就不需要再使用逻辑判断了。
还有if中,不需要再添加逻辑判断。
ThreadLocal实现线程范围的共享变量
变量 ThreadLocal x = new ThreadLocal<>();
自定义类 ThreadLocal myObject = new ThreadLocal<>();
使用单例模式+单例类中,私有化一个静态的ThreadLocal<单例类> = new ThreadLocal<>(),
将单例对象放入ThreadLocal中,每次获取时,先判断ThreadLocal中有没有,有就取出,并返回,
没有就new一个,存入ThreadLocal,并返回。
来保证数据在该单例中被共享。
全局的ThreadLocal变量中,当多个线程使用set方法往里面存值后,就相当于往里面的map存储多条
记录,key分别是各个线程的线程名。当线程结束时,ThreadLocal会自动将死亡的线程释放。
可以调用ThreadLocal的clear方法,将线程全部清空。
多个线程访问共享对象和数据的方式
1.如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runable中封装共享数据,和对数据的操作。例如
买票系统。
2.如果每个系统执行的代码不同,
2.1 将共享数据封装到一个对象中,然后将对象通过实现Runnable接口对象的构造方法逐一传递给各个Runable对象,每个线程对
共享数据的操作方法也放到该对象中,这样容易实现针对该数据进行的各个操作的互斥和通信。
2.2 将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,
每个线程对共享数据的操作方法也分配给外部类。以便实现对共享数据进行的各个操作的互斥和通信,
作为内部类的各个Runable对象调用外部类的这些方法。
2.3 将共享数据封装到另一个对象中,每个线程对共享数据的操作方法也分配到那个对象中,对象作为这个外部类
中的成员变量或方法中的局部变量,每个线程的Runable对象作为外部类中的成员内部类或局部内部类。
(定义一个类,类中将共享数据定义为成员变量,并提供对该变量的操作函数,函数使用synchronized修饰,再定义多个实现了Runnable接口
口的成员内部类,该成员内部类通过内部Run方法调用同一级别的对共享数据操作的成员方法)
java5提供了线程并发库
java.util.concurrent包
automic
提供了对于基本数据类型,和类中基本数据类型的原子性操作。
AutomicInteger
java.utl.concurrent.atomic包
线程池ExecutorService threadPool =
1.固定线程池 Executors.newFixedThreadPool(3);
2.缓存线程池 Executors.newCachedThreadPool();
3
.单一线程池 Executors.newSingleThreadExecutor();(实现线程死掉后重新启动 其实就是在线程死掉后,使用原有引用变量指向一个新的线程)
关闭线程池
shutdown 所有任务执行完后,关闭所有线程
shutdownNow 当前任务完成后,关闭所有线程
线程池任务调度:给线程池中线程分配任务。可以分配多个schedule()
Executors.newScheduledThreadPool(3).schedule(Runnable,delay,unit);
Callable&Future
Future取得的结果类型必须与Callable返回的结果类型一致,通过泛型实现。
Callable采用ExecutorService的submit()方法提交,返回的future对象可以取消任务。
Future future = threadPool.submit(new Callable(){
public String call() throws Exception{
//Thread.sleep(2000);
return t.type = T;
}
});
future.get();
CompletionService 用于提交一组Callable任务,其take方法返回已完成的一个Callable任务对应的Futurure
对象。类似qq种菜机制,中下一波种子,优先成熟的可以收获或偷取。
CompletionService c = new ExecutorCompletionService
Lock&Condition实现线程同步通信
Lock比传统线程模型中的Synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象,
两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象,
锁是在代表要操作的资源的
类的内部方法中,而不是线程代码中
。
使用Lock注意事项
1.多个线程的lock对象必须一致.
2.lock需要放在线程访问的共享资源中。
Lock是一个接口
实现类有ReentrantLock
方法
lock.lock();
try{
需要锁住的代码
}finally{
//执行完成后,必须释放锁,不然可能会导致线程死在锁住的代码中,后续线程无法进入。
lock.unlock();
}
读写锁,限制代码在执行过程中是否允许同步执行读或写的操作。
对读写方法中,具体执行读写操作的代码上锁
读方法上读锁
写方法上写锁
接口ReadWriteLock
实现类 ReentrantReadWriteLock
lock.readLock().lock();
try{
需要上读锁的代码
}finally{
lock.readLock().unlock();
}
lock.writeLock().lock();
try{
}finally{
lock.writeLock.unlock();
}
Lock可以替代Synchronize完成线程互斥
Condition可以替代Object.wait()和Object.notify()完成同步通信
Condition condition = lock.newCondition();
condition.await();
condition.signal();
信号灯
Semaphore可以控制同时访问资源的线程个数。例如:实现文件的并发访问。
Semaphore sp = new Semaphore(3)
;
acquire()获得一个许可
release()释放一个许可
Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放
“锁”,这可应用于死锁恢复的一些场合。
工具类
CyclicBarrier 彼此等待,集合后分散活动 ,分散活动后在指定地点集合,集合后再分散。
CountDownLatch 倒计时记时器 实现裁判吹口号,运动员起跑,运动员都到达终点后,开始评判成绩。
Exchanger 用于实现两个人之间的数据交换,每个人在完成一定的事务后想与对方交换数据,第一个先拿
出数据的人将一直等待第二个人拿着数据到来时,才能彼此交换数据。
阻塞队列
接口BlockingQueue 没有值时报异常 没有值时为null 没有值时阻塞,等待有值(无值)时,进行存取。
ArrayBlockingQueue 存值的方式有三种,add offer put put() take()会产生阻塞
取值的方式有三种 , remove poll take