Java线程机制

快开学了,作为大三的老油条,得赶紧写大二同志们的培训内容了,现在这里打点草稿吧。

先看一下线程池的内容:

线程池

int count = ???  //自己定义一个吧
//long startTime System.currentTimeMillis();
final List<Integer> l = new LinkedList<Integer>();
ThreadPoolExecutor tp = new ThreadPoolExecutor(1,3,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(count));
final Random random = new Random();
for(int i=0;i<count;i++){
    tp.execute(new Runnable(){
        @override
        public void run(){
            l.add(random.nextInt());    //random在安全性上并不是真正的随机,这里略过
        }
    })
}
//新的任务不会再被提交到线程池,但之前的都会依旧执行,通过中断方式停止空闲的(根据没有获取锁来确定)线程。
tp.shutdown();   
try{
    tp.awaitTermination(1,TimeUnit.DAYS);
    //阻塞等待shutdown请求后所有线程终止,会有时间参数,超时和中断也会令方法调用结束。
}catch(InterruptedException e){
    e.printStrackTrace();
}
//long endTime System.currentTimeMillis();

对线程池的一些解释:

关于实例化
new ThreadPoolExecutor(1,3,60,TimeUnit.SECONDS,new LinkedBlockingQueue(count));
参数实际上是:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,
BlockingQueue workQueue,RejectedExecutionHandler handler)

  • corePoolSize: 线程池维护线程的最少数量
  • maximumPoolSize:线程池维护线程的最大数量
  • keepAliveTime: 线程池维护线程所允许的空闲时间
  • unit: 线程池维护线程所允许的空闲时间的单位
  • workQueue: 线程池所使用的缓冲队列
  • handler: 线程池对拒绝任务的处理策略 //这个被缺省了

execute(Runnable param):

将任务提交到线程池
execute提交任务的策略:

  • 线程池中的数量num < corePoolSize,提交任务被线程实例化,直接处理
  • 线程池中的数量num >= corePoolSize && 线程池中的数量num < 缓冲队列workQueue,任务被放到缓冲队列。
  • 线程池中的数量num > workQueue && 线程池中的数量num < maximumPoolSize,任务被直接线程化处理
  • 线程池中的数量num > maximumPoolSize,handler处理被拒绝的任务。

关于keepAliveTime参数

线程池中的数量num > corePoolSize, 有线程空闲时间超过keepAliveTime,线程将被终止。

关于 unit参数

参数可选值为java.util.concurrent.TimeUnit中的几个静态属性:

  • NANOSECONDS、
  • MICROSECONDS、
  • MILLISECONDS、
  • SECONDS。

关于workQueue参数:

  • java.util.concurrent.ArrayBlockingQueue
  • java.util.concurrent.LinkedBlockingQueue

关于handler参数:

  • ThreadPoolExecutor.AbortPolicy() :抛出java.util.concurrent.RejectedExecutionException异常
  • ThreadPoolExecutor.CallerRunsPolicy():当抛出RejectedExecutionException异常时,会调用rejectedExecution方法
    (如果主线程没有关闭,则主线程调用run方法,源码如下:
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }

)
  • ThreadPoolExecutor.DiscardOldestPolicy() : 抛弃旧的任务
  • ThreadPoolExecutor.DiscardPolicy() : 抛弃当前的任务

除了ThreadPoolExecutor,还有定时线程池ScheduledThreadPoolExecutor等

ThreadPoolExecutor的更多解析 : 看 三石 * 道

一般线程操作

int count = ???;
//long startTime = System.currentTimeMillis();
final List<Integer> l= new LinkedList<Integer>();
final Random random = new Random();
for(int i=0;i<count;i++)
{
    Thread thread = new Thread(){
        @override
        public void run(){
            l.add(random.nextInt());
        }
    };
    thread.start();
    try{
            thread.join(); //保证各个线程顺序执行
    }catch(InterruptedException e){
        e.printStackTrace();
    }
}
//long endTime System.currentTimeMillis();

synchronized关键字 :

学习java线程基础,这就应该是个老相识了,
synchronized一般有下面三种:

静态方法同步,所有对象的方法调用互斥 : public synchronized static returnType methodName(){}
对象方法同步 , 同一对象的方法调用互斥 : public synchronizedreturnType methodName(){}
代码块的互斥同步,有synchronized(this)synchronized(ClassName.class),实现更加灵活
建议 : 尽量使用代码块的synchronized,而不是方法上的,最好避免synchronized,在单例模式的懒汉模式中就有讨论。

ReentrantLock和ReentrantReadWriteLock

  • ReentrantLock(可重入锁),提供了tryLock() [判断是否有线程持有锁] 和 公平非公平机制,但是需要显式的unlock().
  • ReentrantReadWriteLock(可重入读写锁),是对读多写少的一种优化,上过操作系统课程的人应该有很深刻的印象,ReentrantReadWriteLock提供了readLock和writeLock两种方法,具体代码实例 : 这里
  • 在线程池的源码实现中,就是用了ReentrantLock

