java多线程编程2--对象及变量的并发访问

1.synchronized方法与锁对象

示例代码如下:

public class Test {

public static void main(String[] args) {
MyObject object = new MyObject();
ThreadA a = new ThreadA(object);
a.setName("A");
ThreadB b = new ThreadB(object);
b.setName("B");
a.start();
b.start();
}
}


class MyObject {
public void methodA(){
try{
System.out.println("begin methodA threadName == " + Thread.currentThread().getName());
Thread.sleep(1000);
System.out.println("end");
}catch(InterruptedException e){
e.printStackTrace();
}
}
}


class ThreadA extends Thread {
private MyObject object;

public ThreadA(MyObject object){
this.object = object;
}

@Override
public void run(){
super.run();
object.methodA();
}
}


class ThreadB extends Thread {
private MyObject object;

public ThreadB(MyObject object){
this.object = object;
}

@Override
public void run(){
super.run();
object.methodA();
}
}

打印输出如下:

begin methodA threadName == B
begin methodA threadName == A
end
end

分析: 线程没有排队运行,a线程访问公共类没结束,线程B就进来访问公共类methodA,导致不同步。

解决办法,在methodA方法前面加关键字synchronized。修改后输出如下:

begin methodA threadName == A
end
begin methodA threadName == B
end

分析:调用关键字synchronized声明的方法一定是排队运行的。

另外只有共享资源才需要同步化,如果线程A,B访问不同的方法或者变量,则不需要同步化。

脏读:读取变量时,此值已被其他线程更改过了。


上面例子前提是多线程访问同一个对象,如果是多个对象,则情况就有所不同

修改测试代码如下:

MyObject objectA = new MyObject();
ThreadA a = new ThreadA(objectA);
a.setName("A");
MyObject objectB = new MyObject();
ThreadB b = new ThreadB(objectB);
b.setName("B");

即线程用不同的对象构造,虽然methodA使用了synchronized修饰,但输出如下:

begin methodA threadName == A
begin methodA threadName == B
end
end

说明关键字synchronized取得的锁都是基于对象的锁,而不是基于方法的锁。

当A线程调用anyObject对象加入synchronized关键字的X方法时,A线程就获得了X方法锁,准确讲是获得了对象的锁,其它线程必须等A线程执行完毕才可以调用X方法,但B线程可以随意调用其它非Synchronized同步方法。

如果B线程想要访问声明了synchronized的非X方法,则必须等线程A执行完,也就是释放掉对象锁之后才可以调用。


synchronized锁重入

当一个线程获得了对象锁之后,再次请求对象锁时可以再次获得该对象的锁。

一个synchronized方法的内部调用奔雷其它synchronized方法,是永远可以得到锁的。

可重入锁也支持父子类继承的环境中。

即某线程获得子类对象的锁以后,可以通过子类synchronized方法访问父类synchronized方法。


出现异常,锁自动释放

当一个线程执行异常时,其所持有的锁会自动释放。


同步synchronized不具备继承性

父类方法有synchronized方法修饰,子类继承的方法,必须也加synchronized才会具有同步性,否则为非同步方法。


synchronized不光可以修饰整个方法,还可以同步语句块来使用

当一个synchronized方法执行时间很长的时候,其它线程则必须一直等待,在这样的情况下可以使用synchronized同步语句块来解决。

示例代码如下:

public class Test {


public static long aa;
public static long bb;
public static void main(String[] args) {
MyObject object = new MyObject();
ThreadA a = new ThreadA(object);
a.setName("A");
ThreadB b = new ThreadB(object);
b.setName("B");
a.start();
b.start();
try{
Thread.sleep(10000);

}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("总计:" + String.valueOf(bb-aa));
}
}


class MyObject {
synchronized public void methodA(){
try{

if(Thread.currentThread().getName().equals("A")){
Test.aa = System.currentTimeMillis();
}
System.out.println(Thread.currentThread().getName() +" begin task : " + System.currentTimeMillis());
Thread.sleep(3000);
if(Thread.currentThread().getName().equals("B")){
Test.bb = System.currentTimeMillis();
}
            System.out.println(Thread.currentThread().getName() + " end:" + System.currentTimeMillis());
}catch(InterruptedException e){
e.printStackTrace();
}
}
}


