java 多线程

  • 锁(synchronized)
  • synchronized 关键字
  • wait notify 实现线程通讯
  • wait 和 sleep区别
  • 自己设计显示锁


自己仅作 java 多线程的记录,看视频主要还是看书看不下去了…

锁(synchronized)

synchronized 关键字

synchronized 可以放在方法上,此时加锁的对象是this,也可以加锁在同步代码块上。

以下实体类,当我们调用的时候,会发现互斥的,证明方法上加的锁是this锁(类对象锁)
如果synchronized 的对象不一致,则不会互斥(即方法调用不会等待)

public class ThreadTestDayTwo {
    
    public synchronized void get1() throws InterruptedException {
        System.out.println("123");
        Thread.sleep(10000);
    }

    public synchronized void get2() throws InterruptedException {
        System.out.println("456");
        Thread.sleep(10000);
    }
}

wait notify 实现线程通讯

实现生产者和消费者

生产者和消费者的关系: 生产者 生产一条数据的时候就要等待,而消费者消费一条数据的时候就要通知生产者继续生产

/**
 * @Author: shengjm
 * @Date: 2020/2/5 19:40
 * @Description: 生产者和消费者
 */

public class ProduceCustomerTest {


    private final Object object = new Object();

    private volatile boolean isCustomer = true;
    private int i = 1;

    public void produce() {
        synchronized (object) {
            if (isCustomer) {
                System.out.println("p->" + i++);
                isCustomer = false;
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                isCustomer = true;
                object.notify();
            }
        }
    }

    public void customer(){
        synchronized (object) {
            if (!isCustomer) {
                isCustomer = true;
                object.notify();
                System.out.println("c->" + i);

            } else {
                try {
                    isCustomer = false;
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }

    public static void main(String[] args) {
        ProduceCustomerTest test = new ProduceCustomerTest();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true)
                test.produce();
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true)
                test.customer();
            }
        }).start();

    }
}

注意:
貌似看上去没什么问题,但是当我们启用多个线程的时候就会发现,还是出现了卡住的现象,而非线程死锁,问题是notify()的时候,只是通知一个线程,所以要改成使用notifyAll()

还有一处需修改,把if判断改成while , 原因是当我们notifyAll 的时候,条件并不一定符合,放在while中的话,循环判断,知道条件符合才继续往下运行。

wait 和 sleep区别

1,sleep 不会释放锁,而wait 可以
2,wait 方法,要在synchronized 关键字内

自己设计显示锁

实际开发中,当我们使用synchronized 关键字时候,里面执行的代码时没办法控制的。
所以,我们下面自己设计显示LOCK

/**
 * @Author: shengjm
 * @Date: 2020/2/6 15:53
 * @Description: 显示锁设计
 */
public interface Lock {

    class TimeoutException extends Exception{

        public TimeoutException(String message){
            super(message);
        }
    }

    void lock() throws InterruptedException;

    void lock(long mills) throws InterruptedException , TimeoutException;

    void unlock();

    Collection<Thread> getLockThread();

    int getBlockSize();

}
/**
 * @Author: shengjm
 * @Date: 2020/2/6 15:56
 * @Description: 实现类
 */

public class BooleanLock implements Lock {

    private boolean initValue;

    private Thread currentThread;

    Collection<Thread> blockThreadCollection = new ArrayList<>();

    public BooleanLock() {
        this.initValue = false;
    }

    @Override
    public synchronized void lock() throws InterruptedException {

        while(initValue){
            blockThreadCollection.add(Thread.currentThread());
            this.wait();
        }
        blockThreadCollection.remove(Thread.currentThread());
        initValue = true;
        this.currentThread = Thread.currentThread();
    }

    @Override
    public synchronized void lock(long mills) throws InterruptedException, TimeoutException {

        if (mills <= 0){
            this.lock();
        }
        long hasRemaining = mills;
        long endTime = System.currentTimeMillis() + mills;
        while(initValue){
            if(hasRemaining <= 0){
                throw  new TimeoutException(Thread.currentThread() + "time out");
            }
            blockThreadCollection.add(Thread.currentThread());
            this.wait(mills);
            hasRemaining = endTime - System.currentTimeMillis();
        }
        initValue = true;
        this.currentThread = Thread.currentThread();
    }

    @Override
    public synchronized void unlock() {
        if(this.currentThread == Thread.currentThread()){
            this.initValue = false;
            System.out.println(Thread.currentThread() + "release the monitor...");
            this.notifyAll();
        }
    }

    @Override
    public Collection<Thread> getLockThread() {
        return Collections.unmodifiableCollection(blockThreadCollection);
    }

    @Override
    public int getBlockSize() {
        return blockThreadCollection.size();
    }

    public static void main(String[] args) {
        final BooleanLock booleanLock = new BooleanLock();

        Stream.of("T1","T2","T3").forEach((name)->{
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
//                        booleanLock.lock();
                        booleanLock.lock(5000);  // lock 等待时间,当等待时间小于实际 work方法的时间,则抛出超时异常
                        Optional.of(Thread.currentThread().getName() + "have the lock monitor...").ifPresent(System.out::println);
                        work();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (TimeoutException e) {
                        e.printStackTrace();
                    } finally {
                        booleanLock.unlock();
                    }
                }
            }).start();
        });
    }

	// work 方法模拟实际消耗的时间
    private static void work() throws InterruptedException {
        Optional.of(Thread.currentThread().getName() + "is working ...").ifPresent(System.out::println);
        Thread.sleep(10000);

    }
}