为何要实现同步
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查), 将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。
同步代码块
即有synchronized关键字修饰的语句块。
被该关键字修饰的语句块会自动被加上内置锁,从而实现同步
代码如:
synchronized(lock){
操作共享资源代码块
}
上面的代码中,lock是一个锁对象,它是同步代码块的关键,保证用于处理共享资源的代码在任何时刻只能有一个线程访问
注:同步是一种高开销的操作,因此应该尽量减少同步的内容。
通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
1 @SpringBootTest 2 //Ticket,实现Runnable接口 3 class Ticket implements Runnable{ 4 //定义变量tickets,并赋值10 5 private int tickets = 10; 6 //定义任意一个对象,用作同步代码块的锁 7 Object lock = new Object(); 8 9 //实现接口中的run()方法 10 @Override 11 public void run() { 12 while (true){ 13 synchronized (lock){ 14 try { 15 Thread.sleep(10); 16 }catch (InterruptedException e){ 17 e.printStackTrace(); 18 } 19 if (tickets > 0){ 20 System.out.println(Thread.currentThread().getName() + "---卖出的票" + tickets--); 21 }else { 22 //如果tickets 小于0 ,跳出循环 23 break; 24 } 25 } 26 } 27 } 28 } 29 public class Example { 30 public static void main(String[] args) { 31 //创建Ticket对象 32 Ticket ticket = new Ticket(); 33 //开启四个线程 34 new Thread(ticket,"线程一").start(); 35 new Thread(ticket,"线程二").start(); 36 new Thread(ticket,"线程三").start(); 37 new Thread(ticket,"线程四").start(); 38 } 39 }
运行结果:
将有关tickets变量的操作全部都放到同步代码块中,为了保证线程的持续执行,将同步代码块放在死循环中,直到tickets<0时跳出循环,持续在获得锁对象时有一定的随机性,在整个程序的运行期间,线程二和线程三始终未获得锁对象。
同步方法
在方法面前同样可以使用synchronized关键字来修饰,被修饰的方法为同步方法,它能实现和同步代码块同样的功能。
具体语法格式:
1 @SpringBootTest 2 //Ticket,实现Runnable接口 3 class Ticket implements Runnable{ 4 //定义变量tickets,并赋值10 5 private int tickets = 10; 6 7 //实现接口中的run()方法 8 public void run() { 9 while (true){ 10 saleTicket(); //调用售票方法 11 if (tickets <=0){ 12 break; 13 } 14 } 15 16 } 17 //定义一个同步方法saleTicket 18 private synchronized void saleTicket(){ 19 if (tickets > 0){ 20 try { 21 Thread.sleep(100); 22 }catch (InterruptedException e){ 23 e.printStackTrace(); 24 } 25 System.out.println(Thread.currentThread().getName() + "卖出的票 --" + tickets --); 26 } 27 } 28 } 29 public class Example { 30 public static void main(String[] args) { 31 //创建Ticket对象 32 Ticket ticket = new Ticket(); 33 //开启四个线程 34 new Thread(ticket,"线程一").start(); 35 new Thread(ticket,"线程二").start(); 36 new Thread(ticket,"线程三").start(); 37 new Thread(ticket,"线程四").start(); 38 } 39 }
运行结果:
将售票代码抽取为售票方法saleTicket(),并用synchronized关键字把saleTicket()修饰为同步方法,实现了和同步代码块一样的效果。