【Java】Synchronized
简介
synchronized
是Java中的一个关键字,也就是说是Java语言内置的特性。
如果一个代码块被synchronized
修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:
- 获取锁的线程执行完了该代码块,然后线程释放对锁的占有。
- 线程执行发生异常,此时JVM会让线程自动释放锁。
语法
修饰代码块
synchronized(需要一个任意的对象(锁)){
代码块中放操作共享数据的代码。
}
修饰方法
synchronized
method(){
...
}
代码示例
public class MySynchronized {
private String name;
public MySynchronized() {
}
public MySynchronized(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
MySynchronized mySynchronized = new MySynchronized("a");
MySynchronized mySynchronized2 = new MySynchronized("b");
// 创建thread1线程
new Thread("thread1") {
@Override
public void run() {
System.out.println(this.getName() + "线程的run方法被调用……");
synchronized (mySynchronized) {
try {
System.out.println("锁名称为:" + mySynchronized.getName());
System.out.println(this.getName() + " start");
Thread.sleep(5000);
System.out.println(this.getName() + "醒了");
System.out.println(this.getName() + " end");
// 如果发生异常,JVM会自动将锁释放
int i = 1 / 0;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
// 创建thread2线程
new Thread("thread2") {
@Override
public void run() {
System.out.println(this.getName() + "线程的run方法被调用……");
// 如果不是同一把锁,线程2不用等待直接执行
//synchronized (mySynchronized2) {
// 争抢同一把锁时,线程1没释放之前,线程2只能等待
synchronized (mySynchronized) {
System.out.println("锁名称为:" + mySynchronized.getName());
System.out.println(this.getName() + " start");
System.out.println(this.getName() + " end");
}
}
}.start();
}
}
具体示例
火车售票系统,客票一共有100张,分三个窗口来卖。
package org.djflying.bigdata.java.thread;
public class TicketWindow implements Runnable {
private int tickets;
public TicketWindow() {
}
public TicketWindow(int tickets) {
this.tickets = tickets;
}
public int getTickets() {
return tickets;
}
public void setTickets(int tickets) {
this.tickets = tickets;
}
/**
* 销售一张客票
*/
public void sellOne() {
this.tickets--;
}
@Override
public void run() {
while (true) {
synchronized (this) {
if (this.getTickets() > 0) {
System.out.println("还剩余票:" + this.getTickets() + "张");
this.sellOne();
System.out.println(Thread.currentThread().getName() + "卖出一张火车票,还剩" + this.getTickets() + "张");
} else {
System.out.println("余票不足,暂停出售!");
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
TicketWindow ticketWindow = new TicketWindow(100);
Thread thread1 = new Thread(ticketWindow, "一号窗口");
Thread thread2 = new Thread(ticketWindow, "二号窗口");
Thread thread3 = new Thread(ticketWindow, "三号窗口");
thread1.start();
thread2.start();
thread3.start();
}
}
缺陷
- 如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。
因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock就可以办到。 - 当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。
如果采用synchronized关键字来实现同步的话,就会导致一个问题:如果多个线程都只是进行读操作,当一个线程在进行读操作时,其他线程只能等待无法进行读操作。
因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。
另外,通过Lock可以知道线程有没有成功获取到锁。这个是synchronized无法办到的。
总的来说,也就是说Lock提供了比synchronized更多的功能。
评论区