Java多线程二线程安全synchronized和Semaphore

线程的基本状态及其转换
跟进程的状态差不多,只是调度的单元从进程切换到线程在这里插入图片描述
blocked\waiting\timedwaiting都是阻塞状态
线程类的常用方法
sleep();
精度取决于系统计时器的精度
setPriority();
设置线程优先级,取值范围1-10
建议操作系统哪个线程优先级较高,但是不能100%确定高优先级就先运行或者先运行完成。

//与start类似
Thread t = new Thread();
t.setPriority(Thread.MAX_PRIORITY);

yield();
线程让步
join();
线程插队
setDaemon();
设置后台线程
如果把一个线程设置为后台线程,jvm不会管后台线程,所有的前台线程运行完之后jvm虚拟机直接退出。
线程安全
在这里插入图片描述在这里插入图片描述在这里插入图片描述举个简单的例子
AB两个线程

public class ThreadSynchronizedTest {

	public static void main(String[] args) {
		ThreadA a = new ThreadA();
		ThreadB b = new ThreadB();
		
		a.start();
		b.start();
	}

}
class ThreadA extends Thread{
	public void run() {
		for (int i = 0; i < 10; i++) {
			try {
				Thread.sleep(1000);//此时ThreadA进入中断状态CPU进行其他调度
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("线程A在执行");
		}	
	}
}
class ThreadB extends Thread{
	public void run() {
		for (int i = 0; i < 10; i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println("线程B在执行");
		}
	}
}

执行结果显而易见,ThreadA和ThreadB交替打印

public class ThreadSynchronizedTest {
	public static final Object ANYOBJECT = new Object();//静态的对象,运行时方便调用。
	public static void main(String[] args) {
		ThreadA a = new ThreadA();
		ThreadB b = new ThreadB();

		a.start();
		b.start();
	}
}
class ThreadA extends Thread {
	public void run() {
		//synchronized关键字获取锁的时候计数器+1,当运行完毕释放锁的时候,计数器-1
		synchronized (ThreadSynchronizedTest.ANYOBJECT) {
			for (int i = 0; i < 10; i++) {
				try {
					Thread.sleep(1000);/此时ThreadA进入中断状态CPU进行其他调度
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("线程A在执行");
			}
		}
	}
}
class ThreadB extends Thread {
	public void run() {
		synchronized (ThreadSynchronizedTest.ANYOBJECT) {
			for (int i = 0; i < 10; i++) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("线程B在执行");
			}
		}
	}
}

结果线程A先打印,谁先获取锁,谁先执行。正常情况下A获取锁后执行代码,但是遇到sleep的时候A则进入中断,此时CPU调度应该执行其他线程,但是因为A有锁,此时CPU调度到线程B,线程B无法获取锁,B进入阻塞状态,因为此时锁的状态是1,所以A继续执行
如果我们想要说我们想让一个线程执行的时候另外一个线程不执行,那么只需要让这两个线程,我们只需要对同一个对象进行加锁就行,至于这个对象具体是什么不重要,然后让这两个线程获取该对象的锁即可。获取到的先执行。
另外,如果当前线程已经拥有一个对象的锁,该线程可以再次尝试去获取这个锁,再+1,这是锁的可重录性。如果一个对象被锁定,再次加锁,会导致死锁的出现类似synchronized嵌套。
所以之前发成绩单的代码可以改成

public class ThreadClassT2 {
	public static void main(String[] args) {
		
		DistributeThread2 dt = new DistributeThread2();
		
		Thread t1 = new Thread(dt, "我");
		Thread t2 = new Thread(dt, "学生甲");
		Thread t3 = new Thread(dt, "学生乙");
		
		t1.start();
		t2.start();
		t3.start();
	}
}
class DistributeThread2 implements Runnable{
	//避免出现重复的情况
	private Object lock = new Object();
	int formCount = 100;
	
	public void run() {
		while (true) {//这里为什么要while true,因为如果按照之前的写法,获取锁之后再进入while(formCount > 0),那么第一个进入到这里面的线程将执行完之后再跳出while循环然后再释放锁,其他线程将无法进行while里面的操作
			synchronized (lock) {
				if (formCount > 0) {
					try {
						Thread.sleep(200);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"正在分发学生NO:" + formCount-- + "学生信息表格");
				}else {
					break;
				}
			}	
		}	
	}
}

如果大家都是同一个锁,那么可以用synchronized (this) ,这个叫synchronized块
我们也可以把synchronized写在方法前面,这样就编程synchronized方法,锁的仍然是this对象,总之还是要同一个锁,至于锁是什么类型,无所谓
如果synchronized修饰的是一个静态方法,那么它锁的对象是“静态方法所在类.class”对象,一旦这个方法获取了锁,那么这个类中所有被synchronized修饰的方法都进不去了。
死锁
在这里插入图片描述
上面的代码同样可以用信号量来代替synchronized来实现
public static void main(String[] args) {

	ThreadSemaphoreTest dt = new ThreadSemaphoreTest();
	
	Thread t1 = new Thread(dt, "我");
	Thread t2 = new Thread(dt, "学生甲");
	Thread t3 = new Thread(dt, "学生乙");
	
	t1.start();
	t2.start();
	t3.start();
}

}
class Signal{
public static final Semaphore LOCK = new Semaphore(1);
}

class ThreadSemaphoreTest implements Runnable{
//避免出现重复的情况
//private Object lock = new Object();
int formCount = 50;

public void run() {
	while (true) {
		try {
			Signal.LOCK.acquire();//用信号量来做
		} catch (InterruptedException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		if (formCount > 0) {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "正在分发学生NO:" + formCount-- + "学生信息表格");
		} else {
			break;
		}
		Signal.LOCK.release();
	}
}

}
在这里插入图片描述
ps:synchronized是隐式锁,看不到加锁和解锁的过程,jdk1.5concurrent并发包里面引入了ReentrantLock显示锁,有lock和unlock方法。
另外释放锁一定要放在finally里面。确保如果程序获得锁之后出现异常,也能释放锁给其他线程继续使用。
ReentrantLock中可以设置公平锁,即

private Lock lock = new ReentrantLock(true);//线程轮流执行

synchronized等待不可中断,ReentrantLock可以
可中断等待ReentrantLock可以让因为等待阻塞的线程不再继续等待
Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。乐观锁一般会使用版本号机制或CAS算法实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值