天天看点

多线程——锁/单例设计模式之懒汉式(线程安全)/通信

释放锁的操作

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();
        }
    }
}