java并发

基本的线程机制
    并发编程可以将程序划分为多个分离的、独立运行的任务。通过使用多线程机制,这些独立任务(也被称为子任务)中的每一个都将由执行线程来驱动。一个纯种就是在进程中的一个单一的顺序控制流,因此,单个进程可以拥有多个并发执行的任务,使得每个任务都好像有自己的CPU一样,其底层机制是切分CPU时间。
定义任务
    使用Runnable接口:
例:
    public class LiftOff implements Runnable {
        protected int countDown = 10;
        private static int taskCount = 0;
        private final int id = taskCount++;
        public LiftOff(){}
        public String status(){
            return "#" + id + "(" + (countDown > 0 ? countDown : "Liftoff!") + "),";
        }
        public void run(){
            while(countDown-- > 0){
                System.out.print(status());
                Thread.yield();
            }
        }
    }

    public class MainThread{
        public static void main(String[] args){
            LiftOff launch = new LiftOff();
            launch.run();
        }

    }

    使用Thread类
    public class BasicThreads{
        public static void main(String[] args){
            Thread t = new Thread(new LiftOff());
            t.start();
            System.out.println("Waiting for LiftOff");
        }
    }

    public class MoreBasicThreads{
        public static void main(String[] args){
            for(int i=0;i<5;i++){
                new Thread(new LiftOff()).start();
                System.out.println("Waiting for LiftOff")
            }
        }
    }
    使用Executor

    import java.util.concurrent.*;

    public class CachedThreadPool{
        public static void main(String[] args){
            ExecutorService exec = Executors.newCachedThreadPool();
            for(int i=0;i<5;i++){
                exec.execute(new LiftOff());
            }
            exec.shutdown();
        }
    }
    单个的Executor被用来创建和管理系统中所有的任务。
    FixedThreadPool使用了有限的纯种集来执行所提交的任务。

    import java.util.concurrent.*;

    public class FixedThreadPool{
        public static void main(String[] args){
            //Constructor argument is number of threads:
            ExecutorService exec = Executors.newFixedThreadPool(5);
            for(int i=0;i<5;i++){
                exec.execute(new LiftOff());
            }
            exec.shutdown();
        }
    }
    CachedThreadPool在程序执行过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程,因此它是合理的Executor的首选。只有当这种方式会引发问题时,才需要切换到FixedThreadPool。SingleThreadExecutor就像是线程数量为1的FixedThreadPool。如果向SingleThreadExector提交了多个任务,那么这些任务将排队,每个任务都会在下一个任务开始之前运行结束,所有任务将使用相同的线程。
    使用SingleThreadExecutor

    import java.util.concurrent.*;

    public class SingleThreadExecutor{
        public static void main(String[] args){
            ExecutorService exec = Exeecutors.newSingleThreadExecutor();
            for(int i=0;i<5;i++){
                exec.execute(new LiftOff());
            }
            exec.shutdown();
        }
    }
