参考 Rowland 的博客: 网址:https://www.cnblogs.com/rwland/articles/9163183.html
java synchronized:方法锁、对象锁、类锁
其中:非静态方法锁和对象锁,都是对象实例的锁,即对象锁。应用于对象实例,实例可很多。
静态方法或类,都是类锁,只有一个类。应用于静态方法或类的class。类只有一个。
一、修饰方法或代码块。
package synchronizeTest;
public class TestSynchronized1 {
public void test1()
{
synchronized(this) //this为对象实例 ,对对象实例的加锁
{
int i = 5;
while( i-- > 0)
{
System.out.println(Thread.currentThread().getName() + " : " + i);
try
{
Thread.sleep(500);
}
catch (InterruptedException ie)
{
}
}
}
}
public synchronized void test2() //对象实例的加锁
{
int i = 5;
while( i-- > 0)
{
System.out.println(Thread.currentThread().getName() + " : " + i);
try
{
Thread.sleep(500);
}
catch (InterruptedException ie)
{
}
}
}
public static void main(String[] args)
{
final TestSynchronized1 myt2 = new TestSynchronized1();
Thread test1 = new Thread( new Runnable() { public void run() { myt2.test1(); } }, "test1" );
Thread test2 = new Thread( new Runnable() { public void run() { myt2.test2(); } }, "test2" );
test1.start();;
test2.start();
// TestRunnable tr=new TestRunnable();
// Thread test3=new Thread(tr);
// test3.start();
}
}
第一个方法时用了同步代码块的方式进行同步,传入的对象实例是this,表明是当前对象。第二个方法是修饰方法的方式进行同步。所以两个同步代码所需要获得的对象锁都是同一个对象锁,下面main方法时分别开启两个线程,分别调用test1和test2方法,那么两个线程都需要获得该对象锁,另一个线程必须等待。
输出为:
test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test2 : 4
test2 : 3
test2 : 2
test2 : 1
test2 : 0
如果去掉test2方法的synchronized关键字,则会交替输出。不占用线程锁。
二、类锁的修饰(静态)方法和代码块:
package synchronizeTest;
public class TestSynchronized2 {
public void test1()
{
synchronized(TestSynchronized2.class)
{
int i = 5;
while( i-- > 0)
{
System.out.println(Thread.currentThread().getName() + " : " + i);
try
{
Thread.sleep(500);
}
catch (InterruptedException ie)
{
}
}
}
}
public static synchronized void test2()
{
int i = 5;
while( i-- > 0)
{
System.out.println(Thread.currentThread().getName() + " : " + i);
try
{
Thread.sleep(500);
}
catch (InterruptedException ie)
{
}
}
}
public static void main(String[] args)
{
final TestSynchronized2 myt2 = new TestSynchronized2();
Thread test1 = new Thread( new Runnable() { public void run() { myt2.test1(); } }, "test1" );
Thread test2 = new Thread( new Runnable() { public void run() { TestSynchronized2.test2(); } }, "test2" );
test1.start();
test2.start();
// TestRunnable tr=new TestRunnable();
// Thread test3=new Thread(tr);
// test3.start();
}
}
都是对类加锁,此时是一个锁。静态方法是所有对象实例共用的,所以对应着synchronized修饰的静态方法的锁也是唯一的,所以抽象出来个类锁。
test1 : 4
test1 : 3
test1 : 2
test1 : 1
test1 : 0
test2 : 4
test2 : 3
test2 : 2
test2 : 1
test2 : 0
synchronized同时修饰静态和非静态方法,锁不同,则不会有线程并发问题。
1 类中方法内部的私有变量是线程安全的。类中直接变量是非线程安全的。
public class Hasprivate{
public void addI(){
int num=0; //方法内部私有变量 ,此时num是线程安全
}
}
public class Hasprivate{
private int num=0; //类中直接是非线程安全
public void addI(){
}
}
public class Hasprivate{
private int num=0; //类中直接是非线程安全,但后面添加 synchronized 也是线程安全的
synchronized public void addI(){
}
}
2 synchronized关键字加到static静态方法上是给Class类上锁,而synchronized关键字加到非static静态方法上是给对象上锁。
public class Service{
synchronized public void methodA(){
System.out.println("methodA begin");
while(true){
}
System.out.println("methodA end");
}
synchronized public void methodB(){
System.out.println("methodB begin");
System.out.println("methodB end");
}
}
//如果有2个线程,都采用同一个实例化对象Service,则一个线程调用methodA方法,一个调用methodB方法,则输出为:
methodA begin.
剩下无法输出。
原因为:synchronized方法是对实例化对象Service加锁,当调用methodA方法时,由于一直在执行,无法执行完methodA方法,故该对象锁,一直未释放,故methodB无法执行。
为解决上面的问题,故:
public class Service{
Object object1=new Object();
public void methodA(){
synchronized (object1){
System.out.println("methodA begin");
while(true){
}
System.out.println("methodA end");
}
}
Object object2=new Object();
public void methodB(){
synchronized (object2){
System.out.println("methodB begin");
System.out.println("methodB end");
}
}
}
//此时可输出:方法内部锁,不对实例Service对象加锁
methodA begin
methodB begin
methodB end.