Fighting_初心的博客

不积跬步无以至千里,不积小流无以成江海!

Java多线程编程(二)——synchronized详解

    对于多个线程同时访问同一个变量(即共享数据的情况),例如实现投票功能的软件时,多个线程可以同时处理同一个人的票数。那么一定会出现非线程安全的问题。

    所谓“非线程安全”,主要是指多个线程对同一个对象中的同一个实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序的执行流程。

    通常时我们会采取synchronized来解决。synchronized可以在任意对象及方法上加锁,而加锁的这段代码被称为“互斥区”或“临界区”。

synchronized加锁原理:

    当一个线程想要执行同步方法里的代码时,线程首先尝试去拿这把锁,如果能够拿到这把锁,那么这个线程可以执行synchronized里面的代码。如果不能拿到这把锁,那么线程就会不断的尝试拿这把锁,直到能够拿到为止,而且是有多个线程同时去争抢这把锁。

    

    synchronized主要由以下几种形式:synchronized声明、synchronized(this)代码块、synchronized(非this)代码块、synchronized静态方法、synchronized(Class)代码块,几种形式用法各不相同,实现的功能上也有细微差别。

一、synchronized声明

   synchronized最原始用法,使用非常简单,只需在需要同步的方法前添加synchronized关键字即可。但是synchronized取得的锁是对象锁,哪个线程先执行带synchronized关键字的方法,哪个线程就持有该方法所属对象的锁Lock,那么其他线程只能呈等待状态。

    前提是多个线程访问的是同一个对象。如果多个线程访问多个对象,则JVM会创建多个锁。

    例如:

public class HasSelfPrivateNum {
	private int num = 0;
	synchronized public void addI(String username) {
		try {
			if(username.equals("a")) {
				num = 100;
				System.out.println("a set over!");
				Thread.sleep(2000);
			}else {
				num = 200;
				System.out.println("b set over!");
			}
			System.out.println(username+" num="+num);
		}catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
}
public class ThreadA extends Thread{
	private HasSelfPrivateNum numRef;
	public ThreadA(HasSelfPrivateNum numRef) {
		this.numRef = numRef;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		numRef.addI("a");
	}
}

ThreadB同ThreadA

同一把锁:

public class Run {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		HasSelfPrivateNum numRef = new HasSelfPrivateNum();
		ThreadA aThreadA = new ThreadA(numRef);
		aThreadA.start();
		ThreadB bThreadB = new ThreadB(numRef);
		bThreadB.start();
	}

}
运行结果:
a set over!
a num=100
b set over!

b num=200

非同一把锁:

public class Run {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		HasSelfPrivateNum numRef1 = new HasSelfPrivateNum();
		HasSelfPrivateNum numRef2 = new HasSelfPrivateNum();
		ThreadA aThreadA = new ThreadA(numRef1);
		aThreadA.start();
		ThreadB bThreadB = new ThreadB(numRef2);
		bThreadB.start();
	}

}

运行结果:

a set over!
b set over!
b num=200

a num=100

    synchronized方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个长时间的任务,那么B线程必须等待比较长时间。在这种情况下可以使用synchronized同步语句块来解决。synchronized方法是对当前对象进行加锁,而synchronized是对某一个对象进行加锁。

二、synchronized(this)代码块

    相较于synchronized声明更加灵活,可以只对方法中的部分代码块进行加锁。对于加锁部分同步,而非加锁部分仍然异步。

public class Task {
	public void doLongTimeTask() {
		//对于非synchronized代码块异步
		for(int i = 0; i<10; i++) {
			System.out.println("nosynchronized threadName = "
					+Thread.currentThread().getName()+" i = "+(i+1));
		}
		System.out.println();
		//对于synchronized代码块同步
		synchronized (this) {
			for(int j = 0; j<10; j++) {
				System.out.println("synchronized threadName = "
					+Thread.currentThread().getName()+" j = "+(j+1));
			}
		}
	}
}

public class MyThread1 extends Thread{
	private Task task;
	public MyThread1(Task task) {
		// TODO Auto-generated constructor stub
		this.task = task;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		task.doLongTimeTask();
	}
}

public class MyThread2 extends Thread {
	private Task task;
	public MyThread2(Task task) {
		// TODO Auto-generated constructor stub
		this.task = task;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		super.run();
		task.doLongTimeTask();
	}
}

public class Run {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Task task = new Task();
		MyThread1 thread1 = new MyThread1(task);
		thread1.start();
		MyThread2 thread2 = new MyThread2(task);
		thread2.start();
	}

}

运行结果:

nosynchronized threadName = Thread-0 i = 1
nosynchronized threadName = Thread-0 i = 2
nosynchronized threadName = Thread-0 i = 3
nosynchronized threadName = Thread-0 i = 4
nosynchronized threadName = Thread-0 i = 5
nosynchronized threadName = Thread-0 i = 6
nosynchronized threadName = Thread-0 i = 7
nosynchronized threadName = Thread-0 i = 8
nosynchronized threadName = Thread-0 i = 9
nosynchronized threadName = Thread-0 i = 10