从任务中产生返回值
    Runnable是执行工作的独立任务,但是它不返回任何值。如果希望任务在完成时能够返回一个值,那么可以实现Callable接口而不是Runnable接口。在Java SE5中引入的Callabel是一种具有类型参数的泛型,它的类型参数表示的是从方法call()(而不是run())中返回的值,并且必须使用ExecutorService.submit()方法调用它。

    //: concurrency/CallableDemo.java
    import java.util.concurrent.*;
    import java.util.*;

    class TaskWithResult implements Callable<String> {
        private int id;
        public TaskWithResult(int id) {
            this.id = id;
        }
        public String call() {
            return "result of TaskWithResult " + id;
        }
    }

    public class CallableDemo {
        public static void main(String[] args) {
            ExecutorService exec = Executors.newCachedThreadPool();
            ArrayList<Future<String>> results = new ArrayList<Future<String>>();
            for(int i = 0; i < 10; i++){
                results.add(exec.submit(new TaskWithResult(i)));
            }
            for(Future<String> fs : results){
                try {
                    // get() blocks until completion:
                    System.out.println(fs.get());
                } catch(InterruptedException e) {
                    System.out.println(e);
                    return;
                } catch(ExecutionException e) {
                    System.out.println(e);
                } finally {
                    exec.shutdown();
                }
            }
        }
    } /* Output:
result of TaskWithResult 0
result of TaskWithResult 1
result of TaskWithResult 2
result of TaskWithResult 3
result of TaskWithResult 4
result of TaskWithResult 5
result of TaskWithResult 6
result of TaskWithResult 7
result of TaskWithResult 8
result of TaskWithResult 9
*///:~
休眠
    影响任务的一种简单方法是调用sleep(),这将使任务中止执行给定的时间。在LiftOff类中,要是把对yield()的调用换成是调用sleep(),将得到如下结果:

    //: concurrency/SleepingTask.java
    // Calling sleep() to pause for a while.
    import java.util.concurrent.*;

    public class SleepingTask extends LiftOff {
        public void run() {
            try {
                while(countDown-- > 0) {
                    System.out.print(status());
                    // Old-style:
                    // Thread.sleep(100);
                    // Java SE5/6-style:
                    TimeUnit.MILLISECONDS.sleep(100);
                }
            } catch(InterruptedException e) {
                System.err.println("Interrupted");
            }
        }
        public static void main(String[] args) {
            ExecutorService exec = Executors.newCachedThreadPool();
            for(int i = 0; i < 5; i++){
                exec.execute(new SleepingTask());
            }
            exec.shutdown();
        }
    } /* Output:
#0(9), #1(9), #2(9), #3(9), #4(9), #0(8), #1(8), #2(8), #3(8), #4(8), #0(7), #1(7), #2(7), #3(7), #4(7), #0(6), #1(6), #2(6), #3(6), #4(6), #0(5), #1(5), #2(5), #3(5), #4(5), #0(4), #1(4), #2(4), #3(4), #4(4), #0(3), #1(3), #2(3), #3(3), #4(3), #0(2), #1(2), #2(2), #3(2), #4(2), #0(1), #1(1), #2(1), #3(1), #4(1), #0(Liftoff!), #1(Liftoff!), #2(Liftoff!), #3(Liftoff!), #4(Liftoff!),
*///:~
优先级
    线程的优先级将该线程的重要性传递给了调度器。尽管CPU处理现有线程的顺序是不确定的,但是调度器将倾向于让优先权最高的线程先执行。然而,这并不意味着优先权较低的线程将得不到执行(也就是说,优先权不会导致死锁)。优先级较低的线程仅仅是执行的频率较低。

    //: concurrency/SimplePriorities.java
    // Shows the use of thread priorities.
    import java.util.concurrent.*;

    public class SimplePriorities implements Runnable {
        private int countDown = 5;
        private volatile double d; // No optimization
        private int priority;
        public SimplePriorities(int priority) {
            this.priority = priority;
        }
        public String toString() {
            return Thread.currentThread() + ": " + countDown;
        }
        public void run() {
            Thread.currentThread().setPriority(priority);
            while(true) {
                // An expensive, interruptable operation:
                for(int i = 1; i < 100000; i++) {
                    d += (Math.PI + Math.E) / (double)i;
                    if(i % 1000 == 0)
                    Thread.yield();
                }
                System.out.println(this);
                if(--countDown == 0) return;
            }
        }
        public static void main(String[] args) {
            ExecutorService exec = Executors.newCachedThreadPool();
            for(int i = 0; i < 5; i++){
                exec.execute(new SimplePriorities(Thread.MIN_PRIORITY));
            }
            exec.execute(new SimplePriorities(Thread.MAX_PRIORITY));
            exec.shutdown();
        }
    } /* Output: (70% match)
Thread[pool-1-thread-6,10,main]: 5
Thread[pool-1-thread-6,10,main]: 4
Thread[pool-1-thread-6,10,main]: 3
Thread[pool-1-thread-6,10,main]: 2
Thread[pool-1-thread-6,10,main]: 1
Thread[pool-1-thread-3,1,main]: 5
Thread[pool-1-thread-2,1,main]: 5
Thread[pool-1-thread-1,1,main]: 5
Thread[pool-1-thread-5,1,main]: 5
Thread[pool-1-thread-4,1,main]: 5
...
*///:~
    尽管JDK有10个优先级,但它与多数操作系统都不能映射得很好。唯一可移植的方法是当调整优先级的时候,只使用MAX_PRIORITY,NORM_PRIORITY和MIN_PRIORITY三种级别。
