为何要实现同步
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关键字来修饰,被修饰的方法为同步方法,它能实现和同步代码块同样的功能。
具体语法格式:
synchronized 返回值类型 方法名([参数1,*****]){}
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()修饰为同步方法,实现了和同步代码块一样的效果。