java 两种锁的区别,Java多线程的两种实现方式的区别以及深刻同步有关问题中的锁对象...

Java多线程的两种实现方式的区别以及深刻同步问题中的锁对象

首先我们知道创建线程有两种方式:

1.继承Thread类;2.实现Runnable接口。

但是这两这并非完全一样的。下面谈谈区别:

因为Java并不支持多继承的(接口是可以多继承接口的。不过一般我们不提),但支持多实现。当一个类继承了父类,就不能再继承Thread类,只能通过实现接口的形式创建线程。

继承Runnable接口更加符合面向对象的思想。线程分为两部分,一是线程对象,二是线程任务。继承Thread类,线程对象和线程任务(run方法内的代码)耦合在一起。一旦创建了Thread类的子类对象,既是线程对象又是线程任务。而实现Runnable接口是将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。线程任务由传递的参数决定。Runable接口将线程对象和线程任务解耦。

第三个区别就是写同步函数的时候这两种方式有明显的差异

综上我们一般采用实现接口的形式来创建线程。

下面我们来说关于同步锁的问题。

Java的关于同步的问题有两种写法,一是代码块写同步,而是写同步函数。二者确是有区别的。

同步函数使用的锁是固定的this(即实现Runnable接口的类的对象),当线程任务只需要一个同步时,完全可以由同步函数来体现;

同步代码块的锁使用的是任意对象,当线程任务需要多同步时,必须用锁来区分。

下面给出详细的解释:首先看一个死锁的示例程序:

/*

* 思路:死锁,嵌套同步时最简单的。

* 两个锁对象进行嵌套。两个线程执行同样的两个锁对象嵌套

*/

class MyLock {

public static final Object LOCKA = new Object();

public static final Object LOCKB = new Object();

}

class Test implements Runnable {

private boolean flag;

Test(boolean flag) {

this.flag = flag;

}

public void run() {

if (flag) {

while (true) {

synchronized (MyLock.LOCKA) {

System.out.println(Thread.currentThread().getName()

+ "...if.....LOCKA");

synchronized (MyLock.LOCKB) {

System.out.println(Thread.currentThread().getName()

+ "...if.....LOCKB");

}

}

}

} else {

while (true) {

synchronized (MyLock.LOCKB) {

System.out.println(Thread.currentThread().getName()

+ "...else.....LOCKB");

synchronized (MyLock.LOCKA) {

System.out.println(Thread.currentThread().getName()

+ "...else.....LOCKA");

}

}

}

}

}

}

public class DeadLock {

public static void main(String[] args) {

Test test1 = new Test(true);

Test test2 = new Test(false);

Thread t1 = new Thread(test1);

Thread t2 = new Thread(test2);

t1.start();

t2.start();

}

}

其中MyLockA和MyLockB指代的是不同的Object对象,当然也可以是其他的类的对象,因为此处的对象锁可以是任意对象

但若写的是同步函数的形式,则锁对象是固定的this,我们还知道this永远都是指向当前对象的(也就是谁调用这个函数,this就指代的谁。)所以当采用继承Thread类来实现多线程时并不能实现同步的效果,因为不同的线程是不同的Thread子类对象,this指代的也不一样,从而锁也不一样。下面给出程序证明观点:

class Bank {

private int sum;

public void add (int num) {

sum += num;

System.out.println(Thread.currentThread().getName()+"...sum=" + sum);

}

}

class Customer extends Thread{

private Bank b = new Bank();

@Override

public void run() {

this.storage();

}

public synchronized void storage () {

for (int i = 0; i < 5; i++) {

if(i == 3)

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

b.add(100);

}

}

}

public class BankAndCustomer {

public static void main(String[] args) {

Customer c1 = new Customer();

Customer c2 = new Customer();

c1.start();

c2.start();

}

}

以上代码的输出结果是(当然多线程的输出结果不唯一):

024022379.png

其中sleep方法时不是放锁对象的,但当其中一个线程休眠之后其他线程立即得到了cpu执行权,来执行storage方法,这足以证明上述观点。

但用通过实现Runnable接口的形式来实现多线程则上述问题可以避免,因为实现线程时我们传递的是Runnable接口的子类对象,两个线程都是子类对象调用的run方法(建议查看API中的Thread类的run方法,一探究竟。),故this所指代的对象是一致的,故当一个线程输出三次循环后,休眠两秒并未被打断,而是苏醒后继续执行。下面给出代码:

class Bank {

private int sum;

public void add (int num) {

sum += num;

System.out.println(Thread.currentThread().getName()+"...sum=" + sum);

}

}

class Customer implements Runnable{

private Bank b = new Bank();

@Override

public void run() {

this.storage();

}

public synchronized void storage () {

for (int i = 0; i < 5; i++) {

if(i == 3)

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

b.add(100);

}

}

}

public class BankAndCustomer {

public static void main(String[] args) {

Customer c = new Customer();

Thread t1 = new Thread(c);

Thread t2 = new Thread(c);

t1.start();

t2.start();

}

}

024022380.png

同时我们需要说明的是,当同步函数被标注为静态函数是他的默认锁对象就不是this了,因为静态中是没有this的。由类名直接调用,而不需要对象。锁对象变为类名.class,上例中若声明为public static synchronized void storage ()锁对象就是Customer.class

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值