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

并发

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: b
05-08 17:10:10.292 3473-3473/old.pkg.com.myapplicati I/clp: a
05-08 17:10:10.292 3473-3473/old.pkg.com.myapplicati I/clp: ba
05-08 17:10:10.292 3473-3473/old.pkg.com.myapplicati I/clp: b
05-08 17:10:10.292 3473-3473/old.pkg.com.myapplicati I/clp: a
05-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 =178
05-08 17:26:20.159 4239-4256/old.pkg.com.myapplicati I/clp: b thread =178
05-08 17:26:20.159 4239-4256/old.pkg.com.myapplicati I/clp: getName thread=178
05-08 17:26:20.159 4239-4256/old.pkg.com.myapplicati I/clp: a thread =178
05-08 17:26:20.161 4239-4255/old.pkg.com.myapplicati I/clp: getFullName thread =177
05-08 17:26:20.161 4239-4255/old.pkg.com.myapplicati I/clp: b thread =177
05-08 17:26:20.161 4239-4255/old.pkg.com.myapplicati I/clp: getName thread=177
05-08 17:26:20.161 4239-4255/old.pkg.com.myapplicati I/clp: a thread =177
05-08 17:26:20.168 4239-4259/old.pkg.com.myapplicati I/clp: getFullName thread =181
05-08 17:26:20.169 4239-4259/old.pkg.com.myapplicati I/clp: b thread =181
05-08 17:26:20.169 4239-4259/old.pkg.com.myapplicati I/clp: getName thread=181
05-08 17:26:20.169 4239-4259/old.pkg.com.myapplicati I/clp: a thread =181
05-08 17:26:20.169 4239-4257/old.pkg.com.myapplicati I/clp: getFullName thread =179
05-08 17:26:20.169 4239-4257/old.pkg.com.myapplicati I/clp: b thread =179
05-08 17:26:20.169 4239-4257/old.pkg.com.myapplicati I/clp: getName thread=179
05-08 17:26:20.169 4239-4257/old.pkg.com.myapplicati I/clp: a thread =179
05-08 17:26:20.172 4239-4258/old.pkg.com.myapplicati I/clp: getFullName thread =180
05-08 17:26:20.172 4239-4258/old.pkg.com.myapplicati I/clp: b thread =180
05-08 17:26:20.172 4239-4258/old.pkg.com.myapplicati I/clp: getName thread=180
05-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=175
05-08 17:18:55.095 4039-4058/old.pkg.com.myapplicati I/clp: getName thread=177
05-08 17:18:55.095 4039-4056/old.pkg.com.myapplicati I/clp: a thread =175
05-08 17:18:55.096 4039-4058/old.pkg.com.myapplicati I/clp: a thread =177
05-08 17:18:55.097 4039-4054/old.pkg.com.myapplicati I/clp: getName thread=173
05-08 17:18:55.097 4039-4054/old.pkg.com.myapplicati I/clp: a thread =173
05-08 17:18:55.098 4039-4057/old.pkg.com.myapplicati I/clp: getFullName thread =176
05-08 17:18:55.098 4039-4057/old.pkg.com.myapplicati I/clp: b thread =176
05-08 17:18:55.098 4039-4057/old.pkg.com.myapplicati I/clp: getName thread=176
05-08 17:18:55.098 4039-4057/old.pkg.com.myapplicati I/clp: a thread =176
05-08 17:18:55.098 4039-4055/old.pkg.com.myapplicati I/clp: getFullName thread =174
05-08 17:18:55.098 4039-4055/old.pkg.com.myapplicati I/clp: b thread =174
05-08 17:18:55.098 4039-4055/old.pkg.com.myapplicati I/clp: getName thread=174
05-08 17:18:55.098 4039-4055/old.pkg.com.myapplicati I/clp: a thread =174

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

68.executor和task优先于线程

使用线程池。

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

你可能感兴趣的:(Effective Java-学习笔记(10-11章))