多線程(六)

1.lockjava

用lock實現同步:dom

public class MyService {
    private Lock lock=new ReentrantLock();
    public void testMethod(){
        lock.lock();
        for (int i = 0; i <5; i++) {
            System.out.println("ThreadName="+Thread.currentThread().getName()+"("+(i+1)+")");
        }
        lock.unlock();
    }
}
public class MyThread extends Thread{
    private MyService myService;
    public MyThread(MyService myService){
        this.myService=myService;
    }

    @Override
    public void run() {
        myService.testMethod();
    }
}
public class Run {
    public static void main(String[] args) {
        MyService service=new MyService();
        MyThread a1=new MyThread(service);
        MyThread a2=new MyThread(service);
        MyThread a3=new MyThread(service);
        MyThread a4=new MyThread(service);
        MyThread a5=new MyThread(service);
        a1.start();
        a2.start();
        a3.start();
        a4.start();
        a5.start();
    }
/*ThreadName=Thread-0(1)
ThreadName=Thread-0(2)
ThreadName=Thread-0(3)
ThreadName=Thread-0(4)
ThreadName=Thread-0(5)
ThreadName=Thread-2(1)
ThreadName=Thread-2(2)
ThreadName=Thread-2(3)
ThreadName=Thread-2(4)
ThreadName=Thread-2(5)
ThreadName=Thread-4(1)
ThreadName=Thread-4(2)
ThreadName=Thread-4(3)
ThreadName=Thread-4(4)
ThreadName=Thread-4(5)
ThreadName=Thread-1(1)
ThreadName=Thread-1(2)
ThreadName=Thread-1(3)
ThreadName=Thread-1(4)
ThreadName=Thread-1(5)
ThreadName=Thread-3(1)
ThreadName=Thread-3(2)
ThreadName=Thread-3(3)
ThreadName=Thread-3(4)
ThreadName=Thread-3(5)
*/實現了同步
}

2.關鍵字synchronized與wait()和notify/notifyAll()方法相結合但是實現等待/通知模式,類ReentrantLock也能夠實現一樣的功能,但須要藉助Condition對象.ide

notify是隨機通知的,可是類ReentrantLock結合Condition類是能夠實現"選擇性通知的"ui

public class ThreadA extends Thread{
    private MyService myService;
    public ThreadA(MyService myService){
        this.myService=myService;
    }

