项目主体源码可以从ConcurrenceBasics获得,喜欢的朋友可以点个star~。
参考文献在参考资料部分有介绍。
/**
* synchronized关键字
* 对某个对象加锁
*/
public class T {
private int count = 10;
private Object o = new Object();
public void m(){
//任何线程要执行下面代码,必须先拿到o锁
synchronized (o){
count--;
System.out.println(Thread.currentThread().getName() + " count = " + count);
}
}
}
/**
* synchronized关键字
* 对某个对象加锁
*/
public class T {
private int count = 10;
public void m(){
//任何线程要执行下面的代码,必须先拿到this的锁
synchronized (this){
count--;
System.out.println(Thread.currentThread().getName() + " count = " + count);
}
}
}
/**
* synchronized关键字
* 对某个对象加锁
*/
public class T {
private int count = 10;
//同等于在方法的代码执行要synchronized(this)
public synchronized void m(){
count--;
System.out.println(Thread.currentThread().getName() + " count = " + count);
}
}
/**
* synchronized关键字
* 对某个对象加锁
*/
public class T {
private static int count = 10;
//这里等同于synchronized(xxx.T.class)
public synchronized static void m(){
count--;
System.out.println(Thread.currentThread().getName() + " count = " + count);
}
public static void ms(){
synchronized (T.class){
count--;
}
}
}
/**
* 实验结果:在run方法如果不加上synchronized,会产生线程抢占,
* 如果加上就确保了线程运行的原子性,先执行的执行,后执行的等待。
*/
public class T implements Runnable {
private int count = 10;
@Override
public /*synchronized*/ void run() {
count--;
System.out.println(Thread.currentThread().getName() + " count = " + count);
}
public static void main(String[] args) {
T t = new T();
for (int i = 0; i < 5; i++){
new Thread(t, "Thread" + i).start();
}
}
}
/**
* 实验结果:此案例说明加synchronized修饰后的线程对象,
* 和不加synchronized修饰,两者同时运行并不冲突。
*/
public class T {
public synchronized void m1(){
System.out.println(Thread.currentThread().getName() + " m1 start...");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " m1 end...");
}
public void m2(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " m2...");
}
public static void main(String[] args) {
T t = new T();
//语法有点叼
new Thread(()->t.m1(),"t1").start();
new Thread(()->t.m2(),"t2").start();
//和上面结果一样
//new Thread(t::m1, "t1").start();
//new Thread(t::m2, "t2").start();
}
}
/**
* 脏读
* 实验结果:实验结果为0.0。
* 解决方法:确保读写都加上synchronized,保证原子性。
*/
public class Account {
String name;
double balance;
public synchronized void set(String name, double balance){
this.name = name;
//这里加上间隔时间的意义在于模拟线程被其他线程进行调用
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.balance = balance;
}
public /*synchronized*/ double getBalance(String name){
return this.balance;
}
public static void main(String[] args) {
Account account = new Account();
new Thread(()->account.set("xipiker", 21.0)).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(account.getBalance("xipiker"));
}
}
/**
* 一个同步方法可以调用另一个同步方法,一个线程已经拥有某个对象的锁,
* 再次申请的时候仍然会得到该兑现的锁。也就是说synchronized获得的
* 锁是可重入的。
*/
public class T {
synchronized void m1(){
System.out.println("m1 start...");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
m2();
}
synchronized void m2(){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m2 start...");
}
public static void main(String[] args) {
T t = new T();
new Thread(()->t.m1(), "t1").start();
}
}
/**
* 子类调用父类的同步方法
*/
public class T {
synchronized void m(){
System.out.println("m start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m end");
}
public static void main(String[] args) {
new TT().m();
}
}
class TT extends T{
synchronized void m(){
System.out.println("child m start");
super.m();
System.out.println("child m end");
}
}
/**
* 程序在执行过程中,如果出现异常,默认情况下锁会被释放。
* 所以,在并发处理过程中,有异常要多加小心,不然可能会发生不一致情况。
* 比如,在一个web app处理过程中,多个servlet线程共同访问同一个资源,
* 这时候如果异常处理不合适,在第一个线程中抛出异常,其他线程就会进入同步代码区,
* 有可能会访问到异常产生时的数据。因此要非常小心的处理同步业务逻辑中的异常。
* 实验结果:锁被释放了,原子性不存在了,第二个线程接着第一个线程继续执行。
*/
public class T {
int count = 0;
synchronized void m(){
System.out.println(Thread.currentThread().getName() + " start...");
while (true){
count++;
System.out.println(Thread.currentThread().getName() + " count = " + count);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//这里我们模拟异常
//此处抛出异常,锁将释放,要想不被释放,可以在这里进行catch,然后让循环继续
if (count == 5){
int i = 1/0;
}
}
}
public static void main(String[] args) {
T t = new T();
Runnable r = new Runnable() {
@Override
public void run() {
t.m();
}
};
new Thread(r, "t1").start();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(r, "t2").start();
}
}
/**
* volatile关键字,使一个变量在多个线程间可见。
* 例子: A B线程都用到一个变量,java默认是A线程中保留一份copy,这样如果B线程修改了该变量,则A线程未必知道
* 使用volatile关键字,会让所有线程都会读到变量的赋值。
*
* 在下面代码中running是存在于堆内存的t对象中,当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,
* 在运行过程中直接使用这个copy,并不会每次都去读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行。
* 使用volatile,将会强制所有线程都去堆内存中读取running的值。
* volatile并不能保证多个线程共同修改running变量时所带来的不一致问题,也就是说volatile不能替代synchronized。
*/
public class T {
volatile boolean running = true;
void m(){
System.out.println("m start...");
while (running){
}
System.out.println("m end!");
}
public static void main(String[] args) {
T t = new T();
new Thread(t::m, "t1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.running = false;
}
}
/**
* volatile并不能保证多个线程共同修改running变量时所带来的不一致问题,也就是说volatile不能代替synchronized。
* 实验结果:多个线程间比如A线程执行中的count为100,B线程计算的A的count在加一为101,然后轮到A执行时他的count值时应该count结果为102,
* 但是执行了为101,所以原本count的102值又被清回101。
*/
public class T {
volatile int count = 5;
void m(){
for(int i = 0; i < 1000; i++)
count++;
}
public static void main(String[] args) {
T t = new T();
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 10; i++){
threads.add(new Thread(t::m, "Thread-" + i));
}
threads.forEach((o)->o.start());
threads.forEach((o)->{
try {
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t.count);
}
}
/**
* 确保变量的原子性,更加高效的方法可以使用AtomXXX类。
* AtomXXX类本身方法都是原子性,但不能保证多个方法连续调用是原子性。
*/
public class T {
AtomicInteger count = new AtomicInteger(0);
void m(){
for (int i = 0; i < 10000; i++)
//if count.get() < 1000 ->这里如果执行那么就不在具有原子性了,
// 因为可能会有线程执行到count.get() < 1000里面去,而又有的线程执行了下面的++操作。
count.incrementAndGet(); //count++
}
public static void main(String[] args) {
T t = new T();
List<Thread> threadList = new ArrayList<Thread>();
for (int i = 0; i < 10; i++){
threadList.add(new Thread(t::m, "thread-" + i));
}
threadList.forEach((o)->o.start());
threadList.forEach((o)->{
try {
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t.count);
}
}