synchronized关键字用于多线程同步,其具有以下作用:
1. 确保线程互斥的访问同步代码
2. 保证共享变量的修改能够及时可见
3. 有效解决重排序问题
其主要有三种用法:
1. 修饰普通方法
2. 修饰静态方法
3. 修饰代码块
测试代码:
public class C{
private static ArrayList list=new ArrayList<>();
public void noSyn() {
for(int i=0;i<5;++i){
System.out.println(Thread.currentThread().getName()+" "+"no Syn");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
public synchronized void normalSyn1() {
for(int i=0;i<5;++i){
System.out.println(Thread.currentThread().getName()+" "+"normal Syn1 ");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
public static void noSynStatic() {
for(int i=0;i<5;++i){
System.out.println(Thread.currentThread().getName()+" "+"no Syn static");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
public static synchronized void staticSyn() {
for(int i=0;i<5;++i){
//list.add(i);
System.out.println(Thread.currentThread().getName()+" "+"static syn "+list);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
public void staticString1(){
synchronized (list) {
for(int i=0;i<5;++i){
list=new ArrayList<>();
System.out.println(Thread.currentThread().getName()+" "+"static integer1 "+list);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
public void staticString2(){
synchronized (list) {
for(int i=0;i<5;++i){
System.out.println(Thread.currentThread().getName()+" "+"static integer2 "+list);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
public synchronized void normalSyn2() {
for(int i=0;i<5;++i){
System.out.println(Thread.currentThread().getName()+" "+"normal Syn2");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
其特点如下:
1.synchronized修饰普通方法methodA时,在不同线程中使用同一对象调用方法methodA,这样会导致先获取该对象锁的线程先执行,其他线程受阻塞。
public static void main(String []ags) throws Exception{
final C c=new C();
final C c1=new C();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.normalSyn();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.normalSyn();
}
}).start();
}
2.synchronized修饰普通方法methodA、methodB时,在不同线程中使用同一对象。在线程A中调用methodA,在线程B中调用methodB,这样会导致先获取对象锁的线程先执行,其他线程受阻塞。这表明,synchronized修饰普通方式时,锁住的是对象。当某一个线程获取到该对象锁时,其他线程就不能执行需要该对象锁的方法(即synchronized修饰的普通方法,代码块上为this的synchronized代码块)
public static void main(String []ags) throws Exception{
final C c=new C();
final C c1=new C();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.normalSyn1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.normalSyn2();
}
}).start();
}
3.synchronized修饰普通方法methodA时,在不同线程中使用不同对象调用该方法,这样会导致各线程按CPU调度进行,没有发送阻塞。这表明,不同对象有不同的对象锁,互不影响。
public static void main(String []ags) throws Exception{
final C c=new C();
final C c1=new C();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.normalSyn1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c1.normalSyn1();
}
}).start();
4.synchronized修饰普通方法methodA时,在不同线程中同一对象,在线程A中调用methodA,在线程B中调用非synchronized的方法methodB,这样会导致各线程按CPU调度进行,没有发送阻塞。这表明,没有锁的方法不会受到对象锁影响。
public static void main(String []ags) throws Exception{
final C c=new C();
final C c1=new C();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.normalSyn1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.noSyn();
}
}).start();
}
5.synchronized修饰静态方法时,在不同线程中使用该类的不同对象调用该方法时,这样会导致先获取类锁的线程先执行,其他线程阻塞。
public static void main(String []ags) throws Exception{
final C c=new C();
final C c1=new C();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.staticSyn();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c1.staticSyn();
}
}).start();
}
6.synchronized修饰普通方法methodA、静态方法methodB时,在不同线程中使用同一对象。在线程A中调用methodA,在线程B中调用methodB,这样会导致线程按CPU调度进行,各不影响。因为普通方法锁的是对象,静态方法锁的是类,两者互不干扰。
public static void main(String []ags) throws Exception{
final C c=new C();
final C c1=new C();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.staticSyn();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.normalSyn1();
}
}).start();
}
7.在方法methodA中用synchronized修饰代码块上的静态变量,synchronized修饰静态方法methodB,在不同线程中使用同一对象。在线程A中调用methodA,在线程B中调用methodB,这样会导致线程按CPU调度进行,各不影响。这表明虽然synchronized修饰的是静态变量,但它锁的是该变量;而synchronized修饰静态方式锁的是类,这样它们还是会互不影响。所以在synchronized锁变量时,必须要注意其在其他方法中的控制。
C类中方法staticSyn有所改变:
public static synchronized void staticSyn() {
for(int i=0;i<5;++i){
System.out.println(Thread.currentThread().getName()+" "+"static syn "+list);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
public static void main(String []ags) throws Exception{
final C c=new C();
final C c1=new C();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.staticString2();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.staticSyn();
}
}).start();
}
8.在方法methodA中用synchronized修饰代码块上的静态变量,在方法methodB中用synchronized修饰代码块上的同样的静态变量,在不同线程中使用同一对象。在线程A中调用methodA,在线程B中调用methodB,这样会导致先获取类锁的线程先执行,其他线程阻塞。前提是在先执行的线程中没有改变该静态变量的内存地址。若改变了,那么线程会按CPU调度运行。因为synchronized锁的是变量的那个内存位置,若后面改变了该变量的内存位置,在其他方法中比对当前位置与被锁位置不一致,则会执行。
public static void main(String []ags) throws Exception{
final C c=new C();
final C c1=new C();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.staticString1();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.staticString2();
}
}).start();
}
顺序改变一下:(先执行的线程没有改变地址)
public static void main(String []ags) throws Exception{
final C c=new C();
final C c1=new C();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.staticString2();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
// TODO 自动生成的方法存根
c.staticString1();
}
}).start();
}