Java 并发

来源:互联网 发布:淘宝怎么买盗版小说 编辑:程序博客网 时间:2024/06/04 18:10

1 什么是并发问题。

多个进程或线程同时(或着说在同一段时间内)访问同一资源会产生并发问题。

银行两操作员同时操作同一账户就是典型的例子。比如A、B操作员同时读取一余额为1000元的账户,A操作员为该账户增加100元,B操作员同时为该账户减去 50元,A先提交,B后提交。 最后实际账户余额为1000-50=950元,但本该为 1000+100-50=1050。这就是典型的并发问题。如何解决?可以用锁。

2 java中synchronized的用法

  1. 用法1
    public class Test{    public synchronized void print(){        ....;    } }

    某线程执行print()方法,则该对象将加锁。其它线程将无法执行该对象的所有synchronized块。

  2. 用法2
    public class Test{    public void print(){        synchronized(this){//锁住本对象            ...;        }    }}

    同用法1, 但更能体现synchronized用法的本质。

  3. 用法3
    public class Test{    private String a = "test";    public void print(){        synchronized(a){//锁住a对象            ...;        }    }    public synchronized void t(){        ...; //这个同步代码块不会因为print()而锁定.    }}

    执行print(),会给对象a加锁,注意不是给Test的对象加锁,也就是说 Test对象的其它synchronized方法不会因为print()而被锁。同步代码块执行完,则释放对a的锁。

    为了锁住一个对象的代码块而不影响该对象其它 synchronized块的高性能写法:

    public class Test{    private byte[] lock = new byte[0];    public void print(){        synchronized(lock){            ...;        }    }    public synchronized void t(){        ...;     }}
  4. 静态方法的锁
    public class Test{    public synchronized static void execute(){        ...;    }}

    效果同

    public class Test{    public static void execute(){        synchronized(TestThread.class){            ...;        }    }}

3 使用Executors、Executor、ExecutorService、ThreadPoolExecutor

可以使用线程管理任务。还可以使用jdk1.5提供的一组类来更方便的管理任务。从这些类里我们可以体会一种面向任务的思维方式。这些类是:

  1. Executor接口。使用方法:
    Executor executor = anExecutor;//生成一个Executor实例。executor.execute(new RunnableTask1());

    用意:使用者只关注任务执行,不用操心去关注任务的创建、以及执行细节等这些第三方实现者关心的问题。也就是说,把任务的调用执行和任务的实现解耦。

    实际上,JDK1.5中已经有该接口出色的实现。够用了。

  2. Executors是一个如同Collections一样的工厂类或工具类,用来产生各种不同接口的实例。
  3. ExecutorService接口它继承自Executor. Executor只管把任务扔进 executor()里去执行,剩余的事就不管了。而ExecutorService则不同,它会多做点控制工作。比如:
    class NetworkService {    private final ServerSocket serverSocket;    private final ExecutorService pool;    public NetworkService(int port, int poolSize) throws IOException {        serverSocket = new ServerSocket(port);        pool = Executors.newFixedThreadPool(poolSize);    }     public void serve() {        try {            for (;;) {                pool.execute(new Handler(serverSocket.accept()));            }        } catch (IOException ex) {            pool.shutdown(); //不再执行新任务        }    }}class Handler implements Runnable {    private final Socket socket;    Handler(Socket socket) { this.socket = socket; }    public void run() {        // read and service request    }}

    ExecutorService(也就是代码里的pool对象)执行shutdown后,它就不能再执行新任务了,但老任务会继续执行完毕,那些等待执行的任务也不再等待了。

  4. 任务提交者与执行者通讯
    public static void main(String args[])throws Exception {    ExecutorService executor = Executors.newSingleThreadExecutor();    Callable<String> task = new Callable<String>(){        public String call()throws Exception{            return "test";        }    };    Future<String> f = executor.submit(task);     String result = f.get();//等待(阻塞)返回结果    System.out.println(result);    executor.shutdown();                }

    Executors.newSingleThreadExecutor()取得的Executor实例有以下特性:

    1. 任务顺序执行. 比如:
      executor.submit(task1);executor.submit(task2);

      必须等task1执行完,task2才能执行。

    2. task1和task2会被放入一个队列里,由一个工作线程来处理。即:一共有2个线程(主线程、处理任务的工作线程)。
  5. 其它的类请参考Java Doc
  6. 简单例子1
package concurrent; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TestCachedThreadPool {         public static void main(String[] args) { //                ExecutorService executorService = Executors.newCachedThreadPool();                 ExecutorService executorService = Executors.newFixedThreadPool(5);//         ExecutorService executorService = Executors.newSingleThreadExecutor();                for (int i = 0; i < 5; i++) {                         executorService.execute(new TestRunnable());                         System.out.println("************* a" + i + " *************");                 }                 executorService.shutdown();         } } class TestRunnable implements Runnable {         public void run() {                 System.out.println(Thread.currentThread().getName() + "线程被调用了。");                 while (true) {                         try {                                 Thread.sleep(5000);                                 System.out.println(Thread.currentThread().getName());                         } catch (InterruptedException e) {                                 e.printStackTrace();                         }                 }         } }
例子2
import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; public class CallableDemo {         public static void main(String[] args) {                 ExecutorService executorService = Executors.newCachedThreadPool();                 List<Future<String>> resultList = new ArrayList<Future<String>>();                 //创建10个任务并执行                 for (int i = 0; i < 10; i++) {                         //使用ExecutorService执行Callable类型的任务,并将结果保存在future变量中                         Future<String> future = executorService.submit(new TaskWithResult(i));                         //将任务执行结果存储到List中                         resultList.add(future);                 }                 //遍历任务的结果                 for (Future<String> fs : resultList) {                         try {                                 System.out.println(fs.get());     //打印各个线程(任务)执行的结果                         } catch (InterruptedException e) {                                 e.printStackTrace();                         } catch (ExecutionException e) {                                 e.printStackTrace();                         } finally {                                 //启动一次顺序关闭,执行以前提交的任务,但不接受新任务。如果已经关闭,则调用没有其他作用。                                 executorService.shutdown();                         }                 }         } } class TaskWithResult implements Callable<String> {         private int id;         public TaskWithResult(int id) {                 this.id = id;         }         /**          * 任务的具体过程,一旦任务传给ExecutorService的submit方法,则该方法自动在一个线程上执行。          *          * @return          * @throws Exception          */         public String call() throws Exception {                 System.out.println("call()方法被自动调用,干活!!!             " + Thread.currentThread().getName());                 //一个模拟耗时的操作                 for (int i = 999999; i > 0; i--) ;                 return "call()方法被自动调用,任务的结果是:" + id + "    " + Thread.currentThread().getName();         } }

4 并发3定律

  1. Amdahl定律. 给定问题规模,可并行化部分占12%,那么即使把并行运用到极致,系统的性能最多也只能提高1/(1-0.12)=1.136倍。即:并行对提高系统性能有上限。
  2. Gustafson定律. Gustafson定律说Amdahl定律没有考虑随着cpu的增多而有更多的计算能力可被使用。其本质在于更改问题规模从而可以把Amdahl定律中那剩下的88%的串行处理并行化,从而可以突破性能门槛。本质上是一种空间换时间。
  3. Sun-Ni定律. 是前两个定律的进一步推广。其主要思想是计算的速度受限于存储而不是CPU的速度. 所以要充分利用存储空间等计算资源,尽量增大问题规模以产生更好/更精确的解.



0 0
原创粉丝点击