java synchronized测试程序

看了些网上相关的文章,感觉有些结果不对,于是自己跑了一遍这些demo并附上每段程序结果和一些心得。

非线程安全的代码

public class Run {

    public static void main(String[] args) {

        HasSelfPrivateNum numRef = new HasSelfPrivateNum();

        ThreadA athread = new ThreadA(numRef);
        ThreadB bthread = new ThreadB(numRef);

        athread.start();
        bthread.start();

    }
}

class HasSelfPrivateNum {

    private int num = 0;

    public void addI(String username) {
        try {
            if (username.equals("a")) {
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println("b set over!");
            }
            System.out.println(username + " num=" + num);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}


class ThreadA extends Thread {

    private HasSelfPrivateNum numRef;

    public ThreadA(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("a");
    }

}



class ThreadB extends Thread {

    private HasSelfPrivateNum numRef;

    public ThreadB(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("b");
    }

}

结果:

a set over!
b set over!
b num=200
a num=200

A线程给num传100后进入sleep状态;B线程又传200,A线程恢复后打印出来的结果就是200。
给addI加上synchroized:

public class Run {

    public static void main(String[] args) {

        HasSelfPrivateNum numRef = new HasSelfPrivateNum();

        ThreadA athread = new ThreadA(numRef);
        ThreadB bthread = new ThreadB(numRef);

        athread.start();
        bthread.start();

    }
}

class HasSelfPrivateNum {

    private int num = 0;

    synchronized public void addI(String username) {
        try {
            if (username.equals("a")) {
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println("b set over!");
            }
            System.out.println(username + " num=" + num);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

class ThreadA extends Thread {

    private HasSelfPrivateNum numRef;

    public ThreadA(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("a");
    }

}

class ThreadB extends Thread {

    private HasSelfPrivateNum numRef;

    public ThreadB(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("b");
    }

}

结果:

a set over!
a num=100
b set over!
b num=200

加上锁不会出现多个线程同时进入一个方法块的情况,只有当一个代码块执行完后才执行另一个线程。

多线程多对象:

public class Run {

    public static void main(String[] args) {

        HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
        HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();

        ThreadA athread = new ThreadA(numRef1);
        ThreadB bthread = new ThreadB(numRef2);

        athread.start();
        bthread.start();

    }
}

class HasSelfPrivateNum {

    private int num = 0;

    synchronized public void addI(String username) {
        try {
            if (username.equals("a")) {
                num = 100;
                System.out.println("a set over!");
                Thread.sleep(2000);
            } else {
                num = 200;
                System.out.println("b set over!");
            }
            System.out.println(username + " num=" + num);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}


class ThreadA extends Thread {

    private HasSelfPrivateNum numRef;

    public ThreadA(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("a");
    }

}



class ThreadB extends Thread {

    private HasSelfPrivateNum numRef;

    public ThreadB(HasSelfPrivateNum numRef) {
        super();
        this.numRef = numRef;
    }

    @Override
    public void run() {
        super.run();
        numRef.addI("b");
    }

}

结果:

b set over!
a set over!
b num=200
a num=100

多对象因为相互之间是独立的,不存在要获得对象锁的问题,去掉synchronized关键字结果也是一样的。多对象也不需要保证线程安全了。

同步块synchronized (this)

public class Run {

    public static void main(String[] args) {
        ObjectService service = new ObjectService();

        ThreadA a = new ThreadA(service);
        a.setName("a");
        a.start();

        ThreadB b = new ThreadB(service);
        b.setName("b");
        b.start();
    }
}

class ObjectService {

    public void serviceMethod() {
        try {
            synchronized (this) {
                System.out.println("begin time=" + System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println("end    end=" + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


class ThreadA extends Thread {

    private ObjectService service;

    public ThreadA(ObjectService service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        super.run();
        service.serviceMethod();
    }

}


class ThreadB extends Thread {
    private ObjectService service;

    public ThreadB(ObjectService service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        super.run();
        service.serviceMethod();
    }
}

结果

begin time=1494832547802
end    end=1494832549803
begin time=1494832549803
end    end=1494832551803

synchronized (this)的this是ObjectService的一个对象。
当一个线程进入synchronized(this){}中,对象的其它包含synchronized的代码是被当前线程独占的。
关于synchronized (this)最后还会专门举例说明。

非this对象

public class Run {

    public static void main(String[] args) {

        Service service = new Service("xiaobaoge");
        ThreadA a = new ThreadA(service);
        a.setName("A");
        a.start();

        ThreadB b = new ThreadB(service);
        b.setName("B");
        b.start();
    }
}

class Service {
    String anyString = new String();
    public Service(String anyString){
        this.anyString = anyString;
        System.out.println("anyString: " + anyString);
    }

    public void setUsernamePassword(String username, String password) {
        try {
            synchronized (anyString) {
                System.out.println("thread name: " + Thread.currentThread().getName()
                        + " go to sync at: " + System.currentTimeMillis());
                Thread.sleep(3000);
                System.out.println("thread name: " + Thread.currentThread().getName()
                        + " go out of sync at: " + System.currentTimeMillis());
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

class ThreadA extends Thread {
    private Service service;
    public ThreadA(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.setUsernamePassword("a", "aa");
    }
}


class ThreadB extends Thread {

    private Service service;
    public ThreadB(Service service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.setUsernamePassword("b", "bb");
    }
}

结果:

anyString: xiaobaoge
thread name: A go to sync at: 1494833882106
thread name: A go out of sync at: 1494833885106
thread name: B go to sync at: 1494833885106
thread name: B go out of sync at: 1494833888106

能保证同步块里的代码是线程安全的。

静态synchronized同步方法

记住一句话:static方法是属于类的,用synchronized修饰static方法就是对类上锁。

public class Run {

    public static void main(String[] args) {

        ThreadA a = new ThreadA();
        a.setName("A");
        a.start();

        ThreadB b = new ThreadB();
        b.setName("B");
        b.start();

    }

}

class Service {

    synchronized public static void printA() {
        try {
            System.out.println("thread: " + Thread.currentThread().getName()
                    + " go to printA at " + System.currentTimeMillis());
            Thread.sleep(3000);
            System.out.println("thread: " + Thread.currentThread().getName()
                    + " go out printA at " + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    synchronized public static void printB() {
        System.out.println("thread: " + Thread.currentThread().getName() + " go to printB at "
                + System.currentTimeMillis());
        System.out.println("thread: " + Thread.currentThread().getName() + " go out printB at "
                + System.currentTimeMillis());
    }

}


class ThreadA extends Thread {
    @Override
    public void run() {
        Service.printA();
    }

}


class ThreadB extends Thread {
    @Override
    public void run() {
        Service.printB();
    }
}

结果:

thread: A go to printA at 1494834629062
thread: A go out printA at 1494834632062
thread: B go to printB at 1494834632062
thread: B go out printB at 1494834632062

printA 和printB 虽然是不同的方法,但因为static修饰的方法和变量都是属于类的,对static方法加锁相当于对类加锁,因此类是线程安全的。

synchronized (class)

类锁:

public class Run {

    public static void main(String[] args) {

        ThreadA a = new ThreadA();
        a.setName("A");
        a.start();

        ThreadB b = new ThreadB();
        b.setName("B");
        b.start();
    }
}

class Service {

    public static void printA() {
        synchronized (Service.class) {
            try {
                System.out.println("thread: " + Thread.currentThread().getName()
                        + " go to printA at: " + System.currentTimeMillis());
                Thread.sleep(3000);
                System.out.println("thread: " + Thread.currentThread().getName()
                        + " go out printA at: " + System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void printB() {
        synchronized (Service.class) {
            System.out.println("thread: " + Thread.currentThread().getName()
                    + " go to printB at: " + System.currentTimeMillis());
            System.out.println("thread: " + Thread.currentThread().getName()
                    + " go out printB at: " + System.currentTimeMillis());
        }
    }
}


class ThreadA extends Thread {
    @Override
    public void run() {
        Service.printA();
    }
}


class ThreadB extends Thread {
    @Override
    public void run() {
        Service.printB();
    }
}

结果:

thread: A go to printA at: 1494835990458
thread: A go out printA at: 1494835993459
thread: B go to printB at: 1494835993459
thread: B go out printB at: 1494835993459

执行printA时对类上锁,类是线程安全的。

补充关于synchronized(this)的例子

当一个线程访问类中synchronized(this)代码块时,其它没有带synchronized的代码块不是线程独占的:

public class Thread2 {
    public void m4t1() {
        synchronized (this) {
            int i = 5;
            while (i-- > 0) {
                System.out
                        .println(Thread.currentThread().getName() + " : " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ie) {
                }
            }
        }
    }

    public void m4t2() {
        int i = 5;
        while (i-- > 0) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException ie) {
            }
        }
    }

    public static void main(String[] args) {
        final Thread2 myt2 = new Thread2();
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                myt2.m4t1();
            }
        }, "t1");
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                myt2.m4t2();
            }
        }, "t2");
        t1.start();
        t2.start();
    }
}

结果:

t1 : 4
t2 : 4
t2 : 3
t1 : 3
t2 : 2
t1 : 2
t2 : 1
t1 : 1
t2 : 0
t1 : 0

如果一个线程正在访问synchronized(this)代码块,则带synchronized的其它代码块都是被当前线程独占的:

public class Thread2 {
    public void m4t1() {
        synchronized (this) {
            int i = 5;
            while (i-- > 0) {
                System.out
                        .println(Thread.currentThread().getName() + " : " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ie) {
                }
            }
        }
    }

    public void m4t2() {
        synchronized (this){
            int i = 5;
            while (i-- > 0) {
                System.out.println(Thread.currentThread().getName() + " : " + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ie) {
                }
            }
        }
    }

    public static void main(String[] args) {
        final Thread2 myt2 = new Thread2();
        Thread t1 = new Thread(new Runnable() {
            public void run() {
                myt2.m4t1();
            }
        }, "t1");
        Thread t2 = new Thread(new Runnable() {
            public void run() {
                myt2.m4t2();
            }
        }, "t2");
        t1.start();
        t2.start();
    }
}

结果:

t1 : 4
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0

带synchronized关键字的部分都是同步代码块,当一个线程访问synchronized(this)时相当于拿到了这个对象的对象锁,对同步代码块的访问需要保证当前线程有该对象的对象锁。

有点像铁索连环,一个受影响全部受影响,解锁一个其它的才能访问。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值