Java多线程-2(线程同步synchronized关键字)

为何要实现线程同步

多线程机制的目的是为了能并发执行程序,为何要实现线程间同步,使其看起来像是让每个线程按顺序执行呢?答案是:当线程之间没有“相交点”(共享资源)时,不需要线程同步,但是当操作某个线程间共享资源(如静态变量),就需要实现线程同步。

java提供的线程同步机制

一.synchronized关键字

1.当synchronized关键字修饰方法

  • a.大部分人熟悉的线程同步方式是上锁,而synchronized关键字实现同步的方式其实也是上锁。那么上锁就是要看给什么东西上锁,是方法还是对象,当synchronized关键字修饰方法时,是给调用该方法的对象上的锁(因为一个类有多个对象,通过哪个对象调用的方法就给哪个个对象上锁)。那么这个锁是什么锁呢,是每个对象都拥有的内置锁

  • b.使用synchronized关键字的例子:

/*
*	synMethrod.java文件
*	定义了一个synchronized方法methrod,该方法先打印一次字符串,然后sleep一秒,在打印一次字符串
*/
public class synMethrod{
	public synchronized void methrod(String arg) {
		System.out.println(arg + "start");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(arg + "end");
	}
}

***************************************************************
/*
*	ThradPractice.java文件
*/
import java.lang.*;
import java.util.*;

public class ThreadPractice{
	public static void main(String[] args) {
		synMethrod m = new synMethrod();
		Thread t1 = new MyThread(m,"Thread1");
		Thread t2 = new MyThread(m,"Thread2");
		t1.start();
		t2.start();
	}
}

class MyThread extends Thread{
	private synMethrod m; 
	private String s;
	public MyThread(synMethrod m,String s) {
		this.m = m;
		this.s = s;
	}
	@Override
	public void run() {
		m.methrod(s);	
	}
}

上述代码的运行结果:
在这里插入图片描述
可以看出,在线程1调用methrod方法后,线程2并没有马上进行methord方法,而是等线程1执行完methrod方法后,线程2才执行methrod方法。如果将methrod方法的synchronized关键字去掉,那么程序的运行结果如下:
在这里插入图片描述
可以看出此时线程1和线程2同时进入了methrod方法执行。

注意:对于上述代码,由于在java中,将对象作为参数时,是进行了引用传递,所以在线程1和线程2中的synMethrod对象是同一个对象,synchronized关键字是将该synMethrod对象的内置锁进行上锁

2.当synchronized关键字修饰静态方法
a.静态成员不属于任何对象,那么当synchronized关键字上的锁可以理解为类锁,当synchronized关键字修饰一般成员方法时上的锁可以理解为对象锁,属于某个对象。
b.下面将举一个例子,可以看出“类锁”和“对象锁不冲突”。在synMethrod类中新定义一个静态方法。线程1调用原methrod方法,线程2调用staticMethrod方法。如下:

/*
*	synMethrod.java文件
*	新定义了staticMethrod方法
*/
public class synMethrod{
	public void methrod(String arg) {
		System.out.println(arg + "start");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(arg + "end");
	}
	
	public static synchronized void staticMethord(String arg) {
		System.out.println(arg + "start (static)");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(arg + "end (static)");
	}
}

********************************************************************
/*
*	ThreadPractice.java
*	线程1执行一般成员方法,线程2执行静态成员方法
*/
import java.lang.*;
import java.util.*;

public class ThreadPractice{
	public static void main(String[] args) {
		synMethrod m = new synMethrod();
		Thread t1 = new MyThread(m,"Thread1");
		Thread t2 = new MyThread2(m,"Thread2");
		t1.start();
		t2.start();
		
	}
}

class MyThread extends Thread{
	private synMethrod m; 
	private String s;
	public MyThread(synMethrod m,String s) {
		this.m = m;
		this.s = s;
	}
	@Override
	public void run() {
		m.methrod(s);
	}
}

class MyThread2 extends Thread{
	private synMethrod m; 
	private String s;
	public MyThread2(synMethrod m,String s) {
		this.m = m;
		this.s = s;
	}
	@Override
	public void run() {
		m.staticMethord(s);
	}
}

上述代码的执行结果如下:
在这里插入图片描述
可以看出,线程1和线程2并没有实现同步,证明“类锁”和"对象锁"之间不存在互斥。
如果将static关键字删掉,上述程序运行结果如下:
在这里插入图片描述
从结果可以看出线程1和线程2实现了互斥,因为他们都使用了同一个对象的“对象锁”。

3.synchronized关键字修饰代码块
为了对线程同步进行更加精确地控制,可以synchronized关键字来修饰代码块。而synchronized关键字在修饰代码块时又分为两种类型,即使用“类锁”还是使用“对象锁”。

  • 使用类锁
    语法:
synchronized(类名.class){
//需要进行同步的代码块
}
  • 使用对象锁
    语法
synchronized(this){
//需要进行同步的代码块
}

例子如下:

/*
* synMethrod文件
*/
public class synMethrod{
	public void methrod(String arg) {
		//使用对象锁
		synchronized(this){
			System.out.println(arg + "start");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(arg + "end");
		}
	}
	
	public void staticMethord(String arg) {
		//使用类锁
		synchronized(synMethrod.class) {
			System.out.println(arg + "start (static)");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(arg + "end (static)");
		}
	}
}

总结:使用synchronized关键字进行现场同步时需要理解一下两点
1.synchronized关键字是通过上锁来实现了
2.synchronized关键字使用的锁分为“类锁”和“对象锁”,根据不同使用场景选择不同的锁(即synchronized关键字的使用方式)

你可能感兴趣的:(java学习路)