Java的多线程编程中常用的关键字是synchronized,当然也经常用到java.util.concurrent.locks包下的一些类,比如ReentrantLock,还有java.util.concurrent.atomic包下的以Atomic开头的类。这个后续有机会再来探讨。
synchronized关键字,它可以用于声明方法,也可以用于声明代码块,以下是具体场景。
synchronized关键字修饰方法
package thread;
public class SynchronizedTest {
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
Thread threadA = new ThreadA(demo);
Thread threadB = new ThreadB(demo);
threadA.setName("A");
threadB.setName("B");
threadA.start();
threadB.start();
}
}
class ThreadA extends Thread{
private SynchronizedDemo demo;
public ThreadA(SynchronizedDemo demo){
this.demo = demo;
}
@Override
public void run() {
try {
demo.foo1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
private SynchronizedDemo demo;
public ThreadB(SynchronizedDemo demo){
this.demo = demo;
}
@Override
public void run() {
try {
demo.foo1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class SynchronizedDemo{
public synchronized void foo1() throws InterruptedException{
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"进入方法foo1");
Thread.sleep(3000);
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"离开方法foo1");;
}
}
运行结果:
线程A在1477807162663进入方法foo1
线程A在1477807165665离开方法foo1
线程B在1477807165665进入方法foo1
线程B在1477807168665离开方法foo1
synchronized关键字修饰代码块
synchronized关键字作用在代码块效果和作用在方法上类似,区别是作用在代码块上的参数是自己设置的,可以根据自己的需求设置锁定的对象,当一个类中有很多个synchronized方法时,使用同步代码块锁定非this对象,则可以避免和其他的同步方法争抢this锁,提高运行效率。 synchronized(非this对象x)格式是将x对象本身作为“对象监视器”,所以当多个线程同时执行synchronized(x){}同步代码块和synchronized同步方法呈同步效果;当其他线程执行x对象方法里面的synchronized关键字的方法时,也呈同步效果。
public class SynchronizedTest {
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo("123");
Thread threadA = new ThreadA(demo);
Thread threadB = new ThreadB(demo);
threadA.setName("A");
threadB.setName("B");
threadA.start();
threadB.start();
}
}
class ThreadA extends Thread{
private SynchronizedDemo demo;
public ThreadA(SynchronizedDemo demo){
this.demo = demo;
}
@Override
public void run() {
try {
demo.foo2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
private SynchronizedDemo demo;
public ThreadB(SynchronizedDemo demo){
this.demo = demo;
}
@Override
public void run() {
try {
demo.foo2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class SynchronizedDemo{
private String obj;
public SynchronizedDemo(String str){
this.obj = str;
}
public void foo2() throws InterruptedException{
synchronized (obj){
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"进入方法foo2");
Thread.sleep(3000);
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"离开方法foo2");;
}
}
}
运行结果:
线程A在1477807251436进入方法foo2
线程A在1477807254437离开方法foo2
线程B在1477807254437进入方法foo2
线程B在1477807257438离开方法foo2
synchronized锁重入
关键字Synchronized拥有锁重入的功能,也就是使用synchronized时,当一个线程得到一个对象锁后,再次请求对象锁时是可以再次获得对象的锁的,这也证明看在一个synchronized方法/代码块内部调用同一个对象的其他synchronized方法/代码块时,是永远可以得到锁的。
public class SynchronizedTest {
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo("123");
Thread threadA = new ThreadA(demo);
Thread threadB = new ThreadB(demo);
threadA.setName("A");
threadB.setName("B");
threadA.start();
threadB.start();
}
}
class ThreadA extends Thread{
private SynchronizedDemo demo;
public ThreadA(SynchronizedDemo demo){
this.demo = demo;
}
@Override
public void run() {
try {
demo.foo1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
private SynchronizedDemo demo;
public ThreadB(SynchronizedDemo demo){
this.demo = demo;
}
@Override
public void run() {
try {
demo.foo1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class SynchronizedDemo{
private String obj;
public SynchronizedDemo(String str){
this.obj = str;
}
public synchronized void foo1() throws InterruptedException{
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"进入方法foo1");
foo2();
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"离开方法foo1");
}
public void foo2() throws InterruptedException{
synchronized (obj){
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"进入方法foo2");
Thread.sleep(3000);
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"离开方法foo2");;
}
}
}
运行结果:
线程A在1477807446886进入方法foo1
线程A在1477807446886进入方法foo2
线程A在1477807449887离开方法foo2
线程A在1477807449887离开方法foo1
线程B在1477807449887进入方法foo1
线程B在1477807449887进入方法foo2
线程B在1477807452887离开方法foo2
线程B在1477807452887离开方法foo1
出现异常,锁自动释放
当一个线程执行的代码出现异常时,其所持有的锁会自动释放,这个较好理解,线程出现异常后,线程会停止,其拥有的锁自然会释放,我们在实际开发过程中要停止线程,一般不用stop方法,而是用其他方式,常用的方式有中断线程,捕获异常来停止线程。
public class SynchronizedTest {
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo("123");
Thread threadA = new ThreadA(demo);
Thread threadB = new ThreadB(demo);
threadA.setName("A");
threadB.setName("B");
threadA.start();
threadB.start();
threadA.interrupt();
}
}
class ThreadA extends Thread{
private SynchronizedDemo demo;
public ThreadA(SynchronizedDemo demo){
this.demo = demo;
}
@Override
public void run() {
demo.foo1();
}
}
class ThreadB extends Thread{
private SynchronizedDemo demo;
public ThreadB(SynchronizedDemo demo){
this.demo = demo;
}
@Override
public void run() {
demo.foo1();
}
}
class SynchronizedDemo{
private String obj;
public SynchronizedDemo(String str){
this.obj = str;
}
public synchronized void foo1() {
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"进入方法foo1");
try{
foo2();
}catch(InterruptedException e){
System.out.println("线程"+Thread.currentThread().getName()+"中断了");
}
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"离开方法foo1");
}
public void foo2() throws InterruptedException{
synchronized (obj){
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"进入方法foo2");
Thread.sleep(3000);
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"离开方法foo2");;
}
}
}
运行结果:
线程A在1477807745022进入方法foo1
线程A在1477807745022进入方法foo2
线程A中断了
线程A在1477807745023离开方法foo1
线程B在1477807745023进入方法foo1
线程B在1477807745023进入方法foo2
线程B在1477807748024离开方法foo2
线程B在1477807748024离开方法foo1
同步不具有继承性
同步不可以继承,因此我们重写父类的synchronized方法时也要加上synchronized关键字,子类的方法才能实现同步。
静态同步synchronized方法与synchronized(class)代码块
关键字synchronized应用在static静态方法上,则是对Class类进行加锁,它与Class对应的对象锁不是同一个锁,Class锁可以对类的所有对象实例起作用,即同一个类的两个不同对象也是互斥的。
public class SynchronizedTest {
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
Thread threadA = new ThreadA(demo);
Thread threadB = new ThreadB(demo);
threadA.setName("A");
threadB.setName("B");
threadA.start();
threadB.start();
}
}
class ThreadA extends Thread{
private SynchronizedDemo demo;
public ThreadA(SynchronizedDemo demo){
this.demo = demo;
}
@Override
public void run() {
try {
demo.foo1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
private SynchronizedDemo demo;
public ThreadB(SynchronizedDemo demo){
this.demo = demo;
}
@Override
public void run() {
try {
SynchronizedDemo.foo2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class SynchronizedDemo{
public synchronized void foo1() throws InterruptedException {
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"进入方法foo1");
Thread.sleep(3000);
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"离开方法foo1");
}
public static synchronized void foo2() throws InterruptedException{
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"进入方法foo2");
Thread.sleep(3000);
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"离开方法foo2");;
}
}
运行结果:
线程A在1477808224594进入方法foo1
线程B在1477808224594进入方法foo2
线程B在1477808227595离开方法foo2
线程A在1477808227595离开方法foo1
public class SynchronizedTest {
public static void main(String[] args) {
SynchronizedDemo demo = new SynchronizedDemo();
SynchronizedDemo demo1 = new SynchronizedDemo();
Thread threadA = new ThreadA(demo);
Thread threadB = new ThreadB(demo1);
threadA.setName("A");
threadB.setName("B");
threadA.start();
threadB.start();
}
}
class ThreadA extends Thread{
private SynchronizedDemo demo;
public ThreadA(SynchronizedDemo demo){
this.demo = demo;
}
@Override
public void run() {
try {
demo.foo2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadB extends Thread{
private SynchronizedDemo demo;
public ThreadB(SynchronizedDemo demo){
this.demo = demo;
}
@Override
public void run() {
try {
demo.foo2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class SynchronizedDemo{
public void foo1() throws InterruptedException {
synchronized(SynchronizedDemo.class){
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"进入方法foo1");
Thread.sleep(3000);
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"离开方法foo1");
}
}
public void foo2() throws InterruptedException{
synchronized(SynchronizedDemo.class){
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"进入方法foo2");
Thread.sleep(3000);
System.out.println("线程"+Thread.currentThread().getName()+"在"+ System.currentTimeMillis()
+"离开方法foo2");;
}
}
}
运行结果:
线程A在1477808373225进入方法foo2
线程A在1477808376226离开方法foo2
线程B在1477808376226进入方法foo2
线程B在1477808379227离开方法foo2