synchronized锁可重入,当一个线程得到一个对象锁后,再次请求此对象锁时是可以再次得到该对象的锁;
(1). 父子类继承
class Parent{
synchronized public void method1(){
}
}
class Child{
synchronized public void method1(){
super.method1();
}
}
当我们调用child的method1()时,可以进入Parent的method1(),如果不支持可重入,那么会陷入死锁。
(2). 同一个类不同synchronized方法之间的调用。
(1).对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态;
(2).同一时间只有一个线程可以执行synchronized同步方法中的代码。
同步代码块可以解决同步方法的一些问题,我们可以将比较耗时且不需要同步的代码放到同步代码块外面,来提高效率。
synchronized(this)同步代码块
(1).对其他synchronized同步方法或synchronized(this)同步代码块调用呈阻塞状态。
(2).同一时间只有一个线程可以执行synchronized(this)同步代码块中的代码。
String的常量池特性
public class Service8 {
public static void print(String stringParam){
try{
synchronized (stringParam) {
while(true){
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
}
}
}catch(InterruptedException e){
}
}
}
下面有两个线程A,B执行上面的任务。
线程A:
public class ThreadA20 extends Thread {
private Service8 service;
public ThreadA20(Service8 service){
super();
this.service = service;
}
@Override
public void run(){
service.print("AA");
}
}
线程B:
public class ThreadB20 extends Thread {
private Service8 service;
public ThreadB20(Service8 service){
super();
this.service = service;
}
@Override
public void run(){
service.print("AA");
}
}
在主线程里面同时开始两个任务,就会发现只执行线程A,原因是因为String常量池的原因,导致A,B持有的锁相同,所以B被阻塞。
锁对象的改变
public class MyService1 {
private String lock = “123”;
public void testMethod(){
try{
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " begin "
+ System.currentTimeMillis());
lock = "456";
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " end "
+ System.currentTimeMillis());
}
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
我们启动线程A,B会发现异步执行,因为锁对象发生了改变。如果锁为一个对象,对象的属性改变,多线程依旧会同步执行。
在多线程中容易造成死锁,可以用JDK自带的工具来监测是否有死锁的现象。进入jdk的安装文件夹中的bin目录,执行jps命令。
再运行jstack命令查看结果。jstack -l 3244
volatile的主要作用是使变量在多个线程间可见,强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。
而用了volatile后变量的读取与写入方式变化为:
volatile能保证数据的可见性,但是不能保证原子性。synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存的数据做同步。
public class MyThread2 extends Thread {
volatile public static int count;
private static void addCount(){
for(int i = 0; i < 100; i++){
count++;
}
System.out.println("count=" + count);
}
@Override
public void run(){
addCount();
}
}
然后我们启动100个线程:
public class Run37 {
public static void main(String[] args) {
MyThread2[] myThreadArray = new MyThread2[100];
for(int i = 0; i < 100; i++){
myThreadArray[i] = new MyThread2();
}
for(int i = 0; i < 100; i++){
myThreadArray[i].start();
}
}
}
运行结果如下:
并不是预期的10000.所以说明volatile不能保证同步,只能保证可见性。
synchronized代码块有volatile同步的功能
public class Service {
private boolean isContinueRun = true;
public void runMethod(){
String anyString = new String();
while(isContinueRun){
}
System.out.println("停下来了!");
}
public void stopMethod(){
isContinueRun = false;
}
}
线程A,B如下:
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service){
super();
this.service = service;
}
@Override
public void run(){
service.runMethod();
}
}
在线程B中,我们运行stopMethod方法。
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service){
super();
this.service = service;
}
@Override
public void run(){
service.stopMethod();
}
}
public class Run {
public static void main(String[] args) {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ThreadB b = new ThreadB(service);
b.start();
System.out.println("已经发起停止的命令了!");
}
}
运行后就会发现根本停不下来。造成这个结果的原因是各线程间的数据值没有可见性造成的,我们可以将isContinueRun变量改为volatile类型,或者通过如下的方式实现。
将runMethod()方法改为如下后,就会发现线程A会停止下来。
public void runMethod(){
String anyString = new String();
while(isContinueRun){
synchronized (anyString) {
}
}
System.out.println("停下来了!");
}