java多线程同步锁_Java多线程-线程的同步与锁

一、同步问题提出

线程的同步是为了防止多个线程访问一个数据对象时,对数据造成的破坏。

例如:两个线程ThreadA、ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据。

packagecn.thread;public classFoo {private int x = 100;public intgetX() {returnx;

}public int fix(inty) {

x= x -y;returnx;

}

}

packagecn.thread;public class MyRunnable implementsRunnable {private Foo foo = newFoo();public static voidmain(String[] args) {

MyRunnable run= newMyRunnable();

Thread ta= new Thread(run, "Thread-A");

Thread tb= new Thread(run, "Thread-B");

ta.start();

tb.start();

}public voidrun() {for (int i = 0; i < 3; i++) {this.fix(30);try{

Thread.sleep(1);

}catch(InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+ " : 当前foo对象的x值= " +foo.getX());

}

}public int fix(inty) {returnfoo.fix(y);

}

}

运行结果:

Thread-B : 当前foo对象的x值= 40Thread-A : 当前foo对象的x值= 40Thread-B : 当前foo对象的x值= -20Thread-A : 当前foo对象的x值= -20Thread-B : 当前foo对象的x值= -80Thread-A : 当前foo对象的x值= -80

从结果发现,这样的输出值明显是不合理的。原因是两个线程不加控制的访问Foo对象并修改其数据所致。

如果要保持结果的合理性,只需要达到一个目的,就是将对Foo的访问加以限制,每次只能有一个线程在访问。这样就能保证Foo对象中数据的合理性了。

在具体的Java代码中需要完成一下两个操作:

把竞争访问的资源类Foo变量x标识为private;

同步哪些修改变量的代码,使用synchronized关键字同步方法或代码。

packagecn.thread;public classFoo2 {private int x = 100;public intgetX() {returnx;

}//同步方法

public synchronized int fix(inty) {

x= x -y;

System.out.println("线程"+Thread.currentThread().getName() + "运行结束,减少“" +y+ "”,当前值为:" +x);returnx;

}// //同步代码块//public int fix(int y) {//synchronized (this) {//x = x - y;//System.out.println("线程"+Thread.currentThread().getName() + "运行结束,减少“" + y//+ "”,当前值为:" + x);//}//

//return x;//}

}

packagecn.thread;public classMyRunnable2 {public static voidmain(String[] args) {

MyRunnable2 run= newMyRunnable2();

Foo2 foo2=newFoo2();

MyThread t1= run.new MyThread("线程A", foo2, 10);

MyThread t2= run.new MyThread("线程B", foo2, -2);

MyThread t3= run.new MyThread("线程C", foo2, -3);

MyThread t4= run.new MyThread("线程D", foo2, 5);

t1.start();

t2.start();

t3.start();

t4.start();

}class MyThread extendsThread {privateFoo2 foo2;/**当前值*/

private int y = 0;

MyThread(String name, Foo2 foo2,inty) {super(name);this.foo2 =foo2;this.y =y;

}public voidrun() {

foo2.fix(y);

}

}

}

线程线程A运行结束,减少“10”,当前值为:90线程线程C运行结束,减少“-3”,当前值为:93线程线程B运行结束,减少“-2”,当前值为:95线程线程D运行结束,减少“5”,当前值为:90

二、同步和锁定

1、锁的原理

Java中每个对象都有一个内置锁。

当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。

当程序运行到synchronized同步方法或代码块时该对象锁才起作用。

一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。

释放锁是指持锁线程退出了synchronized同步方法或代码块。

关于锁和同步,有一下几个要点:

1)、只能同步方法,而不能同步变量和类;

2)、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步?

3)、不必同步类中所有的方法,类可以同时拥有同步和非同步方法。

4)、如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。

5)、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。

6)、线程睡眠时,它所持的任何锁都不会释放。

7)、线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。

8)、同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。

9)、在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。例如:

public int fix(int y) {

synchronized (this) {

x = x - y;

}

return x;

}

当然,同步方法也可以改写为非同步方法,但功能完全一样的,例如:

public synchronized int getX() {

return x++;

}

public int getX() {

synchronized (this) {

return x++;

}

}

效果是完全一样的。

三、静态方法同步

要同步静态方法,需要一个用于整个类对象的锁,这个对象就是这个类(XXX.class)。

例如:

public static synchronized int setName(String name){

Xxx.name = name;

}

等价于

public static int setName(String name){

synchronized(Xxx.class){

Xxx.name = name;

}

}

四、如果线程不能获得锁会怎么样

如果线程试图进入同步方法,而其锁已经被占用,则线程在该对象上被阻塞。实质上,线程进入该对象的的一种池中,必须在哪里等待,直到其锁被释放,该线程再次变为可运行或运行为止。

当考虑阻塞时,一定要注意哪个对象正被用于锁定:

1、调用同一个对象中非静态同步方法的线程将彼此阻塞。如果是不同对象,则每个线程有自己的对象的锁,线程间彼此互不干预。

2、调用同一个类中的静态同步方法的线程将彼此阻塞,它们都是锁定在相同的Class对象上。

3、静态同步方法和非静态同步方法将永远不会彼此阻塞,因为静态方法锁定在Class对象上,非静态方法锁定在该类的对象上。

4、对于同步代码块,要看清楚什么对象已经用于锁定(synchronized后面括号的内容)。在同一个对象上进行同步的线程将彼此阻塞,在不同对象上锁定的线程将永远不会彼此阻塞。

五、何时需要同步

在多个线程同时访问互斥(可交换)数据时,应该同步以保护数据,确保两个线程不会同时修改更改它。

对于非静态字段中可更改的数据,通常使用非静态方法访问。

对于静态字段中可更改的数据,通常使用静态方法访问。

如果需要在非静态方法中使用静态字段,或者在静态字段中调用非静态方法,问题将变得非常复杂。已经超出SJCP考试范围了。

六、线程安全类

当一个类已经很好的同步以保护它的数据时,这个类就称为“线程安全的”。

即使是线程安全类,也应该特别小心,因为操作的线程是间仍然不一定安全。

七、线程同步小结

1、线程同步的目的是为了保护多个线程访问一个资源时对资源的破坏。

2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。

3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。

4、对于同步,要时刻清醒在哪个对象上同步,这是关键。

5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。

6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。

7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
同步Java线程中用于保护共享资源,以确保同一时间只有一个线程可以访问该资源,从而避免数据竞争和并发问题。在Java中,可以使用synchronized关键字来实现同步的机制。 对于非静态的同步方法,可以是this对象或其他对象,要求是同一个对象。例如,使用关键字synchronized修饰的sell()方法,就在this对象上。 对于静态的同步方法,是当前类本身。因为静态方法可以在没有实例化对象的情况下使用,所以只能使用类来作为。可以使用synchronized修饰的静态方法m1()和m2()是示例。 除了直接在方法上使用synchronized关键字,还可以使用同步代码块来实现的机制。同步代码块的对象可以是this对象或其他对象。 当一个线程持有时,其他线程将无法获得该,它们将被阻塞,直到持有线程释放的释放可以通过以下方式实现: 1. 当前线程的同步方法或同步代码块执行结束。 2. 当前线程在同步代码块或同步方法中遇到break或return语句。 3. 当前线程在同步代码块或同步方法中出现未处理的Error或Exception,导致异常结束。 4. 当前线程在同步代码块或同步方法中执行了线程对象的wait()方法,暂停当前线程,并释放。 需要注意的是,线程执行同步代码块或同步方法时,调用Thread.sleep()或Thread.yield()方法暂停当前线程的执行不会释放。此外,使用suspend()方法将线程挂起也不会释放

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值