线程同步 保护性暂停

线程同步

1. 线程同步模式保护性暂停

1.1 简介

一个线程等待一个线程的结果,或者也可以是线程之间同步】

1.2 实现思路

synchronized
synchronized 加锁 要关联到同一个对象,使用wait和notifyAll

1.3 实现

  • 先定义一个 用来在俩线程之间 传递的对象,
class Temp{
     
    
    static Object lock = new Object();     // 1
    Object result = null;
    
    public void put(Object o){
                   // 2
        synchronized (lock){
     
            result = o; 
            lock.notifyAll();               // 5
        }
    }
    public Object take() throws InterruptedException {
           // 3
        synchronized (lock){
     
            while (result == null){
     
                System.out.println("等待--------");
                lock.wait();                            // 4 
            }
            return result;
        }
    }
}

解释说明
注释1 定义一个对象,这个对象的作用是锁。
注释2 put方法,因为 temp类中的result 属性是要多个线程之间共享的,所有对于这个对象的操作要加锁,每次放一个对象,唤醒那些没有拿到值而 等待的线程,
注释3 从temp对象中取值,同样的,涉及到多个线程之间访问,就是要加锁, 如果没有值就等待,有值就返回,如果等待就等put线程把他唤醒,
上面的方法 没有 等待时间,take线程看没有值后就会一直等下去

  • 带等待时间的take方法
    耐心的看看下面
 public Object take(int waitTime) throws InterruptedException {
           
        synchronized (lock){
     
            while (result == null){
     
                System.out.println("等待--------");
                lock.wait(waitTime);            // 1                 
            }
            return result;
        }
    }

如果是上面这样, 直接将传递进来的参数传给wait方法。就会有一个 虚假唤醒 出现
比如,等待时间是 20 秒, 结果在第10 秒的时候被其他线程唤醒了,因为唤醒是 notifyAll方法,线程醒来,发现没有结果,就会再次睡眠 20秒

改进的方法
在方法开始的时间继续开始时间,在wait被唤醒之后记录 结束时间,算出经过时间,用等待时间减去 经过时间就是新的等待时间,如果等待时间<0 线程就结束,也就是break出while循环
比如,要睡20秒,在第10 秒的时候被其他线程唤醒了,经过时间就是10秒,新的等待时间就是 10 秒, 就不会有虚假唤醒的情况出现

    public Object take(long time) throws InterruptedException {
     
        long start = System.currentTimeMillis();
        long passTime = 0L; //经历时间

        synchronized (lock){
     
            while (result == null){
     
                 time = time-passTime;
                System.out.println("waitTime"+time);
                if(time <=0 ) break;
                lock.wait(time);
                passTime = System.currentTimeMillis()-start;
            }
            return result;
        }
    }
  • 测试
public class ThreadSyncTest1 {
     
    public static void main(String[] args) throws InterruptedException {
     
        Temp temp = new Temp();
        new Thread(()->{
     
            try {
     
                TimeUnit.SECONDS.sleep(10000);
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
            temp.put("a");
        }).start();

        new Thread(()->{
     
            try {
     
                String take = (String) temp.take(1000*25);
                System.out.println(take);
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
        }).start();

        for (int i = 0; i < 10; i++) {
     
            TimeUnit.SECONDS.sleep(1);
            temp.wakeUp();
        }
    }
}

1.2 jdk中提供的工具

1.2.1 Exchanger

public class ExchangerTest {
     

    public static void main(String[] args) {
     
        ExecutorService service = Executors.newCachedThreadPool();
        final Exchanger exchanger = new Exchanger();
        service.execute(new Runnable(){
     
            public void run() {
     
                try {
                     
                    String data1 = "thread-1-data";
                    System.out.println("线程" + Thread.currentThread().getName() +"正在把数据" + data1 +"换出去");
                    Thread.sleep((long)(Math.random()*10000));
                    String data2 = (String)exchanger.exchange(data1);
                    System.out.println("线程" + Thread.currentThread().getName() + "换回的数据为" + data2);
                }catch(Exception e){
     
                    
                }
            }    
        });
        service.execute(new Runnable(){
     
            public void run() {
     
                try {
                     
                    String data1 = "thread-2-data";
                    System.out.println("线程" + Thread.currentThread().getName() + "正在把数据" + data1 +"换出去");
                    Thread.sleep((long)(Math.random()*10000));                    
                    String data2 = (String)exchanger.exchange(data1);
                    System.out.println("线程" + Thread.currentThread().getName() + "换回的数据为" + data2);
                }catch(Exception e){
     
                    
                }                
            }    
        });        
    }
}

但是需要注意的是 这个允许等待时间的,但是如果在时间内没有返回,等待的线程会结束,另一个线程就会一直等待下去,来看看例子

public class MyThreadTest {
     
    public static void main(String[] args) {
     
        Exchanger<String> exchanger = new Exchanger<>();
        new Thread(()->{
     
            String s = "a";
            try {
     
                exchanger.exchange(s,1,TimeUnit.SECONDS);  //1
            } catch (InterruptedException | TimeoutException e) {
     
                System.out.println(Thread.currentThread().getName()+"超时了!");
            }
            System.out.println(Thread.currentThread().getName() +":"+s);
        }).start();


        new Thread(()->{
     
            String s = "b";
            try {
     
                TimeUnit.SECONDS.sleep(10);  // 2
                exchanger.exchange(s);
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() +":"+s);
        }).start();
    }
}

注释1 只允许等一秒 可是注释2 需要睡 10 秒,10秒之后 他不会被唤醒,
线程同步 保护性暂停_第1张图片
找时间看看 他的源码是怎么写的

你可能感兴趣的:(Java并发,java,多线程,并发编程,thread)