    @Override
    public void run() {
        myService.await();
    }
}
public class MyService {
    private Lock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();//對象監視器
    public void await(){
        try {
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Run {
    public static void main(String[] args) {
        MyService service=new MyService();
        ThreadA a =new ThreadA(service);
        a.start();
    }
/*Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
    at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:155)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1260)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1723)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2040)
    at lock.MyService.await(MyService.java:15)
    at lock.ThreadA.run(ThreadA.java:14)
*/
}

總結:監視器報錯,解決方法是在condition.await()方法調用以前調用lock.lock();this

修改:線程

public class MyService {
    private Lock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();//對象監視器
    public void await(){
        try {
            lock.lock();
            System.out.println("A");
            condition.await();
            System.out.println("B");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
/*OUTPUT:A
*/
}

lock的喚醒實現:對象

public class ThreadA extends Thread{
    private MyService myService;
    public ThreadA(MyService myService){
        this.myService=myService;
    }

    @Override
    public void run() {
        myService.await();
    }
}
public class MyService {
    private Lock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();//對象監視器
    public void await(){
        try {
            lock.lock();
            System.out.println("await 時間爲"+System.currentTimeMillis());
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void signal(){
        try{
            lock.lock();
            System.out.println("signal時間爲"+System.currentTimeMillis());
            condition.signal();
        }finally {
            lock.unlock();
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyService service=new MyService();
        ThreadA a =new ThreadA(service);
        a.start();
        Thread.sleep(3000);
        service.signal();
    }
/*
await 時間爲1516676645558
signal時間爲1516676648557
*/
}

4.多個condition實現通知所有線程get

public class MyService {
    private Lock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();//對象監視器
    public void awaitA(){
        try {
            lock.lock();
            System.out.println("begin awaitA 時間爲"+System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
            condition.await();
            System.out.println("end awaitA 時間爲"+System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void awaitB(){
        try {
            lock.lock();
            System.out.println("begin awaitB 時間爲"+System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
            condition.await();
            System.out.println("end awaitB 時間爲"+System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void signalAll(){
        try{
            lock.lock();
            System.out.println("signalAll時間爲"+System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
            condition.signalAll();
        }finally {
            lock.unlock();
        }
    }
}
public class ThreadA extends Thread{
    private MyService myService;
    public ThreadA(MyService myService){
        this.myService=myService;
    }

    @Override
    public void run() {
        myService.awaitA();
    }
}
public class ThreadB extends Thread{
    private MyService myService;
    public ThreadB(MyService myService){
        this.myService=myService;
    }

    @Override
    public void run() {
        myService.awaitB();
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyService service=new MyService();
        ThreadA a =new ThreadA(service);
        a.setName("A");
        a.start();
        ThreadB b =new ThreadB(service);
        b.setName("B");
        b.start();
        Thread.sleep(3000);
        service.signalAll();
    }
}

5.多個condition實現通知部分線程同步

public class MyService {
    private Lock lock=new ReentrantLock();
    private Condition conditionA=lock.newCondition();//A對象監視器
    private Condition conditionB=lock.newCondition();//B對象監視器
    public void awaitA(){
        try {
            lock.lock();
            System.out.println("begin awaitA 時間爲"+System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
            conditionA.await();
            System.out.println("end awaitA 時間爲"+System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void awaitB(){
        try {
            lock.lock();
            System.out.println("begin awaitB 時間爲"+System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
            conditionB.await();
            System.out.println("end awaitB 時間爲"+System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void signalAll_A(){
        try{
            lock.lock();
            System.out.println("signalAll時間爲"+System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
            conditionA.signalAll();
        }finally {
            lock.unlock();
        }
    }
    public void signalAll_B(){
        try{
            lock.lock();
            System.out.println("signalAll時間爲"+System.currentTimeMillis()+" ThreadName="+Thread.currentThread().getName());
            conditionB.signalAll();
        }finally {
            lock.unlock();
        }
    }
}
public class ThreadA extends Thread{
    private MyService myService;
    public ThreadA(MyService myService){
        this.myService=myService;
    }

    @Override
    public void run() {
        myService.awaitA();
    }
}
public class ThreadB extends Thread{
    private MyService myService;
    public ThreadB(MyService myService){
        this.myService=myService;
    }

    @Override
    public void run() {
        myService.awaitB();
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyService service=new MyService();
        ThreadA a =new ThreadA(service);
        a.setName("A");
        a.start();
        ThreadB b =new ThreadB(service);
        b.setName("B");
        b.start();
        Thread.sleep(3000);
        service.signalAll_A();
    }
}

6.實現生產者消費者模式:一對一交替打印it

public class MyService {
    private Lock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();//對象監視器
    private boolean hasValue=false;
    public void set(){
        try{
            lock.lock();
            while (hasValue==true){
                condition.await();
            }
            System.out.println("打印*");
            hasValue=true;
            condition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void get(){
        try {
            lock.lock();
            while(hasValue==false){
                condition.await();
            }
            System.out.println("打印-");
            hasValue=false;
            condition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class ThreadA extends Thread{
    private MyService myService;
    public ThreadA(MyService myService){
        this.myService=myService;
    }

    @Override
    public void run() {
        for (int i = 0; i <Integer.MAX_VALUE ; i++) {
            myService.set();
        }
    }
}
public class ThreadB extends Thread{
    private MyService myService;
    public ThreadB(MyService myService){
        this.myService=myService;
    }

    @Override
    public void run() {
        for (int i = 0; i <Integer.MAX_VALUE ; i++) {
            myService.get();
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyService service=new MyService();
        ThreadA a =new ThreadA(service);
        a.start();
        ThreadB b =new ThreadB(service);
        b.start();
    }
/*
打印-
打印*
打印-
打印*
打印-
打印*
*/
}

7.實現生產者消費者模式:多對多交替打印

public class MyService {
    private Lock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();//對象監視器
    private boolean hasValue=false;
    public void set(){
        try{
            lock.lock();
            while (hasValue==true){
                System.out.println("有可能**連續");
                condition.await();
            }
            System.out.println("打印*");
            hasValue=true;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void get(){
        try {
            lock.lock();
            while(hasValue==false){
                System.out.println("有可能--連續");
                condition.await();
            }
            System.out.println("打印-");
            hasValue=false;
            condition.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class ThreadA extends Thread{
    private MyService myService;
    public ThreadA(MyService myService){
        this.myService=myService;
    }

    @Override
    public void run() {
        for (int i = 0; i <Integer.MAX_VALUE ; i++) {
            myService.set();
        }
    }
}
public class ThreadB extends Thread{
    private MyService myService;
    public ThreadB(MyService myService){
        this.myService=myService;
    }

    @Override
    public void run() {
        for (int i = 0; i <Integer.MAX_VALUE ; i++) {
            myService.get();
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        MyService service=new MyService();
        ThreadA[] threadA= new ThreadA[10];
        ThreadB[] threadB= new ThreadB[10];
        for (int i = 0; i < 10; i++) {
            threadA[i]=new ThreadA(service);
            threadB[i]=new ThreadB(service);
            threadA[i].start();
            threadB[i].start();
        }
    }
/*
//signal會出現假死
...
打印*
有可能**連續
打印-
有可能--連續
有可能--連續
打印*
有可能**連續
有可能**連續
...
*/
}

8.公平鎖和非公平鎖

鎖lock分爲公平鎖和非公平鎖,公平鎖表示線程獲取鎖的順序是按照線程加鎖的順序來分配的,即先來先得,非公平鎖是一種獲取鎖的搶佔機制,是隨機得到鎖的.默認是非公平鎖.

9.getHoldCount() getQueueLength() getWaitQueueLength()

int getHoldCount() 查詢當前線程保持此鎖定的個數,也就是調用lock方法的次數.

public class Service {
    private ReentrantLock lock=new ReentrantLock();
    public void serviceMethod1(){
        try{
            lock.lock();
            System.out.println("serviceMethod1 getHoldCount="+lock.getHoldCount());
            serviceMethod2();
        }finally {
            lock.unlock();
        }

    }
    public void serviceMethod2(){
        try{
            lock.lock();
            System.out.println("serviceMethod2 getHoldCount="+lock.getHoldCount());
        }finally {
            lock.unlock();
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        Service service=new Service();
        service.serviceMethod1();
    }
}

getQueueLength() 返回正在等待獲取此鎖定的線程估計數

public class Service {
    private ReentrantLock lock=new ReentrantLock();
    public void serviceMethod1(){
        try{
            lock.lock();
            System.out.println("ThreadName="+Thread.currentThread().getName()+"進入方法");
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public static void main(String[] args) throws InterruptedException {
        final Service service=new Service();
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                service.serviceMethod1();
            }
        };
        Thread[] threadArray=new Thread[10];
        for (int i = 0; i <10; i++) {
            threadArray[i]=new Thread(runnable);
        }
        for (int i = 0; i <10 ; i++) {
            threadArray[i].start();
        }
        Thread.sleep(2000);
        System.out.println("有線程數:"+service.lock.getQueueLength()+"個在等待獲取鎖");
    }
/*
ThreadName=Thread-0進入方法
有線程數:9個在等待獲取鎖
*/
}

getWaitQueueLength(Condition condition)返回等待與此鎖定相關的給定條件Condition的線程估計數,好比有5個線程,每一個線程都執行同一個Condition對象的await()方法,則調用以後返回5

public class Service {
    private ReentrantLock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();
    public void waitMethod(){
        try{
            lock.lock();
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void notifyMethod(){
        try{
            lock.lock();
            System.out.println("有"+lock.getWaitQueueLength(condition)+"個線程正在等待newCondition");
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        final Service service=new Service();
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                service.waitMethod();
            }
        };
        Thread[] threadArray=new Thread[10];
        for (int i = 0; i <10 ; i++) {
            threadArray[i]=new Thread(runnable);
        }
        for (int i = 0; i <10 ; i++) {
            threadArray[i].start();
        }
        Thread.sleep(2000);
        service.notifyMethod();
    }
/*有10個線程正在等待newCondition*/
}

10 hasQueuedThread() hasQueuedThreads() hasWaiters()

boolean hasQueuedThread(Thread thread) 查詢指定的線程是否正在等待獲取此鎖定

boolean hasQueuedThreads() 查詢是否有線程正在等待獲取此鎖定

public class Service {
    public ReentrantLock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();
    public void waitMethod(){
        try{
            lock.lock();
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        final Service service=new Service();
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                service.waitMethod();
            }
        };
        Thread threadA=new Thread(runnable);
        threadA.start();
        threadA.sleep(500);
        Thread threadB=new Thread(runnable);
        threadB.start();
        threadB.sleep(500);
        System.out.println(service.lock.hasQueuedThread(threadA));
        System.out.println(service.lock.hasQueuedThread(threadB));
        System.out.println(service.lock.hasQueuedThreads());
    }
/*
false
true
true
*/
}

hasWaiters(Condition condition)查詢是否有線程正在等待與此鎖定有關的condition條件

public class Service {
    public ReentrantLock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();
    public void waitMethod(){
        try{
            lock.lock();
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void notifyMethod(){
        try{
            lock.lock();
            System.out.println("有沒有線程正在等待condition?"+lock.hasWaiters(condition)+" 線程數是多少?"+lock.getWaitQueueLength(condition));
        } finally {
            lock.unlock();
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        final Service service=new Service();
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                service.waitMethod();
            }
        };
        Thread[] threadArray=new Thread[10];
        for (int i = 0; i <10 ; i++) {
            threadArray[i]=new Thread(runnable);
        }
        for (int i = 0; i <10; i++) {
            threadArray[i].start();
        }
        Thread.sleep(2000);
        service.notifyMethod();
    }
/*
有沒有線程正在等待condition?true 線程數是多少?10
*/
}

11.isFair() isHeldByCurrentThread() isLocked()

isFair()獲取公平鎖狀態

isHeldByCurrentThread()查詢當前線程是否保持此鎖定(相似於isInterrupted())

public class Service {
    private ReentrantLock lock;
    public Service(boolean isFair){
        super();
        lock=new ReentrantLock(isFair);
    }
    public void serviceMethod(){
        try{
            System.out.println(lock.isHeldByCurrentThread());
            lock.lock();
            System.out.println(lock.isHeldByCurrentThread());
        }finally {
            lock.unlock();
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        final Service service=new Service(true);
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                service.serviceMethod();
            }
        };
        Thread thread=new Thread(runnable);
        thread.start();
    }
/*
false
true
*/
}

isLocked() 查詢此鎖是否被任意線程保持

public class Service {
    private ReentrantLock lock;
    public Service(boolean isFair){
        super();
        lock=new ReentrantLock(isFair);
    }
    public void serviceMethod(){
        try{
            System.out.println(lock.isLocked());
            lock.lock();
            System.out.println(lock.isLocked());
        }finally {
            lock.unlock();
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        final Service service=new Service(true);
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                service.serviceMethod();
            }
        };
        Thread thread=new Thread(runnable);
        thread.start();
    }
/*
false
true
*/
}

12.lockInterruptibly() tryLock() tryLock(long timeout,TimeUnit unit)

lockInterruptibly() 當前線程未被中斷,則獲取鎖定,若是已經被中斷則出現異常

public class Service {
    public ReentrantLock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();
    public void waitMethod(){
        try{
            lock.lockInterruptibly();
            System.out.println("lock begin "+Thread.currentThread().getName());
            for (int i = 0; i < Integer.MAX_VALUE/10; i++) {
                String newString=new String();
                Math.random();
            }
            System.out.println("lock end "+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if(lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        final Service service=new Service();
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                service.waitMethod();
            }
        };
        Thread threadA=new Thread(runnable);
        threadA.setName("A");
        threadA.start();
        Thread.sleep(500);
        Thread threadB=new Thread(runnable);
        threadB.setName("B");
        threadB.start();
        threadB.interrupt();//打標記
        System.out.println("main end");
    }
/*
lock begin A
main end
java.lang.InterruptedException
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1219)
    at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340)
    at lock.Service.waitMethod(Service.java:14)
    at lock.Run$1.run(Run.java:12)
    at java.lang.Thread.run(Thread.java:722)
*/
}

boolean tryLock() 僅在調用時鎖定未被另外一個線程保持的狀況下,才獲取該鎖定

public class Service {
    public ReentrantLock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();
    public void waitMethod(){
        if(lock.tryLock()){
            System.out.println(Thread.currentThread().getName()+"得到鎖");
        }else{
            System.out.println(Thread.currentThread().getName()+"沒有得到鎖");
        }
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        final Service service=new Service();
        Runnable runnable=new Runnable() {
            @Override
            public void run() {
                service.waitMethod();
            }
        };
        Thread threadA=new Thread(runnable);
        threadA.setName("A");
        threadA.start();
        Thread.sleep(500);
        Thread threadB=new Thread(runnable);
        threadB.setName("B");
        threadB.start();
    }
/*
A得到鎖
B沒得到鎖
*/
}

tryLock(long timeout,TimeUnit unit) 若是鎖定在給定等待時間內沒有被另外一個線程保持,且當前線程未被中斷,則獲取該鎖定.

13.awaitUtil() 線程在等待時間內,能夠被其餘線程提早喚醒

public class Service {
    public ReentrantLock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();
    public void waitMethod(){
        try{
            Calendar calendar=Calendar.getInstance();
            calendar.add(Calendar.SECOND,10);
            lock.lock();
            System.out.println("wait begin timer="+System.currentTimeMillis());
            condition.awaitUntil(calendar.getTime());
            System.out.println("wait end timer="+System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if(lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }
    public void notifyMethod(){
        try{
            Calendar calendar=Calendar.getInstance();
            calendar.add(Calendar.SECOND,10);
            lock.lock();
            System.out.println("notify begin timer="+System.currentTimeMillis());
            condition.signalAll();
            System.out.println("notify end timer="+System.currentTimeMillis());
        } finally {
            if(lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }
}
public class ThreadA extends Thread{
    private Service service;
    public ThreadA(Service service){
        this.service=service;
    }

    @Override
    public void run() {
        service.waitMethod();
    }
}
public class ThreadB extends Thread{
    private Service service;
    public ThreadB(Service service){
        this.service=service;
    }

    @Override
    public void run() {
        service.notifyMethod();
    }
}
public class Run {
    public static void main(String[] args) throws InterruptedException {
        Service service=new Service();
        ThreadA threadA=new ThreadA(service);
        threadA.start();
        ThreadB threadB=new ThreadB(service);
        threadB.start();
    }
/*
wait begin timer=1516776325733
notify begin timer=1516776325733
notify end timer=1516776325733
wait end timer=1516776325733
*/
}

14.使用Condition實現順序執行

相關文章
相關標籤/搜索