- synchronized是什么
Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码. - synchronized解决什么问题
在多线程环境中,当两个或多个线程尝试更新共享数据时,会发生竞争条件(race condition)。
java提供一种机制来避免竞争条件的发生,就是synchronized关键字。
举例:
共享变量count,每个线程run方法count++ 10000次,10个线程操作共享变量的结果就应该是10 * 10000
实际运行结果不是10 * 10000,小于10 * 10000而且每次运行结果都不同。
原因分析:发生了竞争条件;假设当前count值为1000,线程1执行count++,线程2执行count++本应该是1000 +1 +1,但是实际上两个线程并发执行后,最终结果只是1001
//多线程的实现方式之一,实现Runnable接口并复写run方法
public class SynchronizedDemo implements Runnable{
//共享变量
private static int count = 0;
public static void main(String[] args){
//开启10个线程
for(int i=0;i<10;i++){
Thread t = new Thread(new SynchronizedDemo());
t.start();
}
try {
Thread.sleep(5000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(count);
}
@Override
public void run(){
for(int i=0;i<10000;i++){
count++;
}
}
}
- synchronized是如何使用的
在java中synchronized可以用在代码块和方法中,根据使用的位置可以有如下使用场景:
举例1->静态方法
public class SynchronizedDemo1 implements Runnable{
private static int count = 0;
public static void main(String[] args){
for(int i=0;i<10;i++){
//不同对象,加在静态方法上
Thread t = new Thread(new SynchronizedDemo1());
t.start();
}
try {
Thread.sleep(5000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(count);
}
@Override
public void run() {
countIncrement();
}
public synchronized static void countIncrement(){
for(int i =0;i < 10000;i++){
count++;
}
}
}
举例2->实例方法
public class SynchronizedDemo2 implements Runnable{
private static int count = 0;
public static void main(String[] args){
SynchronizedDemo2 demo2 = new SynchronizedDemo2();
for(int i=0;i<10;i++){
//相同对象,加在对象实例方法上
Thread t = new Thread(demo2);
t.start();
}
try {
Thread.sleep(5000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(count);
}
@Override
public void run() {
countIncrement();
}
public synchronized void countIncrement(){
for(int i =0;i < 10000;i++){
count++;
}
}
}
举例3->代码块的实例对象
public class SynchronizedDemo3 implements Runnable{
private static int count = 0;
public static void main(String[] args){
SynchronizedDemo3 demo3 = new SynchronizedDemo3();
for(int i=0;i<10;i++){
//相同对象,加在代码块锁this对象
Thread t = new Thread(demo3);
t.start();
}
try {
Thread.sleep(5000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(count);
}
@Override
public void run() {
synchronized (this){
for(int i =0;i < 10000;i++){
count++;
}
}
}
}
举例4->代码块的class对象
public class SynchronizedDemo implements Runnable{
private static int count = 0;
public static void main(String[] args){
for(int i=0;i<10;i++){
//不同对象,加在代码块锁当前class对象
Thread t = new Thread(new SynchronizedDemo());
t.start();
}
try {
Thread.sleep(5000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(count);
}
@Override
public void run() {
synchronized (SynchronizedDemo.class){
for(int i=0;i<10000;i++){
count++;
}
}
}
}
举例5->任意实例对象Object
public class SynchronizedDemo4 implements Runnable{
private static int count = 0;
//任意实例对象
private String lock = "lock";
public static void main(String[] args){
SynchronizedDemo4 demo4 = new SynchronizedDemo4();
for(int i=0;i<10;i++){
Thread t = new Thread(demo4);
t.start();
}
try {
Thread.sleep(5000);
}catch(Exception e){
e.printStackTrace();
}
System.out.println(count);
}
@Override
public void run() {
synchronized (lock){
for(int i =0;i < 10000;i++){
count++;
}
}
}
}
4. synchronized有什么缺点
缺点:
①获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能等待,严重影响程序执行效率。
②场景:当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和读操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。采用synchronized 关键字来实现同步的话,就会导致一个问题:如果多个线程都只是进行读操作,当一个线程在进行读操作时,其他线程只能等待无法进行读操作,这是不合理且非常影响效率。
解决办法:使用Lock在下一章节介绍
参考列表:
1、什么是race condition