Volatile

这个关键字保证了获取的变量只保存在主存中,永远得到最新值,而synchronized保证了本地副本和主存的同步,一般的方法获取的变量是当前线程的副本。
volatile关键字虽然保证了变量的最新,解决了可见性,但是无法控制并发,需要使用synchronized,相信大家在java学习过程中,就听过volatile不要用的说法

Atomics

在java.util.concurrent.atomic包里面,提供了很多以Atomic开头的类,用于相关的原子操作,替代了synchronized,是代码更简洁,比如

public class TestAtomics{
    AtomicInteger count =new AtomicInteger();
    public int increase(){
        return count.incrementAndGet();
    }
    public int decrease(){
        return count.decrementAndGet();
    }
}

用synchronized实现是这样的:

public class TestSync{
    int count = 0;
    public int increase(){
        synchronized(this){
            count += 1;
            return count;
        }
    }
    public int decrease(){
        synchronized(this){
            count -= 1;
            return count;
        }
    }
}

除了代码的简介性之外,性能也有了很大的提升,在《大型网站系统和Java中间件实践》一书中,解释为AtomicInteger内部调用了JNI使用了硬件支持的CAS指令。

wait、notify、notifyAll

看过java的Object对象源码的同学应该知道上面三个是Object的三个方法,notify用于唤醒一个等待线程,notifyAll用于唤醒所有的等待线程,wait则是进行等待的,在多线程中,将某个对象作为事件对象,通过上面三个方法完成线程间的状态通知。

CountDownLacth

CountDownLatch提供了这样一个机制 : M个线程等待N个线程都达到了预期状态或者完成了预期的工作时,才被触发,继续自己的后续工作。线程调用ContDownLatch的await方法进行阻塞,到达了预期状态的线程使用countDonw方法计数减一。

具体代码实现看这里

CyclicBarrier

CountDownLatch和CyclicBarrier都实现了多个线程之间的协调。CountDownLatch中,执行countDown的线程,执行完countDown之后就继续自己的工作。而在CyclicBarrier中,调用await操作的各个线程只有所有的线程都到了await方法后,才都继续下面的工作。
CyclicBarrier提供了一个runnable的构造方法,用于await后调用,很赞,还有本身的可复用性的设计也很贴心。
CyclicBarrier的代码例子,点击一下

Semaphore

看名字,就想起了操作系统,对吧,是的,在java中,提供了acquire()和release()方法,以及带有int计数的构造函数
Semaphore的代码例子,点击一下

Exchanger

这个类,用于两个线程之间交换数据的,使用Exchanger<数据类型>进行实例化,在需要交换数据的时候,调用exchanger对象.exchange(交换的数据对象);
Exchanger的代码例子,点击一下

Future和FutureTask

如果熟悉异步阻塞的话,Future作为接口,FutureTask作为Future的具体实现类,能够实现异步的工作。
使用Future,常规有一下几种方法:
* 使用Callable构造Future对象

ExecutorService executor = Executors.newSingleThreadExecutor();   
FutureTask<String> future =  new FutureTask<String>(
    new Callable<String>() {//使用Callable接口作为构造参数   
         public String call() {   
           //真正的任务在这里执行,这里的返回值类型为String,可以为任意类型   
       }});
 executor.execute(future);
 try {   
    result = future.get(5000, TimeUnit.SECONDS); 
    //取得结果,同时设置超时执行时间为5秒。同样可以用future.get(),不设置执行超时时间取得结果   
} catch (InterruptedException e) {
    //取消任务   
    futureTask.cancel(true);   
} catch (ExecutionException e) {   
    futureTask.cancel(true);   
} catch (TimeoutException e) {   
    futureTask.cancel(true);   
} finally {   
    executor.shutdown();   
}     
  • 获取执行结果
ExecutorService executor = Executors.newSingleThreadExecutor();   
FutureTask<String> future = executor.submit(   
   new Callable<String>() {
       //使用Callable接口作为构造参数   
       public String call() {   
      //真正的任务在这里执行,这里的返回值类型为String,可以为任意类型   
   }});     

并发容器

《Java程序员修炼之道》对ConcurrentHashMap、CopyOnWriteArrayList、BlockingQueue、LinkedBlockingQueue、ArrayLinkingQueue、TransferQueue[java1.7的新特性]都有不错的描述。

并发执行

  • java开发中的ForkJoinPool、ForkJoinTask的分支合并框架,怎么都觉得像Map/Reduce。这个参考一下IBM的吧

推荐书籍:

  • Java程序员修炼之道
  • java并发编程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值