释放锁的操作
1:当前线程的同步方法、同步代码块执行结束。
2:当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行。
3:当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束。
4:当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。
不会释放锁的操作
1:线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行
2:线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)。
3:应尽量避免使用suspend()和resume()来控制线程
单例设计模式之懒汉式(线程安全)
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance == null){
instance=new Singleton();
} } }
return instance;
}
更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。
具体实现:
class A{
private final ReentrantLock lock = new ReenTrantLock();
public void m(){
lock.lock();
try{
//保证线程安全的代码;
}
finally{
lock.unlock();
}
}
}
synchronized 与 Lock 的对比
1:Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放
2: Lock只有代码块锁,synchronized有代码块锁和方法锁
3:使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
优先使用顺序:
Lock -》同步代码块(已经进入了方法体,分配了相应资源) ——》同步方法(在方法体之外)
wait() 与 notify() 和 notifyAll()
***wait():***令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行。
***notify()***:唤醒正在排队等待同步资源的线程中优先级最高者结束等待
***notifyAll ():***唤醒正在排队等待资源的所有线程结束等待.
说明:
- 1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
- 2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。否则,会出现IllegalMonitorStateException异常
- 3.wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。
线程通信的例子:
class Number implements Runnable{
private int number = 1;
private Object obj = new Object();
@Override
public void run() {
while(true){
synchronized (obj) {
obj.notify();//进入的线程启动正在等待的线程,实现交替输出,即完成了线程的通信。
if(number <= 100){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + number);
number++;
try {
//使得调用如下wait()方法的线程进入阻塞状态,锁打开。后面的线程进入同步代码块
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
面试题:sleep() 和 wait()的异同?
- 1.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
-
2.不同点:
1)两个方法声明的位置不同:Thread类中声明sleep() , Object类中声明wait()
2)调用的要求不同:sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中
3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。
经典例题:生产者/消费者问题
生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
示例
package view;
import service.Data;
import java.sql.SQLOutput;
import java.util.Date;
public class TeamView {
public static void main(String[] args) {
clerk clerk1 = new clerk();
Creater creater = new Creater(clerk1);
User user = new User(clerk1);
Thread creat = new Thread(creater);
Thread use = new Thread(user);
creat.start();
use.start();
}
}
class clerk{
public int product;
public synchronized void creat(){
if (product>=20){
try{
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else {
product++;
System.out.println("生产者目前产品有:"+product);
notifyAll();
}
}
public synchronized void use(){
if (product<=0){
try{
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else {
product--;
System.out.println("消耗者目前产品有:"+product);
notifyAll();
}
}
}
class Creater implements Runnable{
clerk p1;
public Creater(clerk clerk){
p1 = clerk;
}
@Override
public void run() {
System.out.println("生产者生产商品***************************");
while (true){
try {
Thread.sleep((int) (Math.random()*100));
} catch (InterruptedException e) {
e.printStackTrace();
}
p1.creat();
}
}
}
class User implements Runnable{
clerk p1;
public User(clerk clerk){
p1 = clerk;
}
@Override
public void run() {
System.out.println("消费者消耗商品***************************");
while (true){
try {
Thread.sleep((int) (Math.random()*500));
} catch (InterruptedException e) {
e.printStackTrace();
}
p1.use();
}
}
}