Java04 线程同步问题解决——线程锁(同步锁、互斥锁)

目录

写在最前:

可能有误,请大家批评指正

一、线程切换

Java中,如果要实现在一个线程间的线程切换,需要在线程中使用Thread.yield()即可让出CPU时间。

二、线程锁(也叫同步锁、互斥锁)

线程锁可以在有效缩小同步范围的同时,尽可能的保证并发效率

2.1 使用synchronized关键字对方法进行加锁

对整个线程处理加锁(严重影响效率,不常用

2.1.1 语法

public synchronized void test(){
    
}

2.1.2 案例

package com.javase.thread;

import javax.management.RuntimeErrorException;

/**
 * 		这个类主要讲了Sychronized关键字,给方法加了Synchronized关键字以后,线程在调用这个方法时,相当于对这个方法加了锁,
 * 那么其他线程就不能调用这个方法了(处于阻塞状态)
 * 
 * @author gupan
 *
 */
public class ThreadSyncSychronized {
	public static void main(String[] args) {
		final Table2 table = new Table2();
		Thread t1 = new Thread() {
			public void run() {
				while (true) {
					try {
						int bean = table.getBean();
						Thread.yield(); // 线程切换语句,让出CPU时间
						System.out.println(getName() + ","  + table.getBean());
					} catch (RuntimeErrorException e) {
						System.out.println(getName() + ","  + e);
						break;
					}
					
				}
			}
		};
		
		Thread t2 = new Thread() {
			public void run() {
				while (true) {
					try {
						int bean = table.getBean();
						Thread.yield(); // 线程切换语句,让出CPU时间
						System.out.println(getName() + ","  + table.getBean());
					} catch (RuntimeErrorException e) {
						System.out.println(getName() + "," + e);
						break;
					}
				}
			}
		};
		t2.start();
		t1.start();
	}
}

class Table2{
	// 桌子上有20元钱
	private int beans = 20;
	
	public synchronized int getBean() throws RuntimeErrorException{
		if (this.beans == 1) {
			throw new RuntimeErrorException(null, "地主家没有余粮了");
		}
		Thread.yield(); // 线程切换语句,让出CPU时间
		return this.beans--;
	}
}

2.2 使用synchronize关键字对线程方法中的某一部分加锁(同步块的方式)

2.2.1 语法

// 注意这里是this,可以写new Object(),但是这样起不到加锁的效果
// 也就是说,要实现加锁的效果,需要保证是对同一个对象(也就是保证synchronized后面所跟对象是同一个)加锁
synchronized(this){
    ···
    // 加锁语句
}

2.2.2 案例

package com.javase.thread;
/**
 * 这个类主要演示小范围的使用锁。尽可能的提高并发效率
 * 
 * synchronized(this){
 * 		···
 * 		// 加锁语句
 * }
 * 
 * @author Think
 *
 */
public class ThreadSyncLock {
	public static void main(String[] args) {
		final Shop shop = new Shop();
		Thread t1 = new Thread() {
			public void run() {
				shop.buy();
			}
		};
		
		Thread t2 = new Thread() {
			public void run() {
				shop.buy();
			}
		};
		
		t1.start();
		t2.start();
	}
}

class Shop{
	public void buy() {
		Thread t = Thread.currentThread();
		try {
			System.out.println(t.getName() + "正在挑衣服");
			Thread.sleep(1000);
			
			// 需要传入当前方法所属对象,所以这里要传入this
			synchronized (this) {
				System.out.println(t.getName() + "正在试衣服");
				Thread.sleep(1000);
			}
			
			System.out.println(t.getName() + "结账离开");
			Thread.sleep(1000);
		}catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
}

2.3 静态方法加锁

如果对静态方法加了synchronized关键字,由于静态方法只有一份,整个方法一定是加了互斥锁

package com.javase.thread;

import com.javase.string.Object;

/**
 * 静态方法的同步
 * 		当一个静态方法被synchronized修饰以后,那么该方法就是同步方法,由于静态方法从属类,
 * 全局就一份,所以同步的静态方法一定具有同步效果,与对象无关
 * 
 * @author gupan
 *
 */
public class ThreadSyncStatic {	
	public static void main(String[] args) {
		Thread t1 = new Thread() {
			public void run() {
				Foo.dosome();
			}
		};
		Thread t2 = new Thread() {
			public void run() {
				Foo.dosome();
			}
		};
		t1.start();
		t2.start();
	}
}


class Foo{
	public static synchronized void dosome() {
		try {
			Thread t = Thread.currentThread();
			System.out.println(t.getName() + "正在等待运行dosome方法");
			Thread.sleep(1000);
			
			System.out.println(t.getName() + "正在运行dosome方法");
			Thread.sleep(1000);
			
			System.out.println(t.getName() + "执行dosome方法完毕");
		}catch (Exception e) {
			// TODO: handle exception
		}
	}
}

运行结果:

Thread-1正在等待运行dosome方法
Thread-1正在运行dosome方法
Thread-1执行dosome方法完毕
Thread-0正在等待运行dosome方法
Thread-0正在运行dosome方法
Thread-0执行dosome方法完毕

2.3 互斥锁

2.3.1 同步锁和互斥锁

同步锁和互斥锁原理相同,存在的是用法上的小差异。当两个线程调用同一段代码,并且,对于两个线程的同步监视器,看到的代码相同,那就是同步锁;但是,对于几段代码,用一个同步监视器进行访问,几段代码不能同时执行,就是互斥锁

package com.javase.thread;
/**
 * 这段代码主要演示互斥锁的使用
 * 		使用synchronized修饰这段代码之后,只要他们同步监视器对象相同,那么这几段代码见就是互斥关系,多个线程不能同时执行这些代码
 * 
 * @author gupan
 *
 */
public class ThreadSyncMatual {
	/**
	 * 线程t1和t2不能同时调用methodA或methodB方法,实现互斥关系
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		Boo boo = new Boo();
		Thread t1 = new Thread() {
			public void run() {
				boo.methodA();
			}
		};
		
		Thread t2 = new Thread() {
			public void run() {
				boo.methodB();
			}
		};
		
		t1.start();
		t2.start();
	}
}

class Boo{
	public void methodA(){
		try{
			Thread t = Thread.currentThread();
			System.out.println(t.getName() + "正在执行A方法");
			Thread.sleep(1000);
			System.out.println(t.getName() + "执行A方法完毕");
		}catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
	
	public void methodB(){
		try{
			Thread t = Thread.currentThread();
			System.out.println(t.getName() + "正在执行B方法");
			Thread.sleep(1000);
			System.out.println(t.getName() + "执行B方法完毕");
		}catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
		}
	}
}
原文地址:https://www.cnblogs.com/gupan/p/9126082.html