让步
    如果知道已经完成了在run()方法的循环的一次迭代过程中所需的工作,就可以给线程调度机制一个暗示:可以让别的线程使用CPU了。这个暗示将通过调用yield()方法来作出(没有任何机制保证它将会被采纳)。当调用yield()时,是在建议具有相同优先级的其他纯种可以运行。
后台线程
    所谓后台(daemon)线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程。反过来说,只要有任何非后台线程还在运行,程序就不会终止。比如,执行main()的就是一个非后台线程。

    //: concurrency/SimpleDaemons.java
    // Daemon threads don't prevent the program from ending.
    import java.util.concurrent.*;
    import static net.mindview.util.Print.*;

    public class SimpleDaemons implements Runnable {
        public void run() {
            try {
                while(true) {
                    TimeUnit.MILLISECONDS.sleep(100);
                    print(Thread.currentThread() + " " + this);
                }
            } catch(InterruptedException e) {
                print("sleep() interrupted");
            }
        }
        
        public static void main(String[] args) throws Exception {
            for(int i = 0; i < 10; i++) {
                Thread daemon = new Thread(new SimpleDaemons());
                daemon.setDaemon(true); // Must call before start()
                daemon.start();
            }
            print("All daemons started");
            TimeUnit.MILLISECONDS.sleep(175);
        }
    } /* Output: (Sample)
All daemons started
Thread[Thread-0,5,main] SimpleDaemons@530daa
Thread[Thread-1,5,main] SimpleDaemons@a62fc3
Thread[Thread-2,5,main] SimpleDaemons@89ae9e
Thread[Thread-3,5,main] SimpleDaemons@1270b73
Thread[Thread-4,5,main] SimpleDaemons@60aeb0
Thread[Thread-5,5,main] SimpleDaemons@16caf43
Thread[Thread-6,5,main] SimpleDaemons@66848c
Thread[Thread-7,5,main] SimpleDaemons@8813f2
Thread[Thread-8,5,main] SimpleDaemons@1d58aae
Thread[Thread-9,5,main] SimpleDaemons@83cc67
...
*///:~
    必须在线程启动之前调用setDeamon()方法,才能把它设置为后台线程。
    一旦main()完成其工作,就没什么能阻止程序终止了,因为除了后台线程之外,已经没有线程在运行了。main()线程被设定为短暂睡眠,所以可以观察到所有后台线程启动后的结果。不这样的话,就只能看见一些后台线程创建时得到的结果。
通过编写定制的ThreadFactory可以定制由Executor创建的线程的属性(后台、优先级、名称):

    //: net/mindview/util/DaemonThreadFactory.java
    package net.mindview.util;
    import java.util.concurrent.*;

    public class DaemonThreadFactory implements ThreadFactory {
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            t.setDaemon(true);
            return t;
        }
    } ///:~
这与普通的ThreadFactory的唯一差异就是它将后台状态全部设置为了true。你现在可以用一个新的DeamonThreadFactory作为参数传递给Executor.newCachedThreadPool():

    //: concurrency/DaemonFromFactory.java
    // Using a Thread Factory to create daemons.
    import java.util.concurrent.*;
    import net.mindview.util.*;
    import static net.mindview.util.Print.*;

    public class DaemonFromFactory implements Runnable {
        public void run() {
            try {
                while(true) {
                    TimeUnit.MILLISECONDS.sleep(100);
                    print(Thread.currentThread() + " " + this);
                }
            } catch(InterruptedException e) {
                print("Interrupted");
            }
        }
        public static void main(String[] args) throws Exception {
            ExecutorService exec = Executors.newCachedThreadPool(new DaemonThreadFactory());
            for(int i = 0; i < 10; i++)
                exec.execute(new DaemonFromFactory());
            print("All daemons started");
            TimeUnit.MILLISECONDS.sleep(500); // Run for a while
        }
    } /* (Execute to see output) *///:~
    每个静态的ExecutorService创建方法都被重载为接受一个ThreadFactory对象,而这个对象被用来创建新的线程:

    //: net/mindview/util/DaemonThreadPoolExecutor.java
    package net.mindview.util;
    import java.util.concurrent.*;

    public class DaemonThreadPoolExecutor extends ThreadPoolExecutor {
        public DaemonThreadPoolExecutor() {
            super(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>(),
            new DaemonThreadFactory());
        }
    } ///:~
    可以通过isDeamon()方法来确定线程是否是一个后台线程。如果是一个后台线程,那么它创建的任何线程将被自动设置成后台线程。

    //: concurrency/Daemons.java
    // Daemon threads spawn other daemon threads.
    import java.util.concurrent.*;
    import static net.mindview.util.Print.*;

    class Daemon implements Runnable {
        private Thread[] t = new Thread[10];
        public void run() {
            for(int i = 0; i < t.length; i++) {
                t[i] = new Thread(new DaemonSpawn());
                t[i].start();
                printnb("DaemonSpawn " + i + " started, ");
            }
            for(int i = 0; i < t.length; i++)
                printnb("t[" + i + "].isDaemon() = " + t[i].isDaemon() + ", ");
            while(true)
                Thread.yield();
        }
    }

    class DaemonSpawn implements Runnable {
        public void run() {
           while(true)
               Thread.yield();
        }
    }

    public class Daemons {
        public static void main(String[] args) throws Exception {
            Thread d = new Thread(new Daemon());
            d.setDaemon(true);
            d.start();
            printnb("d.isDaemon() = " + d.isDaemon() + ", ");
            // Allow the daemon threads to
            // finish their startup processes:
            TimeUnit.SECONDS.sleep(1);
        }
    } /* Output: (Sample)
d.isDaemon() = true, DaemonSpawn 0 started, DaemonSpawn 1 started, DaemonSpawn 2 started, DaemonSpawn 3 started, DaemonSpawn 4 started, DaemonSpawn 5 started, DaemonSpawn 6 started, DaemonSpawn 7 started, DaemonSpawn 8 started, DaemonSpawn 9 started, t[0].isDaemon() = true, t[1].isDaemon() = true, t[2].isDaemon() = true, t[3].isDaemon() = true, t[4].isDaemon() = true, t[5].isDaemon() = true, t[6].isDaemon() = true, t[7].isDaemon() = true, t[8].isDaemon() = true, t[9].isDaemon() = true,
*///:~
    Daemon线程被设置成了后台模式,然后派生出许多子线程,这些线程并没有被显式地设置成后台模式,不过它们的确是后台线程。接着,Daemon线程进入了无限循环,并在循环里调用yield()方法把控制权交给其他进程。
    后台进程在不执行finally子句的情况下就会终止其run()方法:
    //: concurrency/DaemonsDontRunFinally.java
    // Daemon threads don't run the finally clause
    import java.util.concurrent.*;
    import static net.mindview.util.Print.*;

    class ADaemon implements Runnable {
        public void run() {
            try {
                print("Starting ADaemon");
                TimeUnit.SECONDS.sleep(1);
            } catch(InterruptedException e) {
                print("Exiting via InterruptedException");
            } finally {
                print("This should always run?");
            }
        }
    }

    public class DaemonsDontRunFinally {
        public static void main(String[] args) throws Exception {
            Thread t = new Thread(new ADaemon());
            t.setDaemon(true);
            t.start();
        }
    } /* Output:
Starting ADaemon
*///:~
当运行这个程序时,finally子句就不会执行,但是如果你注释掉对setDaemon()的调用,就会看到finally子句将会执行。
这种行为是正确的,即便基于前面对finally给出的承诺,并不希望出现这种行为,但情况仍将如此。当最后一个非后台线程终止时,后台线程会"突然"终止。因此一旦main()退出,JVM就会立即关闭所有的后台进程,而不会有任何的确认形式。非后台的Executor通常是一种更好的方式,因为Executor控制的所有任务可以同时被关闭。
编码的变体
   在非常简单的情况下,可能会希望使用直接从Thread继承这种可替换的方式。

    //: concurrency/SimpleThread.java
    // Inheriting directly from the Thread class.

    public class SimpleThread extends Thread {
        private int countDown = 5;
        private static int threadCount = 0;
        public SimpleThread() {
            // Store the thread name:
            super(Integer.toString(++threadCount));
            start();
        }
        public String toString() {
            return "#" + getName() + "(" + countDown + "), ";
        }
        public void run() {
            while(true) {
                System.out.print(this);
                if(--countDown == 0)
                    return;
            }
        }
        public static void main(String[] args) {
            for(int i = 0; i < 5; i++)
                new SimpleThread();
        }
    } /* Output:
#1(5), #1(4), #1(3), #1(2), #1(1), #2(5), #2(4), #2(3), #2(2), #2(1), #3(5), #3(4), #3(3), #3(2), #3(1), #4(5), #4(4), #4(3), #4(2), #4(1), #5(5), #5(4), #5(3), #5(2), #5(1),
*///:~
    实现Runnable接口使得可以继承另一个不同的类,而从Thread继承则不行。
加入一个线程
    一个线程可以在其他线程之上调用join()方法,其效果是等待一段时间直到第二个线程结束才继续执行。如果某个纯种在另一个线程上调用t.join(),此线程将被挂起。直到目录t结束才恢复(即t.isAlive()返回为假)。
    也可以在join()时带上一个超时参数(单位可以是毫秒,或者纳秒),这样如果目标线程在这段时间到期时还没有结束的话,join()方法总能返回。
    对join()方法的调用可以被中断,做法是在调用线程上调用interrupt()方法,这时需要用到try-catch子句。

    //: concurrency/Joining.java
    // Understanding join().
    import static net.mindview.util.Print.*;

    class Sleeper extends Thread {
        private int duration;
        public Sleeper(String name, int sleepTime) {
            super(name);
            duration = sleepTime;
            start();
        }
        public void run() {
            try {
                sleep(duration);
            } catch(InterruptedException e) {
                print(getName() + " was interrupted. " + "isInterrupted(): " + isInterrupted());
                return;
            }
            print(getName() + " has awakened");
        }
    }
    class Joiner extends Thread {
        private Sleeper sleeper;
        public Joiner(String name, Sleeper sleeper) {
            super(name);
            this.sleeper = sleeper;
            start();
        }
        public void run() {
            try {
                sleeper.join();
            } catch(InterruptedException e) {
                print("Interrupted");
            }
            print(getName() + " join completed");
        }
    }

    public class Joining {
        public static void main(String[] args) {
            Sleeper sleepy = new Sleeper("Sleepy", 1500),grumpy = new Sleeper("Grumpy", 1500);
        Joiner dopey = new Joiner("Dopey", sleepy),doc = new Joiner("Doc", grumpy);
        grumpy.interrupt();
    }
} /* Output:
Grumpy was interrupted. isInterrupted(): false
Doc join completed
Sleepy has awakened
Dopey join completed
*///:~
捕获异常
    由于纯种的本质特性,不能捕获从纯种中逃逸的异常。
    Thread.UncaughtExceptionHandler是Java SE5中的新接口,它允许在每个Thread对象上都附着一个异常处理器。Thread.UncaughtExceptionhandler.uncaughtException()会在线程因未捕获的异常而临近死亡时被调用。

    //: concurrency/CaptureUncaughtException.java
    import java.util.concurrent.*;

    class ExceptionThread2 implements Runnable {
        public void run() {
            Thread t = Thread.currentThread();
            System.out.println("run() by " + t);
            System.out.println("eh = " + t.getUncaughtExceptionHandler());
            throw new RuntimeException();
        }
    }

    class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
        public void uncaughtException(Thread t, Throwable e) {
            System.out.println("caught " + e);
        }
    }

    class HandlerThreadFactory implements ThreadFactory {
        public Thread newThread(Runnable r) {
            System.out.println(this + " creating new Thread");
            Thread t = new Thread(r);
            System.out.println("created " + t);
            t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
            System.out.println("eh = " + t.getUncaughtExceptionHandler());
            return t;
        }
    }

    public class CaptureUncaughtException {
        public static void main(String[] args) {
            ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory());
            exec.execute(new ExceptionThread2());
        }
    } /* Output: (90% match)
HandlerThreadFactory@de6ced creating new Thread
created Thread[Thread-0,5,main]
eh = MyUncaughtExceptionHandler@1fb8ee3
run() by Thread[Thread-0,5,main]
eh = MyUncaughtExceptionHandler@1fb8ee3
caught java.lang.RuntimeException
*///:~
    在程序中添加了额外的跟踪机制,用来验证工厂创建的线程会传递给UncaughtExceptionHandler。
共享受限资源
    可以把方法标记为synchronized来防止资源冲突。
    synchronized方法的方式:
    synchronized void f(){ /* ... */ }
    synchronized void f(){ /* ... */ }
    synchronized static方法可以在类的范围内防止对static数据的并发访问。
    如果正在写一个变量,它可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么你必须使用同步,并且,读写线程都必须用相同的监视器锁同步。
使用的Lock对象
    Java SE5的java.util.concurrent类库还包含有定义在java.util.concurrent.locks中的显式的互斥机制。Lock对象必须被显式地创建、锁定和释放。

    //: concurrency/MutexEvenGenerator.java
    // Preventing thread collisions with mutexes.
    // {RunByHand}
    import java.util.concurrent.locks.*;

    public class MutexEvenGenerator extends IntGenerator {
        private int currentEvenValue = 0;
        private Lock lock = new ReentrantLock();
        public int next() {
            lock.lock();
            try {
                ++currentEvenValue;
                Thread.yield(); // Cause failure faster
                ++currentEvenValue;
                return currentEvenValue;
            } finally {
                lock.unlock();
            }
        }
        public static void main(String[] args) {
            EvenChecker.test(new MutexEvenGenerator());
        }
    } ///:~
原子性与易变性
    原子操作是不能被线程调度机制中断的操作。
    原子性可以应用于除long和double之外的所有基本类型之上的"简单操作"。对于读取和写入除long和double之外的基本类型变量是这样的操作,可以保证它们会被当作不可分(原子)的操作来操作内在。但是JVM可以将64位(long和double变量)的读取和写入当作两个分离的32位操作来执行,这就产生了在一个读取和写入操作中间发生上下文切换,从而导致不同的任务可以看到不正确的结果的可能性(这有时被称为字撕裂,因为你可能会看到部分被修改过的数值)。但是,当你定义long和double变量时,如果使用volatile关键字,就会获得(简单的赋值与返回操作的)原子性(注意,在Java SE5之前,volatile一直未能正确地工作)。不同的JVM可以任意地提供更强的保证,但是不应该依赖于平台相关的特性。
    volatile关键字还确保了应用中的可视性。如果你将一个域声明为volatile的,那么只要对这个域产生了写操作,那么所有的读操作就都可以看到这个修改。即便使用了本地缓存,情况也确实如此,volatile域会立即被写入到主存中,而读取操作就发生在主存中。
    理解原子性和易变性是不同的概念这一点很重要。在非Volatile域上的原子操作不必刷新到主存中去,因此其他读取该域的任务也不必看到这个新值。如果多个任务在同时访问某个域,那么这个域就应该是volatile的,否则,这个域就应该只能经由同步来访问。同步也会导致向主存中刷新,因此如果一个域完全由synchronized方法或语句块来防护,那就不必将其设置为是volatile的。
    一个任务所作的任何写入操作对这个任务来说都是可视的,因此如果它只需要在这个任务内部可视,那么你就不需要将其设置为volatile的。
    当一个域的值依赖于它之前的值时(例如递增一个计数器),volatile就无法工作了。如果某个域的值受到其他域的值的限制,那么volatile也无法工作,例如Range类的lower和upper边界就必须遵循lower<=upper的限制。
    使用volatile而不是synchronized的唯一安全的情况是类中只有一个可变的域。第一选择应该是使用synchronized关键字,这是最安全的方式。
    如果一个域可能会被多个任务同时访问,或者这些任务中至少有一个是写入任务,那么就应该将这个域设置为volatile的。如果将一个域设置为volatile,那么它就会告诉编译器不要执行任何移除读取和写入操作的优化,这些操作的目的是用线程中的局部变量维护对这个域的精确同步。实际上,读取和写入都是直接针对内存的,而却没有被缓存。但是,volatile并不能对递增不是原子性操作这一事实产生影响。
原子类
    Java SE5引入了诸如AtomicInteger、AtomicLong、AtomicReference等特殊的原子性变量类,它们提供了下面形式的原子性条件更新操作:
    boolean compareAndSet(expectedValue, updateValue);
    这些类被调整为可以使用在某些现代处理器上的可获得的,并且是在机器级别上的原子性,因此在使用它们时,通常不需要担心。对于常规编程来说,它们很少会派上用场,但是在涉及性能调优时,它们就大有用武之地了。
临界区
    防止多个线程同时访问方法内部的部分代码而不是防止访问整个方法。通过这种方式分离出来的代码段被称为临界区(critical section),它也使用synchronized关键字建立。

    synchronized(syncObject){
        //This code can be accessed
        //by only one task at a time
    }
    这也被称为同步控制块;在进入此段代码前,必须得到syncObject对象的锁。如果其他线程已经得到这个锁,那么就得等到锁被释放以后,才能进入临界区。
线程本地存储
    防止任务在共享资源上产生冲突的第二种方式是根除对变量的共享。线程本地存储是一种自动化机制,可以为使用相同变量的每个不同的线程都创建不同的存储。
    创建和管理线程本地存储可以由java.lang.ThreadLocal类来实现。

    //: concurrency/ThreadLocalVariableHolder.java
    // Automatically giving each thread its own storage.
    import java.util.concurrent.*;
    import java.util.*;

    class Accessor implements Runnable {
        private final int id;
        public Accessor(int idn) { id = idn; }
        public void run() {
            while(!Thread.currentThread().isInterrupted()) {
                ThreadLocalVariableHolder.increment();
                System.out.println(this);
                Thread.yield();
            }
        }
        public String toString() {
            return "#" + id + ": " + ThreadLocalVariableHolder.get();
        }
    }

    public class ThreadLocalVariableHolder {
        private static ThreadLocal<Integer> value = new ThreadLocal<Integer>() {
            private Random rand = new Random(47);
            protected synchronized Integer initialValue() {
                return rand.nextInt(10000);
            }
        };
        public static void increment() {
            value.set(value.get() + 1);
        }
        public static int get() { return value.get(); }
        public static void main(String[] args) throws Exception {
            ExecutorService exec = Executors.newCachedThreadPool();
            for(int i = 0; i < 5; i++)
                exec.execute(new Accessor(i));
            TimeUnit.SECONDS.sleep(3);  // Run for a while
            exec.shutdownNow();         // All Accessors will quit
        }
    } /* Output: (Sample)
#0: 9259
#1: 556
#2: 6694
#3: 1862
#4: 962
#0: 9260
#1: 557
#2: 6695
#3: 1863
#4: 963
...
*///:~
ThreadLocal对象通常当作静态域存储。在创建ThreadLocal时,只能通过get()和set()方法来访问该对象的内容,其中,get()方法将返回与其线程相关的对象的副本,而set()会将参数插入到为其线程存储的对象中,并返回存储中原有的对象。
终结任务

    //: concurrency/OrnamentalGarden.java
    import java.util.concurrent.*;
    import java.util.*;
    import static net.mindview.util.Print.*;

    class Count {
        private int count = 0;
        private Random rand = new Random(47);
        // Remove the synchronized keyword to see counting fail:
        public synchronized int increment() {
            int temp = count;
            if(rand.nextBoolean()) // Yield half the time
                Thread.yield();
            return (count = ++temp);
        }
        public synchronized int value() { return count; }
    }

    class Entrance implements Runnable {
        private static Count count = new Count();
        private static List<Entrance> entrances = new ArrayList<Entrance>();
        private int number = 0;
        // Doesn't need synchronization to read:
        private final int id;
        private static volatile boolean canceled = false;
        // Atomic operation on a volatile field:
        public static void cancel() { canceled = true; }
        public Entrance(int id) {
            this.id = id;
            // Keep this task in a list. Also prevents
            // garbage collection of dead tasks:
            entrances.add(this);
        }
        public void run() {
            while(!canceled) {
                synchronized(this) {
                    ++number;
                }
                print(this + " Total: " + count.increment());
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch(InterruptedException e) {
                    print("sleep interrupted");
                }
            }
            print("Stopping " + this);
        }
        public synchronized int getValue() { return number; }
        public String toString() {
            return "Entrance " + id + ": " + getValue();
        }
        public static int getTotalCount() {
            return count.value();
        }
        public static int sumEntrances() {
            int sum = 0;
            for(Entrance entrance : entrances)
                sum += entrance.getValue();
            return sum;
        }
    }

    public class OrnamentalGarden {
        public static void main(String[] args) throws Exception {
            ExecutorService exec = Executors.newCachedThreadPool();
            for(int i = 0; i < 5; i++)
                exec.execute(new Entrance(i));
            // Run for a while, then stop and collect the data:
            TimeUnit.SECONDS.sleep(3);
            Entrance.cancel();
            exec.shutdown();
            if(!exec.awaitTermination(250, TimeUnit.MILLISECONDS))
                print("Some tasks were not terminated!");
            print("Total: " + Entrance.getTotalCount());
            print("Sum of Entrances: " + Entrance.sumEntrances());
        }
    } /* Output: (Sample)
Entrance 0: 1 Total: 1
Entrance 2: 1 Total: 3
Entrance 1: 1 Total: 2
Entrance 4: 1 Total: 5
Entrance 3: 1 Total: 4
Entrance 2: 2 Total: 6
Entrance 4: 2 Total: 7
Entrance 0: 2 Total: 8
...
Entrance 3: 29 Total: 143
Entrance 0: 29 Total: 144
Entrance 4: 29 Total: 145
Entrance 2: 30 Total: 147
Entrance 1: 30 Total: 146
Entrance 0: 30 Total: 149
Entrance 3: 30 Total: 148
Entrance 4: 30 Total: 150
Stopping Entrance 2: 30
Stopping Entrance 1: 30
Stopping Entrance 0: 30
Stopping Entrance 3: 30
Stopping Entrance 4: 30
Total: 150
Sum of Entrances: 150
*///:~

在阻塞时终结
    上面的示例中的Entrance.run()在其循环中包含对sleep()的调用。sleep()最终将唤醒,而任务也将返回循环的开始部分,去检查canceled标志,从而决定是否跳出循环。但是sleep()一种情况,它使任务从执行状态变为被阻塞状态,而有时你必须终止被阻塞的任务。
线程状态
    一个线程可以处于以下四种状态之一:
    1、新建(New):当线程被创建时,它只会短暂地处于这种状态。此时它已经分配了必需的系统资源,并执行了初始化。此刻线程已经有资格获得CPU时间了,之后调度器将把这个线程转变可运行状态或阻塞状态。
    2、就绪(Runnable):在这种状态下,只要调度器把时间片分配给线程,线程就可以运行。就是说,在任意时刻,线程可以运行也可以不运行。只要调度器能分配时间片给线程,它就可以运行;这不同于死亡和阻塞状态。
    3、阻塞(Blocked):线程能够运行,但有某个条件阻止它的运行。当线程处理阻塞状态时,调度器将忽略线程,不会分配给线程任何CPU时间。直到线程重新进入了就绪状态,它才有可能执行操作。
    4、死亡(Dead):处于死亡或终止状态的线程将不再是可调度的,并且再也不会得到CPU时间,它的任务已结束,或不再是可运行的。任务死亡的通常方式是从run()方法返回,但是任务的线程还可以被中断。
线程之间的协作
    当使用线程来同时运行多个任务时,可以通过使用锁(互斥)来同步两个任务的行为,从而使得一个任务不会干涉另一个任务的资源。也就是说,如果两个任务在交替着步入某项共享资源(通常是内存),可以使用互斥来使得任何时刻只有一个任务可以访问这项资源。
死锁
    一个任务之间相互等待的连续循环,没有哪个线程能继续。这被称之为死锁。

转载于:https://my.oschina.net/Liopleuro/blog/801175

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值