初步学习Java并发中的锁机制

锁的功能

在java并发中,加锁可以保证线程的安全性,解决竞态条件问题。

竞态条件

某个计算的正确性取决于多个线程的交替执行时序。

常见类型:1. 先检查后执行(包括延时初始化)

                  2. 读取 – 修改 – 写入 操作

几种synchronized关键字的使用方式

1. 加在函数上

public class Test {

	private int count;

	public static void main(String[] args) {
		new Test().run();
	}

	private void run() {
		for (int i = 0; i < 1000; i++)
			new Thread() {
				@Override
				public void run() {
					addCount();
				}
			}.start();
		System.out.println("count = " + count);
	}

	private synchronized void addCount() {
		count++;
	}

}

这个锁加在了addCount函数前面,锁定的是Test实例出的对象

如果使用AtomicInteger,就不需要加锁操作

2. synchronized 代码块

    1. synchronized(this){} 锁定对象

    2. synchronized(A.class){} 锁定Test.class实例出的所有对象

public class Test2 {

	private int count;

	public static void main(String[] args) {
		new Test2().run();
	}

	private void run() {
		for (int i = 0; i < 3; i++)
			new Thread() {
				@Override
				public void run() {
					new A().print(getName());
				}
			}.start();
	}

}

class A {
	public void print(String threadName) {
		synchronized (A.class) {
			for (int i = 0; i < 5; i++)
				System.out.println(threadName + "  " + i);
		}
	}
}

虽然在Test2.run中,每个线程持有的是不同的对象,但是由于锁定的是class,输出是

      Thread-0  0
      Thread-0  1
      Thread-0  2
      Thread-0  3
      Thread-0  4
      Thread-2  0
      Thread-2  1
      Thread-2  2
      Thread-2  3
      Thread-2  4
      Thread-1  0
      Thread-1  1
      Thread-1  2
      Thread-1  3
      Thread-1  4

    3.   synchronized(某个变量){}

          在这种情况下,代码块会调用变量内部的锁,因此这个变量不能是原始类型(int,double等)

public class Test2 {
	private A a;
 
	public static void main(String[] args) {
		new Test2().run();
	}
 
	private void run() {
		a = new A();
		for (int i = 0; i < 1000; i++)
			new Thread() {
				@Override
				public void run() {
					addCount();
					System.out.println(a.get());
				}
			}.start();
	}
	private void addCount() {
		synchronized(a) { // 使用的是a的锁
			a.addCount();
		}
	}
}

class A {
	private int count;
	public void addCount() {
		count++;
	}
	public int get() {
		return count;
	}
}

死锁

当一个线程获取到锁后始终不退出时,其他的线程就只能一直等待

public class Test {
	private A a = new A();

	public static void main(String[] args) {
		new Test().run();
	}

	private void run() {
		new Thread() {
			@Override
			public void run() {
				System.out.println("Thread A run");
				a.printA();
			}
		}.start();

		new Thread() {
			@Override
			public void run() {
				System.out.println("Thread B sleep");
				try {
					sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("Thread B try printB");
				a.printB();
			}
		}.start();
	}
}

class A {

	public synchronized void printA() {
		while(true) {
			// 这里死循环,Thread A 获取锁后不退出
		}
	}

	public synchronized void printB() {
		System.out.println("A, printB");
	}

}

输出为:

Thread A run
Thread B sleep
Thread B try printB

这里给一个对比

public class Test {
	private A a = new A();

	public static void main(String[] args) {
		new Test().run();
	}

	private void run() {
		new Thread() {
			@Override
			public void run() {
				System.out.println("Thread A run");
                                while(true) {
					a.printA();
				}
			}
		}.start();

		new Thread() {
			@Override
			public void run() {
				System.out.println("Thread B sleep");
				try {
					sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("Thread B try printB");
				a.printB();
			}
		}.start();
	}
}

class A {

	public synchronized void printA() {
	}

	public synchronized void printB() {
		System.out.println("A, printB");
	}

}

修改的部分为Thread A run函数的实现和A class中的printA函数,即将printA函数中的死循环移到了Thread A run函数中

输出为:

Thread A run
Thread B sleep
Thread B try printB
A, printB

    原文作者:java锁
    原文地址: https://blog.csdn.net/heliumxx/article/details/75252270
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