多线程总结第三篇之传统线程互斥技术

总结:传统线程互斥技术,即为synchronzied操作,锁的概念。至于为什么要加锁?新手可以好考虑一下,老鸟们无视。
重点记录几点概念:

A. synchronized 锁的是对象。且要为同一个对象,而不是对象的引用。

示例代码:
package com.victor.thread;

/**
 * 
 * @author Victor
 * 
 */
public class TraditionalSynchronizedThread_004 {
    Object o = new Object();

    void m() {
        synchronized (o) {
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName());

            }
        }
    }

    public static void main(String[] args) {

        final TraditionalSynchronizedThread_004 a = new TraditionalSynchronizedThread_004();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                a.m();
            }
        }, "t1");
        t1.start();
        Thread t2 = new Thread(new Runnable() {

            @Override
            public void run() {
                a.m();

            }
        }, "t2");
        // 在这里对象发生了改变,这里的m方法锁起不到作用了
        a.o = new Object();
        t2.start();
    }

}

B. 尽量避免将字符串常量当做锁的对象,可能出现死锁的现象。
例子1:String a=“aaa”;
String b=“aaa”;
这里的 a和b是同一个对象;这里 “aaa” 存放在同一堆内存中。
例子2:String key =null;
public String getResult(String key1,String key2){
key = key1+key2;
return key;
}
key = getResult(”a”,””);
key = getResult(“a”,””);
这里的key就不是同一个对象了。
如果是 key=“a”+””;
key=”a”+””;
这才是同一对象
编译器会自动优化key=“a”;
C. 一个类中当一个同步方法执行中,另一个非同步方法也是可以运行的。S
也就说同步方法和非同步方法是可以同时调用的

示例代码:
package com.victor.thread;

/**
 * 一个类中同步方法和非同步方法可以同时运行
 * 
 * @author Victor
 * 
 */
public class TraditionalSynchronizedThread_005 {

    public static void main(String[] args) {

        final TraditionalSynchronizedThread_005 o = new TraditionalSynchronizedThread_005();
        new Thread(new Runnable() {

            @Override
            public void run() {
                o.m1();
            }
        }).start();

        new Thread(new Runnable() {

            @Override
            public void run() {
                o.m2();
            }
        }).start();

    }

    private synchronized void m1() {

        while (true) {
            try {
                System.out.println("m1 start");
                Thread.sleep(5000);
                System.out.println("m1 end");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void m2() {

        while (true) {
            try {
                Thread.sleep(3000);
                System.out.println("m2");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }
}

D. 一个类的一个同步方法可以调用另外一个同步方法,同一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁,也就是说synchronized获得的锁是可以重(新进)入的。

示例代码:
package com.victor.thread;

/**
 * 一个类的一个同步方法可以调用另外一个同步方法,同一个线程已经拥有某个对象的锁, 再次申请的时候仍然会得到该对象的锁,
 * 也就是说synchronized获得的锁是可以重入的。
 * 
 * @author Victor
 * 
 */
public class TraditionalSynchronizedThread_006 {

    private synchronized void m1() {
        try {
            System.out.println("m1");
            Thread.sleep(1000);
            m2();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private synchronized void m2() {
        try {
            Thread.sleep(2000);
            System.out.println("m2");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        final TraditionalSynchronizedThread_006 o = new TraditionalSynchronizedThread_006();
        new Thread(new Runnable() {
            @Override
            public void run() {
                o.m1();
            }
        }).start();
    }
}

E. 对业务写方法加锁,对读方法不加锁,容易产生脏读问题(dirtyRead)
解决办法,对读也加锁

示例代码:
package com.victor.thread;
/**
 * 对业务写方法加锁,
 * 对读方法不加锁,
 * 容易产生脏读问题(dirtyRead)
 * @author Victor
 *
 */
public class TraditionalSynchronizedThread_007 {
    private String name;
    private double account;

    public synchronized void setAccount(String name,double account) {

        this.name = name;

        try {
            Thread.sleep(10000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        this.account = account;
    }

    public /*synchronized */ double getAccount(String name) {
        return account;
    }



    public static void main(String[] args) {
        final TraditionalSynchronizedThread_007 o = new TraditionalSynchronizedThread_007();

        new Thread(new Runnable() {
            @Override
            public void run() {
                o.setAccount("张三", 100);
            }
        }).start();
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(o.getAccount("张三"));
        try {
            Thread.sleep(10000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println(o.getAccount("张三"));
    }
}

F. 程序在执行过程中,如果出现异常,默认情况锁会被释放。

示例代码:
package com.victor.thread;

/**
 * 程序在执行过程中,如果出现异常,默认情况锁会被释放
 * 所以,在并发处理的过程中,有异常要多加小心,不然可能会发生不一致的情况。
 * 比如,在一个web app处理过程中,多个servlet线程共同访问同一个资源,这时如果异常处理不合适,
 * 在第一个线程中抛出异常,其他线程就会进入同步代码区,有可能会访问到异常产生时的数据。
 * 因此要非常小心的处理同步业务逻辑中的异常
 * @author Victor
 */
public class TraditionalSynchronizedThread_008 implements Runnable{

    int count =0;

    @Override
    public void run() {
        m();
    }

    private synchronized void m(){
        System.out.println(Thread.currentThread().getName()+" start");
        while (true) {
            count++;
            try {
                System.out.println(Thread.currentThread().getName()+" count:"+count);
                Thread.sleep(1000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            if(count == 5) {
                int i = 1/0; //此处抛出异常,锁将被释放,要想不被释放,可以在这里进行catch,然后让循环继续
            }
        }
    }

    public static void main(String[] args) {

        TraditionalSynchronizedThread_008 o = new TraditionalSynchronizedThread_008();
        new Thread(o).start();
        try {
            Thread.sleep(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        new Thread(o).start();
    }
}

G. synchronized优化: 同步代码块中的语句越少越好也就说细粒度锁比粗粒度锁效率更高。 采用细粒度的锁,可以使线程争用时间变短,从而提高效率

示例代码:
package com.victor.thread;

/**
 * 比较下面两个方法,哪个效率更快
 * 抽出时间来测
 * @author Victor
 */
public class TraditionalSynchronizedThread_009 {
    int count =0;
    private synchronized void m1(){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count++;
        try {
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void m2() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (this) {
            count++;
        }
        try {
            Thread.sleep(2000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值