class ThreadA extends Thread {
private MyObject object;

public ThreadA(MyObject object){
this.object = object;
}

@Override
public void run(){
super.run();
object.methodA();
}
}


class ThreadB extends Thread {
private MyObject object;

public ThreadB(MyObject object){
this.object = object;
}

@Override
public void run(){
super.run();
object.methodA();
}
}

输出如下:

A begin task : 1475498046580
A end:1475498049591
B begin task : 1475498049591
B end:1475498052605
总计:6025 

说明A和B2个线程用了6秒完成任务。

修改代码如下:

class MyObject {
public void methodA(){
try{

if(Thread.currentThread().getName().equals("A")){
Test.aa = System.currentTimeMillis();
}
System.out.println(Thread.currentThread().getName() +" begin task : " + System.currentTimeMillis());
Thread.sleep(3000);
synchronized(this){
if(Thread.currentThread().getName().equals("B")){
Test.bb = System.currentTimeMillis();
}
           System.out.println(Thread.currentThread().getName() + " end:" + System.currentTimeMillis());
}

}catch(InterruptedException e){
e.printStackTrace();
}
}
}

输出如下:

A begin task : 1475498182777
B begin task : 1475498182777
A end:1475498185790
B end:1475498185790
总计:3013

使用sychronized同步代码块,可以提高运行效率,不用让其它线程一直等待整个方法,而是等待方法中某一块代码。

sychronized代码块与synchronized方法一样,都是锁定对象。


多个线程调用一个同步方法,也会出现脏读,示例代码如下:

public class Test {


public static long aa;
public static long bb;
public static void main(String[] args) {
MyList myList = new MyList();
ThreadA a = new ThreadA(myList);
a.setName("A");
ThreadB b = new ThreadB(myList);
b.setName("B");
a.start();
b.start();
}
}


class MyList {
private ArrayList<String> list = new ArrayList<String>();

synchronized public void add(String username){
System.out.println("ThreadName= " + Thread.currentThread().getName() + "执行了add方法");
list.add(username);
System.out.println("ThreadName= " + Thread.currentThread().getName() + "退出了add方法");
}
}


class ThreadA extends Thread {
private  MyList list;

public ThreadA(MyList list){
this.list = list;
}

@Override
public void run(){
for(int i=0;i<100;i++){
list.add("ThreadA" + (i+1));
}

}
}


class ThreadB extends Thread {
private  MyList list;

public ThreadB(MyList list){
this.list = list;
}

@Override
public void run(){
for(int i=0;i<100;i++){
list.add("ThreadB" + (i+1));
}

}
}

实例中add方法已经synchronized,但运行结果如下:

ThreadName= B执行了add方法
ThreadName= B退出了add方法
ThreadName= A执行了add方法
ThreadName= A退出了add方法
ThreadName= B执行了add方法
ThreadName= B退出了add方法
ThreadName= B执行了add方法
ThreadName= B退出了add方法
虽然线程打印成对的“进入”与“退出”,但是线程A和B交替执行,会导致脏读。解决办法:

将Mylist list对象“同步化”,虽然add方法是“同步化”的,但是“list”对象确没同步,修改代码如下:

class MyList {
private ArrayList<String> list = new ArrayList<String>();

public void add(String username){
synchronized(list){
System.out.println("ThreadName= " + Thread.currentThread().getName() + "执行了add方法");
list.add(username);
System.out.println("ThreadName= " + Thread.currentThread().getName() + "退出了add方法");
}

}
}

修改后执行结果如下:

ThreadName= A执行了add方法
ThreadName= A退出了add方法

...

ThreadName= A执行了add方法
ThreadName= A退出了add方法
ThreadName= A执行了add方法
ThreadName= A退出了add方法
ThreadName= B执行了add方法
ThreadName= B退出了add方法
ThreadName= B执行了add方法
ThreadName= B退出了add方法
...

进入退出"同步化",并且A和B线程执行也"同步化"

synchronized(非this对象x)的结论:

a. 当多个行程同时执行synchronized(){}同步代码块时呈同步效果

b. 当其它线程执行x对象中synchronized同步方法时呈同步效果

c. 当其他线程执行x对象方法里面的synchronized(this)代码块时呈同步效果


静态同步synchronized方法

运行效果同非static方法一致,区别在于:

synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁。

同步synchronized(class)代码块的作用 = synchronized static方法的作用一样。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值