JAVA之synchronized认识

本文深入探讨了Java中的synchronized关键字在多线程环境下的使用方法,包括同步方法、同步语句块、实例变量、对象引用、静态方法和类字面常量的应用。通过具体例子展示了如何正确使用synchronized来实现线程同步,避免了不可预期的并发问题,并详细解释了不同使用场景下的锁机制和同步规则。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

         看了传智张孝详老师关于多线程视频中的synchronized的介绍后,同时上网搜素了些资料看。本文结合张老师讲的及网上的一些资料,并且结合自己的一些认识对synchronized的使用做个简单总结,以备以后复习。

 

       JAVA中synchronized关键字能够作为方法的修饰符,也可作为方法内的语句,也就是平时说的同步方法和同步语句块。假如再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static方法和class literals(类名称字面常量)身上。

 

      A.无论synchronized关键字加在方法上还是对象上,他取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。
      B.每个对象只有一个锁(lock)和之相关联。
      C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

 

        一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

  二、当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

  三、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的除synchronized(this)同步代码块以外的部分。

  四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

  五、以上规则对其它对象锁同样适用

 

      下面来看个张老师写的例子

package thread;

public class SynchronizedTest {

	public static void main(String[] args) {
		final Output output=new Output();
		//启动两个线程,都是用output对象中的output方法进行打印
		new Thread(new Runnable() {
			@Override
			public void run() {
				output.output("weijianfei");
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				output.output("dreambroken");
			}
		}).start();
	}
}
	

 class Output{
	public  void output(String s){
		for(int i=0;i<s.length();i++){
			try {
				Thread.sleep(100);//模拟处理一个字符需要100毫秒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.print(s.charAt(i));
		}
		System.out.println();
	}

}


运行结果:

wderiejaimabnrfoeki
en

        这结果不是我们期待的。这种不可预期的结果是因为某个时间点CPU只能运行一个线程,并且并不是一直把该线程执行完后才去执行其他线程,而是这个线程执行一个时间段后,不管该线程是否结束了,CPU都会去执行其他线程。

           为了解决这中问题,锁就出来了,每个对象都有一把锁,谁(线程)得到了锁,谁就执行,执行完后释放锁,其他线程再去抢锁。(回头看上面红字部分的第一点).

       代码改善后,如下.

package thread;

public class SynchronizedTest {

	public static void main(String[] args) {
		final Output output=new Output();
		//启动两个线程,都是用output对象中的output方法进行打印
		new Thread(new Runnable() {
			@Override
			public void run() {
				output.output("weijianfei");
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				output.output("dreambroken");
			}
		}).start();
	}
}
	

 class Output{
	public synchronized  void output(String s){
		for(int i=0;i<s.length();i++){
			try {
				Thread.sleep(100);//模拟处理一个字符需要100毫秒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.print(s.charAt(i));
		}
		System.out.println();
	}

}


运行结果

dreambroken
weijianfei

      是我们期待的结果。

     我们知道synchronized还可以修饰代码块,所以上面的public void output(String s)方法,可以改成这样.

public   void output(String s){
		synchronized (this) {
			for (int i = 0; i < s.length(); i++) {
				try {
					Thread.sleep(100);// 模拟处理一个字符需要100毫秒
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.print(s.charAt(i));
			}
			System.out.println();
		}
	}

        当使用synchronized修饰代码块时,一定要保证多个线程抢的是同一个对象的锁。synchronized(this)抢的是当前对象的锁,对于上面程序,抢的是output对象的锁(final Output output=new Output()这里的output)。如果每个线程不是竞争同一把锁,那大家都拿到锁,大家都执行,那就没意义了,比如代码修改如下。

public   void output(String s){
		synchronized (s) {
			for (int i = 0; i < s.length(); i++) {
				try {
					Thread.sleep(100);// 模拟处理一个字符需要100毫秒
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.print(s.charAt(i));
			}
			System.out.println();
		}
	}


       这样一来,上面的两个线程,一个拿到的"weijianfei"对象的锁,一个拿到的是"dreambroken"的锁,大家都拿到锁,所以他们都可以执行,那结果又是不可预期的了。所以要注意保证多个线程竞争的是同一个对象的锁.

 

     上面红字部分说道,同一个对象内的多个方法用synchronized修饰时,如果某个线程在执行其中一个synchronized修饰的方法了,那么其他线程就无法访问任何synchronized修饰的方法(非synchronized修饰的方法可以).

package thread;

public class SynchronizedTest {

	public static void main(String[] args) {
		final Output output=new Output();
		//启动两个线程,都是用output对象中的output方法进行打印
		new Thread(new Runnable() {
			@Override
			public void run() {
				output.output("weijianfei");
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				output.output2("dreambroken");
			}
		}).start();
	}
}
	

 class Output{
	public synchronized void output(String s) {
		for (int i = 0; i < s.length(); i++) {
			try {
				Thread.sleep(100);// 模拟处理一个字符需要100毫秒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.print(s.charAt(i));
		}
		System.out.println();
	}
	public synchronized void output2(String s) {
		for (int i = 0; i < s.length(); i++) {
			try {
				Thread.sleep(100);// 模拟处理一个字符需要100毫秒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.print(s.charAt(i));
		}
		System.out.println();
	}
}

运行结果
weijianfei
dreambroken

      仍然是我们期待的结果。

    但是,需要注意的是,如果某个方法有static修饰时,那就有需要注意的地方了。比如public synchronized void output(String s)和public static synchronized void output2(String s)竞争的并不是同一个对象的锁.

package thread;

public class SynchronizedTest {

	public static void main(String[] args) {
		final Output output=new Output();
		//启动两个线程,都是用output对象中的output方法进行打印
		new Thread(new Runnable() {
			@Override
			public void run() {
				output.output("weijianfei");
			}
		}).start();

		new Thread(new Runnable() {
			@Override
			public void run() {
				output.output2("dreambroken");
			}
		}).start();
	}
}
	

 class Output{
	public synchronized void output(String s) {
		for (int i = 0; i < s.length(); i++) {
			try {
				Thread.sleep(100);// 模拟处理一个字符需要100毫秒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.print(s.charAt(i));
		}
		System.out.println();
	}
	public static synchronized void output2(String s) {
		for (int i = 0; i < s.length(); i++) {
			try {
				Thread.sleep(100);// 模拟处理一个字符需要100毫秒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.print(s.charAt(i));
		}
		System.out.println();
	}
}


运行结果

dwreiejaimabnrfoeki
en

        这是因为public synchronized void output(String s)对应的锁是this,当前对象output(final Output output=new Output()),而public static synchronized void output2(String s)对应的是Class(Output.class)对象,前者对象位于堆中,后者对象位于方法区中.

 

       synchronized还可以修饰变量,为了让多个线程竞争同一个对象的锁,所以变量应该是全局变量.

class Output{
	 String lock="";

	public void output(String s) {
		synchronized (lock) {
			for (int i = 0; i < s.length(); i++) {
				try {
					Thread.sleep(100);// 模拟处理一个字符需要100毫秒
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.print(s.charAt(i));
			}
			System.out.println();
		}
	}
}


 

      总结:必须保证每个线程竞争资源时,把资源进行加锁时,每个线程抢的是同一把锁。

 
 
 
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值