Java并发基础总结
并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力。如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可 交互性将大大改善。现代的PC都有多个cpu或一个cpu中有多个核,是否能合理运用多核的能力将成为一个大规模应用程序的关键。 线程基本使用编写线程运行时执行的代码有两种方式:一种是创建Thread子类的一个实例并重写run方法,第二种是创建类的时候实现Runnable接口。当然,实现 Callable也算是一种方式,Callable和Future结合实现可以实现在执行完任务后获取返回值,而Runnable和Thread方式是无法获取任务执行后的结果的。 public class ThreadMain { static void main(String[] args) { MyThread myThread = new MyThread(); Thread(myThread).start(); MyThreas2().start(); } } // 第一种方式,实现Runable接口 class MyThread implements Runnable { @Override run() { System.out.println("MyThread run..."); } } 第二种方式,继承Thread类,重写run()方法 class MyThreas2 extends Thread { @Override run() { System.out.println("MyThread2 run..."); } } 一旦线程启动后start()方法会立即返回,而不会等待run()方法执行完毕后返回,就好像run方法是在另外一个cpu上执行一样。 注意:创建并运行一个线程所犯的常见错误是调用线程的run()方法而非start()方法,如下所示: Thread newThread = Thread(MyRunnable()); newThread.run(); should be start(); 起初你并不会感觉到有什么不妥,因为run()方法的确如你所愿的被调用了。但是,事实上,run()方法并非是由刚创建的新线程所执行的,而是当前线程所执行了。也就是被执行上面两行代码的线程所执行的。想要让创建的新线程执行run()方法,必须调用新线程的start方法。 Callable和Future结合实现实现在执行完任务后获取返回值: main(String[] args) { ExecutorService exec = Executors.newSingleThreadExecutor(); Future<String> future = exec.submit( CallTask()); System.out.println(future.get()); } class CallTask Callable { public String call() { return "hello"; } } 给线程设置线程名: MyTask myTask = MyTask(); Thread thread = new Thread(myTask,"myTask thread"); thread.start(); System.out.println(thread.getName()); 当创建一个线程的时候,可以给线程起一个名字。它有助于我们区分不同的线程。 ? volatile在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值。它在某些情况下比synchronized的开销更小,但是volatile不能保证变量的原子性。 volatile变量进行写操作时(汇编下有lock指令),该lock指令在多核系统下有2个作用:
多cpu下遵循缓存一致性原则,每个cpu通过嗅探在总线上传播的数据来检查自己的缓存值是否过期了,当发现缓存对应的内存地址被修改,将对应缓存行设置为无效状态,下次对数据操作会从系统内存重新读取。更多volatile知识请点击深入分析Volatile的实现原理。 ? synchronized在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,有些情况下它并不那么重了。 Java中每一个对象都可以作为锁,当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。
synchronized关键字是不能继承的,也就是说基类中的synchronized方法在子类中默认并不是synchronized的。当线程试图访问同步代码块时,必须先获得锁,退出或抛出异常时释放锁。Java中每个对象都可以作为锁,那么锁存在哪里呢?锁存在Java对象头中,如果对象是数组类型,则虚拟机用3个word(字宽) 存储对象头,如果对象是非数组类型,则用2字宽存储对象头。更多synchronized知识请点击Java SE1.6中的Synchronized。 ? 线程池线程池负责管理工作线程,包含一个等待执行的任务队列。线程池的任务队列是一个Runnable集合,工作线程负责从任务队列中取出并执行Runnable对象。 ExecutorService executor = Executors.newCachedThreadPool(); for (int i = 0; i < 5; i++) { executor.execute( MyThread2()); } executor.shutdown(); Java通过Executors提供了4种线程池:
以上几种线程池底层都是调用ThreadPoolExecutor来创建线程池的。 ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,1)">long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue)
?
当提交新任务到线程池时,其处理流程如下:
? 参考: 1、深入分析Volatile的实现原理 2、Java SE1.6中的Synchronized (编辑:北几岛) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |