多线程2

来源:互联网 发布:c语言绝对值函数fabs 编辑:程序博客网 时间:2024/05/02 04:58

/**
* volatile 关键字,使一个变量在多个线程间可见
* A B线程都用到一个变量,java默认是A线程中保留一份copy,这样如果B线程修改了该变量,则A线程未必知道
* 使用volatile关键字,会让所有线程都会读到变量的修改值
*
* 在下面的代码中,running是存在于堆内存的t对象中
* 当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,在运行过程中直接使用这个copy,并不会每次都去
* 读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行
*
* 使用volatile,将会强制所有线程都去堆内存中读取running的值
*
* 可以阅读这篇文章进行更深入的理解
* http://www.cnblogs.com/nexiyi/p/java_memory_model_and_thread.html
*
* volatile并不能保证多个线程共同修改running变量时所带来的不一致问题,也就是说volatile不能替代synchronized
* @author mashibing
*/

public class T {    /*volatile*/ boolean running = true; //对比一下有无volatile的情况下,整个程序运行结果的区别    void m() {        System.out.println("m start");        while(running) {            /*            try {                TimeUnit.MILLISECONDS.sleep(10);            } catch (InterruptedException e) {                e.printStackTrace();            }*/        }        System.out.println("m end!");    }    public static void main(String[] args) {        T t = new T();        new Thread(t::m, "t1").start();        try {            TimeUnit.SECONDS.sleep(1);        } catch (InterruptedException e) {            e.printStackTrace();        }        t.running = false;    }}

/**
* volatile并不能保证多个线程共同修改running变量时所带来的不一致问题,也就是说volatile不能替代synchronized
* 运行下面的程序,并分析结果(不保证写的一致性,只保证读一致性,不保证原子性)
* @author mashibing
*/

public class T {    volatile int count = 0;     void m() {        for(int i=0; i<10000; i++) count++;    }    public static void main(String[] args) {        T t = new T();        List<Thread> threads = new ArrayList<Thread>();        for(int i=0; i<10; i++) {            threads.add(new Thread(t::m, "thread-"+i));        }        threads.forEach((o)->o.start());        threads.forEach((o)->{            try {                o.join();            } catch (InterruptedException e) {                e.printStackTrace();            }        });        System.out.println(t.count);    }}

/**
* 对比上一个程序,可以用synchronized解决,synchronized可以保证可见性和原子性,volatile只能保证可见性
* @author mashibing
*/
/**
* 解决同样的问题的更高效的方法,使用AtomXXX类
* AtomXXX类本身方法都是原子性的,但不能保证多个方法连续调用是原子性的
* @author mashibing
*/

public class T {    /*volatile*/ //int count = 0;    //Atomic** 多个方法之间依然不能保证是原子性的,仅仅一个方法适合    AtomicInteger count = new AtomicInteger(0);     /*synchronized*/ void m() {         for (int i = 0; i < 10000; i++)            //if count.get() < 1000            count.incrementAndGet(); //count++    }    public static void main(String[] args) {        T t = new T();        List<Thread> threads = new ArrayList<Thread>();        for (int i = 0; i < 10; i++) {            threads.add(new Thread(t::m, "thread-" + i));        }        threads.forEach((o) -> o.start());        threads.forEach((o) -> {            try {                o.join();            } catch (InterruptedException e) {                e.printStackTrace();            }        });        System.out.println(t.count);    }}

/**
* synchronized优化
* 同步代码块中的语句越少越好
* 比较m1和m2
* @author mashibing
*/

public class T {    int count = 0;    synchronized void m1() {        //do sth need not sync        try {            TimeUnit.SECONDS.sleep(2);        } catch (InterruptedException e) {            e.printStackTrace();        }        //业务逻辑中只有下面这句需要sync,这时不应该给整个方法上锁        count ++;        //do sth need not sync        try {            TimeUnit.SECONDS.sleep(2);        } catch (InterruptedException e) {            e.printStackTrace();        }    }    void m2() {        //do sth need not sync        try {            TimeUnit.SECONDS.sleep(2);        } catch (InterruptedException e) {            e.printStackTrace();        }        //业务逻辑中只有下面这句需要sync,这时不应该给整个方法上锁        //采用细粒度的锁,可以使线程争用时间变短,从而提高效率        synchronized(this) {            count ++;        }        //do sth need not sync        try {            TimeUnit.SECONDS.sleep(2);        } catch (InterruptedException e) {            e.printStackTrace();        }    }}

/**
* 锁定某对象o,如果o的属性发生改变,不影响锁的使用
* 但是如果o变成另外一个对象,则锁定的对象发生改变
* 应该避免将锁定对象的引用变成另外的对象
* @author mashibing
*/

public class T {    Object o = new Object();    void m() {        synchronized(o) {            while(true) {                try {                    TimeUnit.SECONDS.sleep(1);                } catch (InterruptedException e) {                    e.printStackTrace();                }                System.out.println(Thread.currentThread().getName());            }        }    }    public static void main(String[] args) {        T t = new T();        //启动第一个线程        new Thread(t::m, "t1").start();        try {            TimeUnit.SECONDS.sleep(3);        } catch (InterruptedException e) {            e.printStackTrace();        }        //创建第二个线程        Thread t2 = new Thread(t::m, "t2");        t.o = new Object(); //锁对象发生改变,所以t2线程得以执行,如果注释掉这句话,线程2将永远得不到执行机会        t2.start();    }}

/**
* 不要以字符串常量作为锁定对象
* 在下面的例子中,m1和m2其实锁定的是同一个对象
* 这种情况还会发生比较诡异的现象,比如你用到了一个类库,在该类库中代码锁定了字符串“Hello”,
* 但是你读不到源码,所以你在自己的代码中也锁定了”Hello”,这时候就有可能发生非常诡异的死锁阻塞,
* 因为你的程序和你用到的类库不经意间使用了同一把锁
*
* jetty
*
* @author mashibing
*/

public class T {    String s1 = "Hello";    String s2 = "Hello";    void m1() {        synchronized(s1) {        }    }    void m2() {        synchronized(s2) {        }    }}