Effective Java-学习笔记(10-11章)

来源:互联网 发布:阿里云企业邮箱设置 编辑:程序博客网 时间:2024/06/05 00:08

并发

66.同步访问共享的可变数据

private static boolean isStop = true;Thread myThread = new Thread(new Runnable() {    @Override    public void run() {        int i = 0;        while (isStop) {            i++;            Log.i("clp", "i=" + i);        }    }});    myThread.start();    try {        Thread.sleep(1000);    } catch (Exception e) {    }    isStop = false;

上面的代码Thread的线程看不到isStop值的修改,所以会一直执行下去。
一种方式是添加volatile。private static volatile boolean isStop = true;

private static synchronized void requestStop() {    isStop = true;}private static synchronized boolean stopRequested() {    return isStop;}private static boolean isStop = false;Thread myThread = new Thread(new Runnable() {    @Override    public void run() {        int i = 0;        while (!stopRequested()) {            i++;            Log.i("clp", "i=" + i);        }    }});    myThread.start();    try {        Thread.sleep(1000);    } catch (Exception e) {    }    requestStop();

67.避免过度同步

1.在同步块中不要调用外来方法
RE:你不知道外来方法执行了什么操作

2.尽量减少同步块中代码的工作量
RE:在多核的设备上,这样会耗费性能(持续等待)

锁,是指当前线程持有的话,其他线程要想调用,需要等待当前线程的锁释放,但是如果当前线程执行到同步的代码,可以直接执行。

public class CldSyncUtil {    private static Object object = new Object();    public static String getName() {        synchronized (object) {//因为getFullName中的方法直接调用getName,是在同一个线程,所以可以直接使用            Log.i("clp", "a");            return "a";        }    }    public static String getFullName() {        synchronized (object) {            Log.i("clp", "b");            return "b" + getName();//调用同样需要同步锁的getName方法        }    }}
        for (int j = 0; j < 10; j++) {            Log.i("clp", CldSyncUtil.getFullName());        }
05-08 17:10:10.291 3473-3473/old.pkg.com.myapplicati I/clp: b05-08 17:10:10.292 3473-3473/old.pkg.com.myapplicati I/clp: a05-08 17:10:10.292 3473-3473/old.pkg.com.myapplicati I/clp: ba05-08 17:10:10.292 3473-3473/old.pkg.com.myapplicati I/clp: b05-08 17:10:10.292 3473-3473/old.pkg.com.myapplicati I/clp: a05-08 17:10:10.292 3473-3473/old.pkg.com.myapplicati I/clp: ba

上面的这种写法,代码会顺序执行。因为第一次调用,会锁住对象,for循环后面的要等待。

public class CldSyncUtil {    private static Object object = new Object();    public static String getName() {        Log.i("clp", "getName thread=" + Thread.currentThread().getId());        synchronized (object) {//因为getFullName中的方法直接调用getName,是在同一个线程,所以可以直接使用            Log.i("clp", "a");            return "a";        }    }    public static String getFullName() {        Log.i("clp", "getFullName thread =" + Thread.currentThread().getId());        synchronized (object) {            Log.i("clp", "b");            return "b" + getName();//调用同样需要同步锁的getName方法        }    }}
        for (int j = 0; j < 5; j++) {            new Thread(new Runnable() {                @Override                public void run() {                    Log.i("clp", CldSyncUtil.getFullName());                }            }).start();        }
05-08 17:26:20.159 4239-4256/old.pkg.com.myapplicati I/clp: getFullName thread =17805-08 17:26:20.159 4239-4256/old.pkg.com.myapplicati I/clp: b thread =17805-08 17:26:20.159 4239-4256/old.pkg.com.myapplicati I/clp: getName thread=17805-08 17:26:20.159 4239-4256/old.pkg.com.myapplicati I/clp: a thread =17805-08 17:26:20.161 4239-4255/old.pkg.com.myapplicati I/clp: getFullName thread =17705-08 17:26:20.161 4239-4255/old.pkg.com.myapplicati I/clp: b thread =17705-08 17:26:20.161 4239-4255/old.pkg.com.myapplicati I/clp: getName thread=17705-08 17:26:20.161 4239-4255/old.pkg.com.myapplicati I/clp: a thread =17705-08 17:26:20.168 4239-4259/old.pkg.com.myapplicati I/clp: getFullName thread =18105-08 17:26:20.169 4239-4259/old.pkg.com.myapplicati I/clp: b thread =18105-08 17:26:20.169 4239-4259/old.pkg.com.myapplicati I/clp: getName thread=18105-08 17:26:20.169 4239-4259/old.pkg.com.myapplicati I/clp: a thread =18105-08 17:26:20.169 4239-4257/old.pkg.com.myapplicati I/clp: getFullName thread =17905-08 17:26:20.169 4239-4257/old.pkg.com.myapplicati I/clp: b thread =17905-08 17:26:20.169 4239-4257/old.pkg.com.myapplicati I/clp: getName thread=17905-08 17:26:20.169 4239-4257/old.pkg.com.myapplicati I/clp: a thread =17905-08 17:26:20.172 4239-4258/old.pkg.com.myapplicati I/clp: getFullName thread =18005-08 17:26:20.172 4239-4258/old.pkg.com.myapplicati I/clp: b thread =18005-08 17:26:20.172 4239-4258/old.pkg.com.myapplicati I/clp: getName thread=18005-08 17:26:20.172 4239-4258/old.pkg.com.myapplicati I/clp: a thread =180

上面的写法,那个占用了锁,就会执行完,然后再释放给其他线程使用。