synchronized threadName = Thread-0 j = 1
synchronized threadName = Thread-0 j = 2
synchronized threadName = Thread-0 j = 3
synchronized threadName = Thread-0 j = 4
synchronized threadName = Thread-0 j = 5
synchronized threadName = Thread-0 j = 6
synchronized threadName = Thread-0 j = 7
synchronized threadName = Thread-0 j = 8
synchronized threadName = Thread-0 j = 9
synchronized threadName = Thread-0 j = 10
nosynchronized threadName = Thread-1 i = 1
nosynchronized threadName = Thread-1 i = 2
nosynchronized threadName = Thread-1 i = 3
nosynchronized threadName = Thread-1 i = 4
nosynchronized threadName = Thread-1 i = 5
nosynchronized threadName = Thread-1 i = 6
nosynchronized threadName = Thread-1 i = 7
nosynchronized threadName = Thread-1 i = 8
nosynchronized threadName = Thread-1 i = 9
nosynchronized threadName = Thread-1 i = 10


synchronized threadName = Thread-1 j = 1
synchronized threadName = Thread-1 j = 2
synchronized threadName = Thread-1 j = 3
synchronized threadName = Thread-1 j = 4
synchronized threadName = Thread-1 j = 5
synchronized threadName = Thread-1 j = 6
synchronized threadName = Thread-1 j = 7
synchronized threadName = Thread-1 j = 8
synchronized threadName = Thread-1 j = 9

synchronized threadName = Thread-1 j = 10

三、synchronized(非this)代码块

    当一个类中有多个synchronized方法,使用synchronized(非this)代码块中的程序与其他同步方法是异步的 ,不与其他锁this同步方法争抢this锁,可以大大提高运行效率。

    但是使用synchronized(非this)代码块进行同步操作时,对象监视器必须是同一个对象,而持有不同的对象监视器是异步的。

同一个对象:

public class Service {
	private String anyString = new String();
	public void doSomething() {
		try {
			synchronized (anyString) {
				System.out.println("线程名称:"+Thread.currentThread().getName()
						+"    进入代码块时间:"+System.currentTimeMillis());
				Thread.sleep(3000);
				System.out.println("线程名称:"+Thread.currentThread().getName()
						+"    离开代码块时间:"+System.currentTimeMillis());
			}
		}catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
}
不同对象:
public class Service {
	public void doSomething() {
		try {
			String anyString = new String();
			synchronized (anyString) {
				System.out.println("线程名称:"+Thread.currentThread().getName()
						+"    进入代码块时间:"+System.currentTimeMillis());
				Thread.sleep(3000);
				System.out.println("线程名称:"+Thread.currentThread().getName()
						+"    离开代码块时间:"+System.currentTimeMillis());
			}
		}catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
}

四、synchronized静态方法

    synchronized关键字也可以加在static静态方法上,加在静态方法上是给Class类上锁,而synchronized关键字加在非static静态方法上是给对象上锁。

    

public class Service {
	synchronized public static void printA() {
		try {
			System.out.println("线程名称:"+Thread.currentThread().getName()
					+"    进入printA时间:"+System.currentTimeMillis());
			Thread.sleep(3000);
			System.out.println("线程名称:"+Thread.currentThread().getName()
					+"    离开printA时间:"+System.currentTimeMillis());
		}catch (InterruptedException e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
	synchronized public static void printB() {
			System.out.println("线程名称:"+Thread.currentThread().getName()
					+"     进入printB时间:"+System.currentTimeMillis());
			System.out.println("线程名称:"+Thread.currentThread().getName()
					+"     离开printB时间:"+System.currentTimeMillis());
	}
}

五、synchronized(Class)代码块

    该方法和synchronized静态方法作用上其实一样,只是用法上略有不同。

    

public class Service {
	public static void printA() {
		synchronized (Service.class) {
			try {
				System.out.println("线程名称:"+Thread.currentThread().getName()
						+"    进入printA时间:"+System.currentTimeMillis());
				Thread.sleep(3000);
				System.out.println("线程名称:"+Thread.currentThread().getName()
						+"    离开printA时间:"+System.currentTimeMillis());
			}catch (InterruptedException e) {
				// TODO: handle exception
				e.printStackTrace();
			}
		}
	}
	public static void printB() {
		synchronized (Service.class) {
			System.out.println("线程名称:"+Thread.currentThread().getName()
					+"     进入printB时间:"+System.currentTimeMillis());
			System.out.println("线程名称:"+Thread.currentThread().getName()
					+"     离开printB时间:"+System.currentTimeMillis());
		}
	}
}

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34519487/article/details/79947174
文章标签: Java 多线程 同步
个人分类: Java
上一篇Java多线程编程(一)——Thread中的start和run方法比较
想对作者说点什么? 我来说一句

Java多线程编程详解

2009年03月07日 38KB 下载

Java多线程编程详解.

2010年07月19日 38KB 下载

Java多线程编程详解 .doc

2010年10月11日 35KB 下载

JAVA多线程编程详解

2013年10月02日 60KB 下载

没有更多推荐了,返回首页

关闭
关闭