1 线程的三种方式:
A 实现Runnable接口 new Thread(new MyRunnable()).start();
B 继承Thread类
C Callable与Future方式
2 中断线程
interrupt方法请求终止线程(线程的中断状态被置位)
isInterrupted方法,检查这个中断状态
while( !Thread.currentThread().isInterrupted() ) { do more work!}
注:有个静态方法interrupted,也是用来测试当前线程是否被中断,但是这一方法会产 生副作用--他将当前现在的中断状态置为false.
3 线程的六种状态:
new(新生)、runnable(可运行)、blocked(阻塞)、
waitng(等待)、timed waiting(计时等待)、terminated(终止)
当一个线程视图获取一个内部的对象锁,而该锁被其他线程持有,则该线程进入阻塞状态。当线程等待另一个线程通知调度时,他自己进入等待状态。如调用Object.wait或者
Thread.join
4 wait和sleep区别
a:sleep() 方法是使线程停止运行一段时间,sleep时间间隔期满后,线程不一定立即恢 复运行。
wait()是线程交互式,如果线程对一个同步对象(方法)发出一个wait调用,改线程会暂停执行,被调用对象(方法)进入等待状态,直到被唤醒。
b:Sleep时别的线程也不可以访问锁定对象(睡着也抱着锁);
Wait时别的线程可以访问锁定对象(调用wait,锁就撒手);
5 锁
a:synchronized 如:public synchronized void push(char c);
b:java.util.concurrent.locks.ReentrantLock类,
ReentrantLock(boolean fair)
这个构造方法还可以创建一个带有公平策略的锁。公平锁偏爱等待时间最长的线程。但是,这一公平的保证将大大降低性能。
注:如果线程调度器选择忽略一个线程,而该线程为了这个锁已经等待很长时间了,哪么也没有几乎公平处理这个锁。
class X {
private final ReentrantLock lock = new ReentrantLock();
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
补充volatile关键字
有时候为了读写一两个实例域就使用同步,显然开销会很大。这里推荐使用volatile关键字。
private boolean done;
public synchronized boolean getDone(){return done;}
public synchronized void setDone(boolean done) {this.done = done;}
相当于:
private volatile boolean done;
public boolean getDone(){return done;}
public void setDone(boolean done) {this.done = done;}
6 新的锁控制和应用例子
class Bank
{
private Lock bankLock;
private Condition sFouds;
public Bank()
{
bankLock = new ReenTrantLock();
sFouds = bankLock.newCondition();//条件叫余额充足
}
public void transfer()
{
backLock.lock();
while(acounts[forms] < amount)
{
sFouds.await();
}
//do more work
sFouds.signalAll();
}
}
7.内部的锁和条件存在一些局限(synchronized)
a不能中断一个正在试图获得锁的线程。
b视图获得锁时不能设定超时。
c每个锁仅有单一条件,可能不够。
8.特殊锁
synchronized(obj) { ... }
使用一个对象的锁定来实现额外的原子操作,称为客户端锁定。client-side locking
客户端锁定非常脆弱,通常不推荐使用。
9.读写锁
java.util.concurrent.locks.ReentrantReadWriteLock类
如果很多线程从一个数据结构读取数据,而很少线程修改其中的数据的话,此类非常有用。
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private Lock readLock = rwl.readLock();
private Lock writeLock = rwl.writeLock();
public doble getTBalance()
{
readLock.lock();
try { ... }
finally {readLock.unlock();}
}
public void transfer()
{
writeLock.lock();
try { ... }
finally {writeLock.unlock();}
}
10.死锁
public class DeadLock extends Thread {
int flag=0;
static Object o1=new Object();
static Object o2=new Object();
public DeadLock(int flag){
this.flag=flag;
}
@Override
public void run() {
if(this.flag==1){
synchronized(o1){
System.out.println(Thread.currentThread().getName()+"锁定对象o1");
try {
Thread.sleep(1000);//让另一个线程能进入另一块区域,锁定对象o2
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(o2){
System.out.print("this.flag="+this.flag);
}
}
}
if(this.flag==2){
synchronized(o2){
System.out.println(Thread.currentThread().getName()+"锁定对象o2");
try {
Thread.sleep(1000);//让另一个线程能进入另一块区域,锁定对象o1
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(o1){
System.out.print("this.flag="+this.flag);
}
}
}
}
public static void main(String []args){
DeadLock d1=new DeadLock(1);
DeadLock d2=new DeadLock(2);
d1.start();
d2.start();
}
}
11.经典生产者与消费者
class SyncStack{
private int index = 0;
private char []data = new char[6];
public synchronized void push(char c){
if(index == data.length){
try{
this.wait();
}catch(InterruptedException e){}
}
this.notify();
data[index] = c;
index++;
}
public synchronized char pop(){
if(index ==0){
try{
this.wait();
}catch(InterruptedException e){}
}
this.notify();
index--;
return data[index];
}
}
class Producer implements Runnable{
SyncStack stack;
public Producer(SyncStack s){
stack = s;
}
public void run(){
for(int i=0; i<20; i++){
char c =(char)(Math.random()*26+'A');//
stack.push(c);
System.out.println("produced:"+c);
Thread.sleep((int)(Math.random()*1000));
}
}
}
class Consumer implements Runnable{
SyncStack stack;
public Consumer(SyncStack s){
stack = s;
}
public void run(){
for(int i=0;i<20;i++){
char c = stack.pop();
System.out.println("消费:"+c);
Thread.sleep((int)(Math.random()*1000));
}
}
}
public class ProduceConsumer {
public static void main(String args[]){
SyncStack stack = new SyncStack();
Runnable p=new Producer(stack);
Runnable c = new Consumer(stack);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
12 阻塞队列
阻塞队列方法分3类,取决与队列满时或空时他们的响应方式,
如果将队列当做线程管理工具来使用,将要使用put和take方法,满或空,线程阻塞。
add、 element 、remove和操作抛出异常。
在一个多线程程序中,队列会在任何时候空或者满,offer、poll、peek,这些方法有返回值。
java.util.concurrent包提供了阻塞队列的几个变种
LinkedBlockingQueue 容量默认为int的最大值,也可以指定最大容量。
LinkedBlockingDeque 是一个双端版本。
ArrayBlockingQueue 在构造时需指定容量,并且有个可选的参数来指定是否需要公平性。若设置了公平参数,则等待最长时间的线程会优先处理,
这一设置会降低性能。
PriorityBlockingQueue 带优先级的队列
13 Callable与Future
Runable封装的一个异步运行的任务,可以把它想象成一个没有参数和返回值的异步方法。
Callable与Runnable类似,但是有返回值。Future保存异步计算的记过,get方法调用被阻塞,直到计算完成。
FutureTask包装器是一种非常便利的机制,可将Callable接口转换成Future和Runable,它同时实现二者接口。
例如:
Callable<Integer> myComputation = ...;
FutureTask<Integer> task = new FutureTask<Integer>(myComputation)
new Thread(task).start();
Integer result = task.get();
例如:
class MatchCounter implements Callable<Integer>
{
private File startDir;
private String keyword;
private int count;
public MatchCounter(File startDir,String keyword)
{
this.startDir = startDir;
this.keyword = keyword;
}
@Override
public Integer call() throws Exception
{
count = 0;
File[] files = startDir.listFiles();
ArrayList<Future<Integer>> results = new ArrayList<Future<Integer>>();
for(File f: files) {
if(f.isDirectory()){
MatchCounter counter = new MatchCounter(f,keyword);
FutureTask<Integer> task = new FutureTask<Integer>(counter);
results.add(task);
new Thread(task).start();
} else{
if(search(f)){
count ++;
}
}
}
for(Future<Integer> result : results)
{
count += result.get();
}
return count;
}
public boolean search(File file) {
boolean flag = false;
try
{
Scanner in = new Scanner(file);
while(in.hasNextLine() && !flag)
{
String str = in.nextLine();
if(str.contains(keyword))
{
flag = true;
}
}
in.close();
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
return flag;
}
}
14 执行器Executor
使用线程池理由:
a:线程池包含许多空闲线程,当一线程退出时,线程不会死亡,而是在池中准备为下一请求提供服务。
b:使用线程池可以减少并发线程的数目。(创建固定线程数的线程池)
Executors类有许多静态工厂方法用来构建线程池,如:
newCachedThreadPool 必要时创建新线程;空闲线程会被保留60秒
newFuxedThreadPool 该池包含固定数量的线程;空闲线程一致被保留
newSingleThreadExecutor 只有一个线程的池,该线程顺序执行每一个提交的任务
newScheduledThreadPool 用于预定执行而构建的固定线程池,替代java.util.Timer
newSingleThreadScheduledExecutor 用于预定执行而构建的单线程池。
使用线程池应该做的事:
1)调用Executors类中的静态方法产生线程池。
2)调用submit提交Runable或Callable对象,该方法返回值为Future对象。
3)如果想要取消一个任务,或如果提交Callable对象,那就要保存好返回的Future对象。
4)当不再提交任务时,调用shutdown。
ExecutorService pool = Executors.newCachedThreadPool();
MatchCounter counter = new MathcCounter(...);
Futurn<Integer> countResult = pool.submit(counter);
ExecutorService接口常用方法
invokeAll 执行给定的任务,当所有任务完成时,返回保持任务结果的 Future 列表
List<Callable<T>> tasks = ...?;
List<Future<T>> results = executorService.invokeAll(tasks);
invokeAny 执行给定的任务,如果某个任务已成功完成(也就是未抛出异常),则返回其结果。
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
shutdown 启动线程依此顺序关闭,执行以前提交的任务,但不接受新任务
List<Runnable> shutdownNow() 试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。
submit 提交一个任务。
15 预定执行
ScheduleExecutorService接口具有为预定执行(Scheduled Execution)或重复执行而设计的方法。
Executors类的newScheduledThreadPool 和newSingleThreadScheduledExecutor方法返回实现了ScheduledExecutorService接口的对象。
java.util.concurrent.ScheduledExecutorService:
ScheduledFuture<?> schedule(Runnable command, long delay,TimeUnit unit) 预定在指定的时间之后执行
ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit)
command - 要执行的任务
initialDelay - 首次执行的延迟时间
period - 连续执行之间的周期
unit - initialDelay 和 period 参数的时间单位
16 门闩java.util.concurrent.CountDownLatch
CountDownLatch
一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
await() 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
await(long timeout, TimeUnit unit) 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
countDown() 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
CountDownLatch 很适合用来将一个任务分为n个独立的部分,等这些部分都完成后继续接下来的任务,CountDownLatch 只能出发一次,计数值不能被重置。
例:一个项目可以分为多个模块,只有但这些模块都完成后才可以继续下一步的工作。
class Module implements Callable<Integer>
{
private CountDownLatch latch;
private String moduleName;
private int time;
public Module(CountDownLatch latch, String moduleName, int time)
{
this.latch = latch;
this.moduleName = moduleName;
this.time = time;
}
private void work() throws InterruptedException
{
TimeUnit.MILLISECONDS.sleep(time);
System.out.println(moduleName + " 完成,耗时:" + time);
}
@Override
public Integer call() throws Exception
{
work();
latch.countDown();
return time;
}
}
class Controller implements Runnable
{
private CountDownLatch latch;
private List<Future<?>> runResults;
int count = 0;
public Controller(CountDownLatch latch,List<Future<?>> runResults)
{
this.latch = latch;
this.runResults = runResults;
}
@Override
public void run()
{
latch.await();
for(Future<?> f : runResults)
{
count = count + (Integer)f.get();
}
System.out.println("所有模块都完成,耗时:" + count);
}
}
public class Project
{
static final int SIZE = 20;
public static void main(String[] args) throws InterruptedException, ExecutionException
{
List<Future<?>> runResults = new ArrayList<Future<?>>();
CountDownLatch latch = new CountDownLatch(SIZE);
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < SIZE; i++)
{
Future temp = exec.submit(new Module(latch, "模块" + (i + 1), (i + 1)));
runResults.add(temp);
}
Controller controller = new Controller(latch,runResults);
exec.submit(controller);
exec.shutdown();
}
}
17 栅栏java.util.concurrent.CyclicBarrier
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point),线程才继续开始执行任务.
在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该barrier在释放等待线程后可以重用,
所以称它为循环的barrier。CyclicBarrier可以多次重复使用.
await() 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待
await(long timeout, TimeUnit unit) 在所有参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。
3个玩家同时进行通关游戏,当他们都到达或通过某一关后,才可继续往下过关.
class GameGate
{
// 关名称
private String barrierName;
// 玩家通关时间
private int passTime;
public GameGate(String barrierName, int passTime)
{
this.barrierName = barrierName;
this.passTime = passTime;
}
public String getBarrierName()
{
return barrierName;
}
public int getPassTime()
{
return passTime;
}
}
class GameRunable implements Runnable
{
// 游戏关集合
private List<GameGate> gameGates;
// 玩家名称
private String gamePlayer;
private CyclicBarrier cyclicBarrier;
public GameRunable(List<GameGate> gameGates, String gamePlayer,CyclicBarrier cyclicBarrier)
{
this.cyclicBarrier = cyclicBarrier;
this.gameGates = gameGates;
this.gamePlayer = gamePlayer;
}
public void run()
{
try
{
for (GameGate gameBarrier : gameGates)
{
Thread.sleep(gameBarrier.getPassTime() * 1000);
System.out.println(gamePlayer + " passed game barrier " + gameBarrier.getBarrierName());
cyclicBarrier.await();
}
}
}
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3);
// 为玩家1建立通关任务
List<GameGate> playerOneGates = new ArrayList<GameGate>(4);
playerOneGates.add(new GameGate("第一关", 5));
playerOneGates.add(new GameGate("第二关", 60));
playerOneGates.add(new GameGate("第三关", 8));
GameRunable task1 = new GameRunable(playerOneGates, "张三", barrier);
// 为玩家2建立通关任务
List<GameGate> playerTwoGates = new ArrayList<GameGate>(4);
playerTwoGates.add(new GameGate("第一关", 1));
playerTwoGates.add(new GameGate("第二关", 6));
playerTwoGates.add(new GameGate("第三关", 3));
GameRunable task2 = new GameRunable(playerTwoGates, "李四", barrier);
// 为玩家3建立通关任务
List<GameGate> playerThreeGates = new ArrayList<GameGate>(4);
playerThreeGates.add(new GameGate("第一关", 7));
playerThreeGates.add(new GameGate("第二关", 6));
playerThreeGates.add(new GameGate("第三关", 4));
GameRunable task3 = new GameRunable(playerThreeGates, "王五", barrier);
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.submit(task1);
executorService.submit(task2);
executorService.submit(task3);
executorService.shutdown();
}
线程复习笔记
最新推荐文章于 2023-04-03 08:00:00 发布