    private static Object object = new Object();    public static String getName() {        Log.i("clp", "getName thread=" + Thread.currentThread().getId());        synchronized (object) {//因为getFullName中的方法直接调用getName,是在同一个线程,所以可以直接使用            Log.i("clp", "a thread =" + Thread.currentThread().getId());            return "a";        }    }    public static String getFullName() {        Log.i("clp", "getFullName thread =" + Thread.currentThread().getId());        synchronized (object) {            Log.i("clp", "b thread =" + Thread.currentThread().getId());            return "b" + getName();//调用同样需要同步锁的getName方法        }    }
        for (int j = 0; j < 5; j++) {            final int index = j;            new Thread(new Runnable() {                @Override                public void run() {                    CldSyncUtil.getFullName();                }            }).start();        }
05-08 17:18:55.095 4039-4056/old.pkg.com.myapplicati I/clp: getName thread=17505-08 17:18:55.095 4039-4058/old.pkg.com.myapplicati I/clp: getName thread=17705-08 17:18:55.095 4039-4056/old.pkg.com.myapplicati I/clp: a thread =17505-08 17:18:55.096 4039-4058/old.pkg.com.myapplicati I/clp: a thread =17705-08 17:18:55.097 4039-4054/old.pkg.com.myapplicati I/clp: getName thread=17305-08 17:18:55.097 4039-4054/old.pkg.com.myapplicati I/clp: a thread =17305-08 17:18:55.098 4039-4057/old.pkg.com.myapplicati I/clp: getFullName thread =17605-08 17:18:55.098 4039-4057/old.pkg.com.myapplicati I/clp: b thread =17605-08 17:18:55.098 4039-4057/old.pkg.com.myapplicati I/clp: getName thread=17605-08 17:18:55.098 4039-4057/old.pkg.com.myapplicati I/clp: a thread =17605-08 17:18:55.098 4039-4055/old.pkg.com.myapplicati I/clp: getFullName thread =17405-08 17:18:55.098 4039-4055/old.pkg.com.myapplicati I/clp: b thread =17405-08 17:18:55.098 4039-4055/old.pkg.com.myapplicati I/clp: getName thread=17405-08 17:18:55.098 4039-4055/old.pkg.com.myapplicati I/clp: a thread =174

上面的两种方式,都是线程外,会快速执行完毕。 但是线程内部,要等待其中一个线程(持有锁),执行完毕之后,释放锁,另外一个线程再获得锁,去执行。

68.executor和task优先于线程

使用线程池。

工程用会大量使用到线程的地方,最好封装一个工具类,统一来调度。

0 0