多线程安全

脏读:多个线程对同一个对象的实例变量进行修改后访问,导致读到的数据是被修改过的。

实例

ThreadDomain16类

public class ThreadDomain16 {
private int num = 0; public void addNum(String userName)
{
try
{
if ("a".equals(userName))
{
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)
{
e.printStackTrace();
}
}
}

MyThread16_0类

public class MyThread16_0 extends Thread{
private ThreadDomain16 td; public MyThread16_0(ThreadDomain16 td)
{
this.td = td;
} public void run()
{
td.addNum("a");
}
}

MyThread16_1类

启动MyThread16_0 和MyThread16_1 线程

public class MyThread16_1 extends Thread {
private ThreadDomain16 td; public MyThread16_1(ThreadDomain16 td)
{
this.td = td;
} public void run()
{
td.addNum("b");
} }

main方法

public class MyThread16_main_0 {
public static void main(String[] args)
{
ThreadDomain16 td = new ThreadDomain16();
MyThread16_0 mt0 = new MyThread16_0(td);
MyThread16_1 mt1 = new MyThread16_1(td);
mt0.start();
mt1.start();
}
}

输出结果如下

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

理论上应该输出a num = 100,b num = 200,结果a num = 200。

我们来分析下运行流程,MyThread16_0线程修改num为100,此时sleep两秒,在MyThread16_0 sleep的过程中,MyThread16_1线程修改num为200。导致MyThread16_0的num也变成200。最后先输出b num = 200,再输出a num = 200。出现了脏读。

解决该问题很简单,我们在addNum()前面加上synchronized关键字

public synchronized void addNum(String userName)
{
try
{
if ("a".equals(userName))
{
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)
{
e.printStackTrace();
}
}

输出结果如下

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

按序打印,synchronized修饰的方法形成了锁,哪个线程先执行了synchronized修饰的方法哪个线程就先获得了锁,其他线程必须等待,直到该线程释放锁,等待的线程获得锁方可执行。

一个对象一个锁

同步情况下,使用如下main方法

public class MyThread16_main_1 {
public static void main(String[] args)
{
ThreadDomain16 td0 = new ThreadDomain16();
ThreadDomain16 td1 = new ThreadDomain16();
MyThread16_0 mt0 = new MyThread16_0(td0);
MyThread16_1 mt1 = new MyThread16_1(td1);
mt0.start();
mt1.start();
}
}

运行结果

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

出现了交叉打印,b没有等待a执行结束,说明synchronized是对象锁,而不是方法锁。这里声明了两个ThreadDomain16对象,所以有两个对象锁,不受“等待锁的释放”的制约。

那么我们把addNum方法前面的synchronized关键字去掉,异步情况

运行结果如下

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

可以看到,没有synchronized的帮助,依然避免了脏读现象。实例变量num为每个对象独自占有。但是,这种解决方式需要声明多个对象,不推荐。

synchronized方法与锁对象

现在看一个例子,一个方法有synchronized修饰,另一个方法无synchronized修饰

ThreadDomain17类

public class ThreadDomain17 {
public synchronized void methodA()
{
try
{
System.out.println("开始 methodA, 线程名称 = " +
Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("结束 methodA, 线程名称 = " +
Thread.currentThread().getName());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
} public void methodB()
{
try
{
System.out.println("开始 methodB, 线程名称 = " +
Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("结束 methodB, 线程名称 = " +
Thread.currentThread().getName());
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}

MyThread17_0类

public class MyThread17_0 extends Thread{
private ThreadDomain17 td; public MyThread17_0(ThreadDomain17 td)
{
this.td = td;
} public void run()
{
td.methodA();
}
}

MyThread17_1类

public class MyThread17_1 extends Thread{
private ThreadDomain17 td; public MyThread17_1(ThreadDomain17 td)
{
this.td = td;
} public void run()
{
td.methodB();
}
}

main方法

public class MyThread17_main {
public static void main(String[] args) {
ThreadDomain17 td = new ThreadDomain17();
MyThread17_0 mt0 = new MyThread17_0(td);
mt0.setName("A");
MyThread17_1 mt1 = new MyThread17_1(td);
mt1.setName("B");
mt0.start();
mt1.start();
}
}

运行结果如下

开始 methodB, 线程名称 = B
开始 methodA, 线程名称 = A
结束 methodA, 线程名称 = A
结束 methodB, 线程名称 = B

可以看到,非synchronized的methodB方法以异步方式被调用。

把MyThread17_1类的run方法改成td.methodA();

输出结果如下

开始 methodA, 线程名称 = A
结束 methodA, 线程名称 = A
开始 methodA, 线程名称 = B
结束 methodA, 线程名称 = B

待执行的线程在等待synchronized方法同步再执行。

A线程持有ThreadDomain17对象锁,B线程可以异步方式调用ThreadDomain17对象的非synchronized方法;

A线程持有ThreadDomain17对象锁,B线程调用ThreadDomain17对象的synchronized方法需要等待;


把MyThread17_1类的run方法改回td.methodB();把ThreadDomain17类的methodB方法加上synchronized关键字,输出结果如下

开始 methodA, 线程名称 = A
结束 methodA, 线程名称 = A
开始 methodB, 线程名称 = B
结束 methodB, 线程名称 = B

这个很好理解,先执行的线程获得同步锁,等该线程必须执行完毕,等待的线程才能执行,所有的操作都是同步的。

synchronized锁重入

synchronized是可重入锁,当一个线程得到对象锁之后,可以再次申请对象锁并获得对象锁。

ThreadDomain18类

public class ThreadDomain18 {
public synchronized void print1()
{
System.out.println("ThreadDomain18.print1()");
print2();
} public synchronized void print2()
{
System.out.println("ThreadDomain18.print2()");
print3();
} public synchronized void print3()
{
System.out.println("ThreadDomain18.print3()");
}
}

MyThread18类

public class MyThread18 extends Thread{
public void run()
{
ThreadDomain18 td = new ThreadDomain18();
td.print1();
} public static void main(String[] args)
{
MyThread18 mt = new MyThread18();
mt.start();
} }

结果如下

ThreadDomain18.print1()
ThreadDomain18.print2()
ThreadDomain18.print3()

结果说明,MyThread18在有锁的情况下多次获取到对象锁

异常释放锁

ThreadDomain19类

public class ThreadDomain19 {
public synchronized void testMethod()
{
try
{
System.out.println("进入 ThreadDomain19.testMethod(), currentThread = " +
Thread.currentThread().getName());
long l = 5;
while (true)
{
long lo = 2 / l;
Thread.sleep(1000);
l--;
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}

MyThread19类

public class MyThread19 extends Thread {
private ThreadDomain19 td; public MyThread19(ThreadDomain19 td)
{
this.td = td;
} public void run()
{
td.testMethod();
} public static void main(String[] args)
{
ThreadDomain19 td = new ThreadDomain19();
MyThread19 mt0 = new MyThread19(td);
MyThread19 mt1 = new MyThread19(td);
mt0.start();
mt1.start();
}
}

输出结果如下

进入 ThreadDomain19.testMethod(), currentThread = Thread-1
java.lang.ArithmeticException: / by zero
进入 ThreadDomain19.testMethod(), currentThread = Thread-0
at com.advance.MultiThread3.ThreadDomain19.testMethod(ThreadDomain19.java:18)
at com.advance.MultiThread3.MyThread19.run(MyThread19.java:18)
java.lang.ArithmeticException: / by zero
at com.advance.MultiThread3.ThreadDomain19.testMethod(ThreadDomain19.java:18)
at com.advance.MultiThread3.MyThread19.run(MyThread19.java:18)

由上可见,一旦出现异常,锁自动释放。Thread-1刚抛完异常,此时Thread-1释放了锁,Thread-0就开始执行。

synchronized方法块

ThreadDomain20类

public class ThreadDomain20 extends Thread{
public void doLongTimeTask() throws Exception
{
for (int i = 0; i < 100; i++)
{
System.out.println("nosynchronized threadName = " +
Thread.currentThread().getName() + ", i = " + (i + 1));
}
System.out.println();
synchronized (this){
for (int i = 0; i < 100; i++)
{
System.out.println("synchronized threadName = " +
Thread.currentThread().getName() + ", i = " + (i + 1));
}
}
} }

MyThread20类

public class MyThread20 extends Thread{
private ThreadDomain20 td; public MyThread20(ThreadDomain20 td)
{
this.td = td;
} public void run()
{
try
{
td.doLongTimeTask();
}
catch (Exception e)
{
e.printStackTrace();
}
} public static void main(String[] args)
{
ThreadDomain20 td = new ThreadDomain20();
MyThread20 mt0 = new MyThread20(td);
mt0.setName("A");
MyThread20 mt1 = new MyThread20(td);
mt1.setName("B");
mt0.start();
mt1.start();
} }

运行结果如下

nosynchronized threadName = A, i = 1
nosynchronized threadName = B, i = 1
nosynchronized threadName = A, i = 2
nosynchronized threadName = B, i = 2
nosynchronized threadName = A, i = 3
nosynchronized threadName = B, i = 3
nosynchronized threadName = A, i = 4
nosynchronized threadName = B, i = 4
nosynchronized threadName = A, i = 5
nosynchronized threadName = B, i = 5
nosynchronized threadName = A, i = 6
nosynchronized threadName = B, i = 6
nosynchronized threadName = A, i = 7
nosynchronized threadName = B, i = 7
nosynchronized threadName = A, i = 8
nosynchronized threadName = B, i = 8
nosynchronized threadName = A, i = 9
nosynchronized threadName = B, i = 9
nosynchronized threadName = A, i = 10
nosynchronized threadName = B, i = 10
nosynchronized threadName = A, i = 11
nosynchronized threadName = B, i = 11
nosynchronized threadName = A, i = 12
nosynchronized threadName = B, i = 12
nosynchronized threadName = A, i = 13
nosynchronized threadName = B, i = 13
nosynchronized threadName = A, i = 14
nosynchronized threadName = B, i = 14
nosynchronized threadName = A, i = 15
nosynchronized threadName = A, i = 16
nosynchronized threadName = A, i = 17
nosynchronized threadName = A, i = 18
nosynchronized threadName = B, i = 15
nosynchronized threadName = A, i = 19
nosynchronized threadName = B, i = 16
nosynchronized threadName = A, i = 20
nosynchronized threadName = B, i = 17
nosynchronized threadName = A, i = 21
nosynchronized threadName = B, i = 18
nosynchronized threadName = A, i = 22
nosynchronized threadName = B, i = 19
nosynchronized threadName = A, i = 23
nosynchronized threadName = A, i = 24
nosynchronized threadName = B, i = 20
nosynchronized threadName = A, i = 25
nosynchronized threadName = B, i = 21
nosynchronized threadName = A, i = 26
nosynchronized threadName = B, i = 22
nosynchronized threadName = A, i = 27
nosynchronized threadName = B, i = 23
nosynchronized threadName = A, i = 28
nosynchronized threadName = B, i = 24
nosynchronized threadName = A, i = 29
nosynchronized threadName = B, i = 25
nosynchronized threadName = A, i = 30
nosynchronized threadName = B, i = 26
nosynchronized threadName = A, i = 31
nosynchronized threadName = A, i = 32
nosynchronized threadName = B, i = 27
nosynchronized threadName = A, i = 33
nosynchronized threadName = B, i = 28
nosynchronized threadName = A, i = 34
nosynchronized threadName = B, i = 29
nosynchronized threadName = A, i = 35
nosynchronized threadName = B, i = 30
nosynchronized threadName = A, i = 36
nosynchronized threadName = B, i = 31
nosynchronized threadName = A, i = 37
nosynchronized threadName = B, i = 32
nosynchronized threadName = A, i = 38
nosynchronized threadName = A, i = 39
nosynchronized threadName = B, i = 33
nosynchronized threadName = A, i = 40
nosynchronized threadName = B, i = 34
nosynchronized threadName = A, i = 41
nosynchronized threadName = B, i = 35
nosynchronized threadName = A, i = 42
nosynchronized threadName = B, i = 36
nosynchronized threadName = A, i = 43
nosynchronized threadName = B, i = 37
nosynchronized threadName = A, i = 44
nosynchronized threadName = B, i = 38
nosynchronized threadName = A, i = 45
nosynchronized threadName = B, i = 39
nosynchronized threadName = A, i = 46
nosynchronized threadName = B, i = 40
nosynchronized threadName = A, i = 47
nosynchronized threadName = B, i = 41
nosynchronized threadName = A, i = 48
nosynchronized threadName = B, i = 42
nosynchronized threadName = A, i = 49
nosynchronized threadName = B, i = 43
nosynchronized threadName = A, i = 50
nosynchronized threadName = B, i = 44 nosynchronized threadName = B, i = 45
synchronized threadName = A, i = 1
nosynchronized threadName = B, i = 46
synchronized threadName = A, i = 2
nosynchronized threadName = B, i = 47
synchronized threadName = A, i = 3
nosynchronized threadName = B, i = 48
synchronized threadName = A, i = 4
nosynchronized threadName = B, i = 49
nosynchronized threadName = B, i = 50 synchronized threadName = A, i = 5
synchronized threadName = A, i = 6
synchronized threadName = A, i = 7
synchronized threadName = A, i = 8
synchronized threadName = A, i = 9
synchronized threadName = A, i = 10
synchronized threadName = A, i = 11
synchronized threadName = A, i = 12
synchronized threadName = A, i = 13
synchronized threadName = A, i = 14
synchronized threadName = A, i = 15
synchronized threadName = A, i = 16
synchronized threadName = A, i = 17
synchronized threadName = A, i = 18
synchronized threadName = A, i = 19
synchronized threadName = A, i = 20
synchronized threadName = A, i = 21
synchronized threadName = A, i = 22
synchronized threadName = A, i = 23
synchronized threadName = A, i = 24
synchronized threadName = A, i = 25
synchronized threadName = A, i = 26
synchronized threadName = A, i = 27
synchronized threadName = A, i = 28
synchronized threadName = A, i = 29
synchronized threadName = A, i = 30
synchronized threadName = A, i = 31
synchronized threadName = A, i = 32
synchronized threadName = A, i = 33
synchronized threadName = A, i = 34
synchronized threadName = A, i = 35
synchronized threadName = A, i = 36
synchronized threadName = A, i = 37
synchronized threadName = A, i = 38
synchronized threadName = A, i = 39
synchronized threadName = A, i = 40
synchronized threadName = A, i = 41
synchronized threadName = A, i = 42
synchronized threadName = A, i = 43
synchronized threadName = A, i = 44
synchronized threadName = A, i = 45
synchronized threadName = A, i = 46
synchronized threadName = A, i = 47
synchronized threadName = A, i = 48
synchronized threadName = A, i = 49
synchronized threadName = A, i = 50
synchronized threadName = B, i = 1
synchronized threadName = B, i = 2
synchronized threadName = B, i = 3
synchronized threadName = B, i = 4
synchronized threadName = B, i = 5
synchronized threadName = B, i = 6
synchronized threadName = B, i = 7
synchronized threadName = B, i = 8
synchronized threadName = B, i = 9
synchronized threadName = B, i = 10
synchronized threadName = B, i = 11
synchronized threadName = B, i = 12
synchronized threadName = B, i = 13
synchronized threadName = B, i = 14
synchronized threadName = B, i = 15
synchronized threadName = B, i = 16
synchronized threadName = B, i = 17
synchronized threadName = B, i = 18
synchronized threadName = B, i = 19
synchronized threadName = B, i = 20
synchronized threadName = B, i = 21
synchronized threadName = B, i = 22
synchronized threadName = B, i = 23
synchronized threadName = B, i = 24
synchronized threadName = B, i = 25
synchronized threadName = B, i = 26
synchronized threadName = B, i = 27
synchronized threadName = B, i = 28
synchronized threadName = B, i = 29
synchronized threadName = B, i = 30
synchronized threadName = B, i = 31
synchronized threadName = B, i = 32
synchronized threadName = B, i = 33
synchronized threadName = B, i = 34
synchronized threadName = B, i = 35
synchronized threadName = B, i = 36
synchronized threadName = B, i = 37
synchronized threadName = B, i = 38
synchronized threadName = B, i = 39
synchronized threadName = B, i = 40
synchronized threadName = B, i = 41
synchronized threadName = B, i = 42
synchronized threadName = B, i = 43
synchronized threadName = B, i = 44
synchronized threadName = B, i = 45
synchronized threadName = B, i = 46
synchronized threadName = B, i = 47
synchronized threadName = B, i = 48
synchronized threadName = B, i = 49
synchronized threadName = B, i = 50

结论:

A线程访问synchronized方法块的时候,B线程依然可以访问非synchronized方法块的内容;

A线程访问synchronized方法块的时候,B线程访问synchronized方法块会被阻塞;

synchronized方法块和synchronized方法

synchronized关键字修饰的方法和synchronized方法块修饰的方法效果一致。synchronized方法块可以提高代码执行效率,在需要同步的地方去同步。

ThreadDomain21类

public class ThreadDomain21 {
public synchronized void otherMethod()
{
System.out.println("----------run--otherMethod");
} public void doLongTask()
{
synchronized (this)
{
for (int i = 0; i < 100; i++)
{
System.out.println("synchronized threadName = " +
Thread.currentThread().getName() + ", i = " + (i + 1));
try
{
Thread.sleep(5);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
}

MyThread21_0类

public class MyThread21_0 extends Thread{
private ThreadDomain21 td; public MyThread21_0(ThreadDomain21 td)
{
this.td = td;
} public void run()
{
td.doLongTask();
}
}

MyThread21_1类

public class MyThread21_1 extends Thread{
private ThreadDomain21 td; public MyThread21_1(ThreadDomain21 td)
{
this.td = td;
} public void run()
{
td.otherMethod();
}
}

main方法,sleep一会,让mt0先执行。

public class MyThread21_main {
public static void main(String[] args) throws Exception
{
ThreadDomain21 td = new ThreadDomain21();
MyThread21_0 mt0 = new MyThread21_0(td);
mt0.setName("A");
MyThread21_1 mt1 = new MyThread21_1(td);
mt1.setName("B");
mt0.start();
Thread.sleep(100);
mt1.start();
}
}

输出结果如下

synchronized threadName = A, i = 1
synchronized threadName = A, i = 2
synchronized threadName = A, i = 3
synchronized threadName = A, i = 4
synchronized threadName = A, i = 5
synchronized threadName = A, i = 6
synchronized threadName = A, i = 7
synchronized threadName = A, i = 8
synchronized threadName = A, i = 9
synchronized threadName = A, i = 10
synchronized threadName = A, i = 11
synchronized threadName = A, i = 12
synchronized threadName = A, i = 13
synchronized threadName = A, i = 14
synchronized threadName = A, i = 15
synchronized threadName = A, i = 16
synchronized threadName = A, i = 17
synchronized threadName = A, i = 18
synchronized threadName = A, i = 19
synchronized threadName = A, i = 20
synchronized threadName = A, i = 21
synchronized threadName = A, i = 22
synchronized threadName = A, i = 23
......
synchronized threadName = A, i = 89
synchronized threadName = A, i = 90
synchronized threadName = A, i = 91
synchronized threadName = A, i = 92
synchronized threadName = A, i = 93
synchronized threadName = A, i = 94
synchronized threadName = A, i = 95
synchronized threadName = A, i = 96
synchronized threadName = A, i = 97
synchronized threadName = A, i = 98
synchronized threadName = A, i = 99
synchronized threadName = A, i = 100
----------run--otherMethod

可以看到,otherMethod方法被doLongTask方法阻塞,说明synchronized锁定了整个对象,把otherMethod方法synchronized关键字去掉,输出结果如下

synchronized threadName = A, i = 1
synchronized threadName = A, i = 2
synchronized threadName = A, i = 3
synchronized threadName = A, i = 4
synchronized threadName = A, i = 5
synchronized threadName = A, i = 6
synchronized threadName = A, i = 7
synchronized threadName = A, i = 8
synchronized threadName = A, i = 9
synchronized threadName = A, i = 10
synchronized threadName = A, i = 11
synchronized threadName = A, i = 12
synchronized threadName = A, i = 13
synchronized threadName = A, i = 14
synchronized threadName = A, i = 15
synchronized threadName = A, i = 16
synchronized threadName = A, i = 17
synchronized threadName = A, i = 18
synchronized threadName = A, i = 19
synchronized threadName = A, i = 20
synchronized threadName = A, i = 21
----------run--otherMethod
......
synchronized threadName = A, i = 72
synchronized threadName = A, i = 73
synchronized threadName = A, i = 74
synchronized threadName = A, i = 75
synchronized threadName = A, i = 76
synchronized threadName = A, i = 77
synchronized threadName = A, i = 78
synchronized threadName = A, i = 79
synchronized threadName = A, i = 80
synchronized threadName = A, i = 81
synchronized threadName = A, i = 82
synchronized threadName = A, i = 83
synchronized threadName = A, i = 84
synchronized threadName = A, i = 85
synchronized threadName = A, i = 86
synchronized threadName = A, i = 87
synchronized threadName = A, i = 88
synchronized threadName = A, i = 89
synchronized threadName = A, i = 90
synchronized threadName = A, i = 91
synchronized threadName = A, i = 92
synchronized threadName = A, i = 93
synchronized threadName = A, i = 94
synchronized threadName = A, i = 95
synchronized threadName = A, i = 96
synchronized threadName = A, i = 97
synchronized threadName = A, i = 98
synchronized threadName = A, i = 99
synchronized threadName = A, i = 100

otherMethod方法异步执行了。

结论如下:

不同的方法被synchronized修饰会产生互斥,synchronized锁住的是对象;

synchronized方法块和synchronized方法效果一致;

synchronized(非this对象)

ThreadDomain22类

public class ThreadDomain22 {
private String userNameParam;
private String passwordParam;
private String anyString = new String(); public void setUserNamePassword(String userName, String password)
{
try
{
synchronized (anyString)
{
System.out.println("线程名称为:" + Thread.currentThread().getName() +
"在 " + new Date() + " 进入同步代码块");
userNameParam = userName;
Thread.sleep(3000);
passwordParam = password;
System.out.println("线程名称为:" + Thread.currentThread().getName() +
"在 " + new Date() + " 离开同步代码块");
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}

MyThread22_0类

public class MyThread22_0 extends Thread{
private ThreadDomain22 td; public MyThread22_0(ThreadDomain22 td)
{
this.td = td;
} public void run()
{
td.setUserNamePassword("A", "AA");
}
}

MyThread22_1类

public class MyThread22_1 extends Thread{
private ThreadDomain22 td; public MyThread22_1(ThreadDomain22 td)
{
this.td = td;
} public void run()
{
td.setUserNamePassword("B", "B");
}
}

main

public class MyThread22_main{
public static void main(String[] args)
{
ThreadDomain22 td = new ThreadDomain22();
MyThread22_0 mt0 = new MyThread22_0(td);
MyThread22_1 mt1 = new MyThread22_1(td);
mt0.start();
mt1.start();
}
}

输出结果

线程名称为:Thread-0在 Tue Jul 02 14:51:16 CST 2019 进入同步代码块
线程名称为:Thread-0在 Tue Jul 02 14:51:19 CST 2019 离开同步代码块
线程名称为:Thread-1在 Tue Jul 02 14:51:19 CST 2019 进入同步代码块
线程名称为:Thread-1在 Tue Jul 02 14:51:22 CST 2019 离开同步代码块

synchronized(anyString)和synchronized(this)效果一致,anyString作为全局变量是一个对象,如果放入try里面,就变成了多个对象,变成了异步执行。

再看一个例子

ThreadDomain23类

public class ThreadDomain23 {
public void testMethod1(MyObject mo)
{
try
{
synchronized (mo)
{
System.out.println("testMethod1__getLock time = " +
new Date() + ", run ThreadName = " +
Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("testMethod1__releaseLock time = " +
new Date() + ", run ThreadName = " +
Thread.currentThread().getName());
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}

MyObject类

public class MyObject {
public synchronized void speedPrintString()
{
System.out.println("speedPrintString__getLock time = " +
new Date() + ", run ThreadName = " +
Thread.currentThread().getName());
System.out.println("speedPrintString__releaseLock time = " +
new Date() + ", run ThreadName = " +
Thread.currentThread().getName());
}
}

MyThread23_0类

public class MyThread23_0 extends Thread{
private ThreadDomain23 td;
private MyObject mo; public MyThread23_0(ThreadDomain23 td, MyObject mo)
{
this.td = td;
this.mo = mo;
} public void run()
{
td.testMethod1(mo);
}
}

MyThread23_1类

public class MyThread23_1 extends Thread {
private MyObject mo; public MyThread23_1(MyObject mo)
{
this.mo = mo;
} public void run()
{
mo.speedPrintString();
}
}

main方法

public class MyThread23_main {
public static void main(String[] args)
{
ThreadDomain23 td = new ThreadDomain23();
MyObject mo = new MyObject();
MyThread23_0 mt0 = new MyThread23_0(td, mo);
MyThread23_1 mt1 = new MyThread23_1(mo);
mt0.start();
mt1.start();
}
}

输出结果如下

testMethod1__getLock time = Tue Jul 02 15:23:05 CST 2019, run ThreadName = Thread-0
testMethod1__releaseLock time = Tue Jul 02 15:23:10 CST 2019, run ThreadName = Thread-0
speedPrintString__getLock time = Tue Jul 02 15:23:10 CST 2019, run ThreadName = Thread-1
speedPrintString__releaseLock time = Tue Jul 02 15:23:10 CST 2019, run ThreadName = Thread-1

speedPrintString方法总是在testMethod1方法之后执行,说明ThreadDomain23获得了锁,mo对象中的syncronized方法被阻塞,执行完testMethod1方法才会执行speedPrintString方法。

即当一个线程调用该方法时,其他线程阻塞。

结论:

1.当多个线程同时执行synchronized(非this对象){}同步代码块时呈同步效果

2.当其他线程执行非this对象中的synchronized同步方法时呈同步效果

synchronized静态方法

synchronized static可以对当前的类加锁,即类锁。

ThreadDomain24类

public class ThreadDomain24 {
public synchronized static void printA()
{
try
{
System.out.println("线程名称为:" + Thread.currentThread().getName() +
"在" + System.currentTimeMillis() + "进入printA()方法");
Thread.sleep(3000);
System.out.println("线程名称为:" + Thread.currentThread().getName() +
"在" + System.currentTimeMillis() + "离开printA()方法");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
} public synchronized static void printB()
{
System.out.println("线程名称为:" + Thread.currentThread().getName() +
"在" + System.currentTimeMillis() + "进入printB()方法");
System.out.println("线程名称为:" + Thread.currentThread().getName() +
"在" + System.currentTimeMillis() + "离开printB()方法"); } public synchronized void printC()
{
System.out.println("线程名称为:" + Thread.currentThread().getName() +
"在" + System.currentTimeMillis() + "进入printC()方法");
System.out.println("线程名称为:" + Thread.currentThread().getName() +
"在" + System.currentTimeMillis() + "离开printC()方法");
}
}

MyThread24_0类,静态方法

public class MyThread24_0 extends Thread{
public void run()
{
ThreadDomain24.printA();
}
}

MyThread24_1类,静态方法

public class MyThread24_1 extends Thread {
public void run()
{
ThreadDomain24.printB();
}
}

MyThread24_2类,实例化调用方法

public class MyThread24_2 extends Thread{
private ThreadDomain24 td; public MyThread24_2(ThreadDomain24 td)
{
this.td = td;
} public void run()
{
td.printC();
}
}

main方法

public class MyThread24_main {
public static void main(String[] args)
{
ThreadDomain24 td = new ThreadDomain24();
MyThread24_0 mt0 = new MyThread24_0();
MyThread24_1 mt1 = new MyThread24_1();
MyThread24_2 mt2 = new MyThread24_2(td);
mt0.start();
mt1.start();
mt2.start();
}
}

输出结果如下

线程名称为:Thread-0在1562057924755进入printA()方法
线程名称为:Thread-2在1562057924755进入printC()方法
线程名称为:Thread-2在1562057924755离开printC()方法
线程名称为:Thread-0在1562057927755离开printA()方法
线程名称为:Thread-1在1562057927755进入printB()方法
线程名称为:Thread-1在1562057927755离开printB()方法

可以看到,printA方法(类锁)和printB方法(类锁)同步执行,printC方法(对象锁)异步执行,printA方法和printB方法互斥,printC不受类锁影响。

结论:

静态方法持有类锁,非静态方法持有对象锁,两者互不干扰。

Java多线程(三):Synchronized的更多相关文章

  1. java 多线程三

    java 多线程一 java 多线程二 java 多线程三 java 多线程四 注意到 java 多线程一 中 MyThread2 运行结果出现0.-1,那是因为在操作共享数据时没有加锁导致. 加锁的 ...

  2. Java多线程-同步:synchronized 和线程通信:生产者消费者模式

    大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同 ...

  3. 从零开始学习Java多线程(三)

    本文主要对Java多线程同步与通信以及相关锁的介绍. 1 .Java多线程安全问题 Java多线程安全问题是实现并发最大的问题,可以说多线程开发其实就是围绕多线程安全问题开发,涉及之深,不是简简单单一 ...

  4. java多线程三之线程协作与通信实例

    多线程的难点主要就是多线程通信协作这一块了,前面笔记二中提到了常见的同步方法,这里主要是进行实例学习了,今天总结了一下3个实例: 1.银行存款与提款多线程实现,使用Lock锁和条件Condition. ...

  5. Java多线程同步 synchronized 关键字的使用

    代表这个方法加锁,相当于不管哪一个线程A每次运行到这个方法时,都要检查有没有其它正在用这个方法的线程B(或者C D等),有的话要等正在使用这个方法的线程B(或者C D)运行完这个方法后再运行此线程A, ...

  6. Java多线程——<三>简单的线程执行:Executor

    一.概述 按照<Java多线程——<一><二>>中所讲,我们要使用线程,目前都是显示的声明Thread,并调用其start()方法.多线程并行,明显我们需要声明多个 ...

  7. Java多线程同步方法Synchronized和volatile

    11 同步方法  synchronized – 同时解决了有序性.可见性问题  volatile – 结果可见性问题 12 同步- synchronized synchronized可以在任意对象上加 ...

  8. java多线程(三)-Executors实现的几种线程池以及Callable

    从java5开始,类库中引入了很多新的管理调度线程的API,最常用的就是Executor(执行器)框架.Executor帮助程序员管理Thread对象,简化了并发编程,它其实就是在 提供了一个中间层, ...

  9. Java多线程:synchronized的可重入性

    从Java多线程:线程间通信之volatile与sychronized这篇文章中我们了解了synchronized的基本特性,知道了一旦有一个线程访问某个对象的synchronized修饰的方法或代码 ...

  10. 关于JAVA多线程并发synchronized的测试与合理使用

    在项目开发中, 或许会碰到JAVA的多线程处理, 为保证业务数据的正常, 必须加上锁机制,  常用的处理方法一般是加上synchronized关键字, 目前JDK版本对synchronized已经做了 ...

随机推荐

  1. 拦截和跟踪HTTP请求的主要方法及实现

    一.HTTP 请求的拦截技术可以广泛地应用在反向代理.拦截 Ajax 通信.网页的在线翻译.网站改版重构等方面.而拦截根据位置可以分为服务器端和客户端两大类,客户端拦截借助 JavaScript 脚本 ...

  2. ssh探头安全

    1.  ssh 合约                 SSH 为建立在应用层和传输层基础上的安全协议. SSH 是眼下较可靠,专为远程登录会话和其它网络服务提供安全性的协议.利用 SSH 协议能够有效 ...

  3. 如若已在管理后台更新域名配置,请刷新项目配置后重新编译项目,操作路径:“项目-域名信息” http://www.mysite.com 不在以下 request 合法域名列表中

    报错如图 报错文字如下: 如若已在管理后台更新域名配置,请刷新项目配置后重新编译项目,操作路径:“项目-域名信息” http://www.mysite.net 不在以下 request 合法域名列表中 ...

  4. Linux性能测试 top衍生命令 atop/htop/slaptop

    1. Atop Atop 是一个类似 top 的工具,但比 top 更有料.通过 Atop,你能够监视 Linux 系统的性能状况,包括进程活动.CPU.内存.硬盘.网络等方面的使用情况等. 2. h ...

  5. Hopfield 神经网络及稳态性的证明

    根据其提出者,John Joseph Hopfield 命名.Hopfield 在 1982 年提出的划时代的:Neural networks and physical systems with em ...

  6. SICP 锻炼 (1.40)解决摘要

    SICP 锻炼1.40 是一个休闲的工作非常easy,但它看起来很复杂,单的一道题. 题目原题例如以下: 请定义一个过程cubic, 它和newtons-method过程一起使用在以下形式的表达式里: ...

  7. ASP.NET Core MVC 设计模式 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core MVC 设计模式 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core MVC 设计模式 上一章节中,我们提到 ASP.NET Co ...

  8. 【C++】【TinyXml】xml文件的读写功能使用——写xml文件

    TinyXml工具是常用比较简单的C++中xml读写的工具 需要加载 #include "TinyXml\tinyxml.h" 在TinyXML中,根据XML的各种元素来定义了一些 ...

  9. halcon基础数据类型

    halcon基础数据类型 使用变量不需定义 等号       := 不等号    # 字符串赋值  str:='sdff' 等于比较符         if(q=0) 与       if(a< ...

  10. jquery 相对元素

    <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content=&q ...