java线程安全之synchronized代码块(六)

来源:互联网 发布:java线程池中线程状态 编辑:程序博客网 时间:2024/06/11 19:46

科技快讯

      今天是中国航天科技集团公司一院的60岁生日。作为中国运载火箭的摇篮,从长征一号运载火箭到新一代运载火箭长征五号、长征七号,多型飞天利箭都诞生在这里。
      近日,这里又传来关于航天运输系统未来发展的新消息。在第一届中国航天运输系统发展论坛上,一院发布《2017—2045年航天运输系统发展路线图》学术成果,系统规划航天运输系统的能力建设前景与发展蓝图,进一步落实党的十九大提出的建设航天强国的战略目标。

正文

      使用synchronized声明的方法在某些情况下是有弊端的,比线程调用同步的方法执行一个很长时间的任务,那么B线程就必须等待比较长的时间才能执行,这样的情况下可以使用synchronized代码块去优化代码执行时间,也就是通常所说的减小锁的粒度。

案例如下:

public class Optimize {    public void doLongTimeTask(){        try {            System.out.println("当前线程开始:" + Thread.currentThread().getName() +                     ", 正在执行一个较长时间的业务操作,其内容不需要同步");            Thread.sleep(2000);            synchronized(this){                System.out.println("当前线程:" + Thread.currentThread().getName() +                     ", 执行同步代码块,对其同步变量进行操作");                Thread.sleep(1000);            }            System.out.println("当前线程结束:" + Thread.currentThread().getName() +                    ", 执行完毕");        } catch (InterruptedException e) {            e.printStackTrace();        }    }    public static void main(String[] args) {        final Optimize otz = new Optimize();        Thread t1 = new Thread(new Runnable() {            @Override            public void run() {                otz.doLongTimeTask();            }        },"t1");        Thread t2 = new Thread(new Runnable() {            @Override            public void run() {                otz.doLongTimeTask();            }        },"t2");        t1.start();        t2.start();    }}

打印结果:

当前线程开始:t1, 正在执行一个较长时间的业务操作,其内容不需要同步当前线程开始:t2, 正在执行一个较长时间的业务操作,其内容不需要同步当前线程:t2, 执行同步代码块,对其同步变量进行操作当前线程:t1, 执行同步代码块,对其同步变量进行操作当前线程结束:t2, 执行完毕当前线程结束:t1, 执行完毕

synchronized可以使用任意的Object进行加锁,用法比较灵活。

案例如下:

public class ObjectLock {    public void method1(){        synchronized (this) {   //对象锁            try {                System.out.println("do method1..");                Thread.sleep(2000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    public void method2(){              synchronized (ObjectLock.class) {//类锁            try {                System.out.println("do method2..");                Thread.sleep(2000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    private Object lock = new Object();    public void method3(){              synchronized (lock) {//任何对象锁            try {                System.out.println("do method3..");                Thread.sleep(2000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    public static void main(String[] args) {        final ObjectLock objLock = new ObjectLock();        Thread t1 = new Thread(new Runnable() {            @Override            public void run() {                objLock.method1();            }        });        Thread t2 = new Thread(new Runnable() {            @Override            public void run() {                objLock.method2();            }        });        Thread t3 = new Thread(new Runnable() {            @Override            public void run() {                objLock.method3();            }        });        t1.start();        t2.start();        t3.start();    }}

打印结果:

do method1..do method2..do method3..

另外特别注意一个问题,就是不要使用string的常量加锁,会出现死循环问题。

案例如下:

public class StringLock {    public void method() {        synchronized ("字符串常量") {            try {                while(true){                    System.out.println("当前线程 : "  + Thread.currentThread().getName() + "开始");                    Thread.sleep(1000);                         System.out.println("当前线程 : "  + Thread.currentThread().getName() + "结束");                }            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    public static void main(String[] args) {        final StringLock stringLock = new StringLock();        Thread t1 = new Thread(new Runnable() {            @Override            public void run() {                stringLock.method();            }        },"t1");        Thread t2 = new Thread(new Runnable() {            @Override            public void run() {                stringLock.method();            }        },"t2");        t1.start();        t2.start();    }

打印结果(这里是死循环一直都是t1线程在执行,使用string的常量加锁,会出现死循环问题):

当前线程 : t1开始当前线程 : t1结束当前线程 : t1开始当前线程 : t1结束当前线程 : t1开始当前线程 : t1结束当前线程 : t1开始当前线程 : t1结束当前线程 : t1开始

      锁对象的改变问题,当使用一个对象讲行加锁的时候,要注意对象本身发生改变的时候,那么持有的锁就不同。如果对象本身不发生改变,那么依然是同步的,即使是对象的属性发生了改变。

案例如下:

public class ModifyLock {    private String name ;    private int age ;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public synchronized void changeAttributte(String name, int age) {        try {            System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 开始");            this.setName(name);            this.setAge(age);            System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 修改对象内容为: "                     + this.getName() + ", " + this.getAge());            Thread.sleep(2000);            System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 结束");        } catch (InterruptedException e) {            e.printStackTrace();        }    }    public static void main(String[] args) {        final ModifyLock modifyLock = new ModifyLock();        Thread t1 = new Thread(new Runnable() {            @Override            public void run() {                modifyLock.changeAttributte("张三", 20);            }        },"t1");        Thread t2 = new Thread(new Runnable() {            @Override            public void run() {                modifyLock.changeAttributte("李四", 21);            }        },"t2");        t1.start();        try {            Thread.sleep(100);        } catch (InterruptedException e) {            e.printStackTrace();        }        t2.start();    }}

打印结果:

当前线程 : t1 开始当前线程 : t1 修改对象内容为: 张三, 20当前线程 : t1 结束当前线程 : t2 开始当前线程 : t2 修改对象内容为: 李四, 21当前线程 : t2 结束

死锁问题,在设计程序时就应该避免双方相互持有对方的锁的情况:

案例如下:

public class DeadLock implements Runnable{    private String tag;    private static Object lock1 = new Object();    private static Object lock2 = new Object();    public void setTag(String tag){        this.tag = tag;    }    @Override    public void run() {        if(tag.equals("a")){            synchronized (lock1) {                try {                    System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock1执行");                    Thread.sleep(2000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                synchronized (lock2) {                    System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock2执行");                }            }        }        if(tag.equals("b")){            synchronized (lock2) {                try {                    System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock2执行");                    Thread.sleep(2000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                synchronized (lock1) {                    System.out.println("当前线程 : "  + Thread.currentThread().getName() + " 进入lock1执行");                }            }        }    }    public static void main(String[] args) {        DeadLock d1 = new DeadLock();        d1.setTag("a");        DeadLock d2 = new DeadLock();        d2.setTag("b");        Thread t1 = new Thread(d1, "t1");        Thread t2 = new Thread(d2, "t2");        t1.start();        try {            Thread.sleep(500);        } catch (InterruptedException e) {            e.printStackTrace();        }        t2.start();    }}

打印结果:

当前线程 : t1 进入lock1执行当前线程 : t2 进入lock2执行

源代码:https://github.com/hfbin/Thread_Socket/tree/master/Thread/sync006

阅读全文
0 0