Java线程(二) 线程同步

目录

synchronized基础概念

为什么要用到线程同步

synchronized的3中实现方式:

方式1:synchronized代码块

方式2:synchronized修饰方法

方式3:synchronized修饰类


synchronized基础概念

  • 多个线程步调一致的执行;
  • 让多个线程争夺同一个对象的同步锁,任何对象都有唯一同步锁,只要创建一个对象,就会自有一个同步锁,注意,这里说的是对象,定义出来的基本类型是没有线程锁的;
  • 多个线程对同一个对象操作,谁抢到锁谁执行,抢不到就等待,这样就保证没有脏数据;

为什么要用到线程同步

先来看一个例子:比如我现在有一个1-10个数字的整型集合,我现在对集合中2对倍数的数字进行加2,反复操作1000次,在另一个集合中进行数组打印:

public class SynchornizedTest {
	
	static int nums=1000;
	static int nums2=10;
	static ArrayList<Integer>list=new ArrayList<>();
	
	public static void main(String[] args) {
		
		//集合中有1到10个数字
		for(int i=1;i<=10;i++) {
			list.add(i);
		}
		
		SynchornizedThread t1=new SynchornizedThread();
		t1.start();
		
		SynchornizedThread2 t2=new SynchornizedThread2();
		t2.start();
	}
	
	//该线程对是2的倍数的数据进行反复的加2,操作1000次
	static class SynchornizedThread extends Thread{
		@Override
		public void run() {
				while(nums>0) {
					for(int i=0;i<list.size();i++) {
						if(list.get(i)%2==0) {
							list.set(i, list.get(i)+2);
						}
					}
					nums--;
				}
		}
	}
	
	//该线程取数据,打印10次
	static class SynchornizedThread2 extends Thread{
		@Override
		public void run() {
				while(nums2>0) {
				System.out.println(list);
				nums2--;
				}
		}
	}
}

打印结果:很明显每次打印结果都是不同的,这里面就有脏数据,因为输出的话,应该输出线程1中操作完成的数据


synchronized的3中实现方式:

synchronized代码块方式2 synchronized修饰方法方式3 synchronized静态方法--属于类

synchronized(要操作的对象){

 

}

  • 争夺指定对象的锁

synchronized void f(){

 

}

  • 争夺当前对象(this)的锁

static synchronized void f(){

 

}

  • 相当于抢当前类的锁

方式1:synchronized代码块

测试:争夺指定对象list集合的锁

举例:对上面对例子进行线程同步

public class SynchornizedTest {
	
	static int nums=1000;
	static int nums2=10;
	static ArrayList<Integer>list=new ArrayList<>();
	
	public static void main(String[] args) {
		
		//集合中有1到10个数字
		for(int i=1;i<=10;i++) {
			list.add(i);
		}
		
		SynchornizedThread t1=new SynchornizedThread();
		t1.start();
		
		SynchornizedThread2 t2=new SynchornizedThread2();
		t2.start();
	}
	
	//该线程对是2的倍数的数据进行反复的加2,操作1000次
	static class SynchornizedThread extends Thread{
		@Override
		public void run() {
			synchronized (list) {
				while(nums>0) {
					for(int i=0;i<list.size();i++) {
						if(list.get(i)%2==0) {
							list.set(i, list.get(i)+2);
						}
					}
					nums--;
				}
			}
		}
	}
	
	//该线程取数据,打印10次
	static class SynchornizedThread2 extends Thread{
		@Override
		public void run() {
			synchronized (list) {
				while(nums2>0) {
				System.out.println(list);
				nums2--;
				}
			}
		}
	}
}

打印结果:


方式2:synchronized修饰方法

测试:争夺当前对象(this)的锁,由对象r1调用add和get两个方法,按序执行

举例:比如我现在循环中打印一个数字是否是奇数:

public class SynchornizedTest2 {

	public static void main(String[] args) {
		R1 r1=new R1();
		Thread t1=new Thread(r1);
		t1.start();
		
		//死循环检查是不是奇数
		while(true) {
			int i=r1.get();
			if(i%2==1) {
				System.out.println("出现奇数:"+i);
				System.exit(0);//关闭虚拟机
			}
		}
	}
	
	static class R1 implements Runnable{
		
		static int i;
		
		public  void add() {
			i++;
			i++;
		}
		
		public  int get() {
			return i;
		}
		
		@Override
		public void run() {
			 while(true) {
				 add();
			 }
		}
	}
}

打印结果:按照上面的逻辑,其实不应该打印出奇数,因为i++应该执行两次,现在打印出来了,说明i++执行了一次就执行get方法了,这是main线程和t1线程冲突导致

  

通过给方法加锁,让t1线程拿到对应方法的锁执行完之后,再拿到另一个方法的锁,再继续执行

public class SynchornizedTest2 {

	public static void main(String[] args) {
		R1 r1=new R1();
		Thread t1=new Thread(r1);
		t1.start();
		
		//死循环检查是不是奇数
		while(true) {
			int i=r1.get();
			if(i%2==0) {
				System.out.println("出现偶数:"+i);
				System.exit(0);//关闭虚拟机
			}
		}
	}
	
	static class R1 implements Runnable{
		
		static int i;
		
		public synchronized void add() {
			i++;
			i++;
		}
		
		public synchronized int get() {
			return i;
		}
		
		@Override
		public void run() {
			 while(true) {
				 add();
			 }
		}
	}
}

这时再打印数据:


方式3:synchronized修饰类

测试:相当于抢当前类的锁

举例:对上面对例子进行思考,上面是只要一个线程t1,假如我现在有2个线程,在main线程里用r2进行get,看下执行结果:

public class SynchornizedTest2 {

	public static void main(String[] args) {
		R r1=new R();
		Thread t1=new Thread(r1);
		t1.start();
		
		R r2=new R();
		
		
		//死循环检查是不是奇数
		while(true) {
			int i=r2.get();
			if(i%2==1) {
				System.out.println("出现奇数:"+i);
				System.exit(0);//关闭虚拟机
			}
		}
	}
	
	static class R implements Runnable{
		
		static int i;
		
		public synchronized void add() {
			i++;
			i++;
		}
		
		public synchronized int get() {
			return i;
		}
		
		@Override
		public void run() {
			 while(true) {
				 add();
			 }
		}
	}
}

输出:依然出现里奇数,为什么呢?因为此时虽然有synchronized来对方法进行修饰,但是此时已经不是一个对象进行操作,方法2只是争夺当前对象(this)的锁;

此时对代码进行处理:

public class SynchornizedTest2 {

	public static void main(String[] args) {
		R r1=new R();
		Thread t1=new Thread(r1);
		t1.start();
		
		R r2=new R();
		
		
		//死循环检查是不是奇数
		while(true) {
			int i=r2.get();
			if(i%2==0) {
				System.out.println("出现偶数:"+i);
				System.exit(0);//关闭虚拟机
			}
		}
	}
	
	static class R implements Runnable{
		
		static int i;
		
		public static synchronized void add() {
			i++;
			i++;
		}
		
		public static synchronized int get() {
			return i;
		}
		
		@Override
		public void run() {
			 while(true) {
				 add();
			 }
		}
	}
}

输出:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小苏的小小苏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值