Java多线程同步

 

Thread常用方法

Thread.yield()	 【静态方法】   //放弃本次cpu抢占权,只是本次。
Thread.sleep()   【静态方法】   // 休眠,不释放锁旗标,睡醒接着执行。
----------------------------------------------------------------------------------------
thread1.join()	 //加入【实例方法】  例如:在thread2的run()中有一个thread1.join(),此时thread2必须等到thread1执行完毕才能继续进行
thread1.interrupt():在其它线程调用此函数可以中断thread1,前提是线程1在sleep、join或者Object的实例方法wait的阻塞状态,通过抛出中断异常。
InterruptException:例如在main线程中执行thread1.interrupt(),如果此时thread1恰好处于线程阻塞状态,阻塞的thread1就会抛出Interruptexception,通过catch捕获可以继续执行,达到终端阻塞线程的目的。

线程同步

最简单的案例:假设有一个票池,售票员不断地去票池取票,直到票池中没有票为止。

思路:票池相当于共享资源,去票池取票一定是同步的,否则会发生不同的售票员取到同一张票,不符合实际情况,所以取票的过程是一个线程安全的过程,可以通过加锁实现。

package com.multithread.java;

public class TicketPool {
    private int ticketNo = 100;
    // saler从票池取票,当票号为0时说明票池没有票了
    // 如果有票,取出该票号,并且票号减1
    public synchronized  int getTicketNo(){
        int t = ticketNo;
        if (t == 0) {
            return 0;
        }
        ticketNo--;
        return t;
    }
}

public class Saler extends Thread {

    private TicketPool pool;
    private String name;

    public Saler(TicketPool pool, String name) {
        super();
        this.pool = pool;
        this.name = name;
    }

    @Override
    public void run() {
        while (true) {
            int tickNo = pool.getTicketNo();
            if (tickNo == 0) {
                return;
            }
            System.out.println(name + " : " + tickNo);
        }
    }
}

最终测试代码如下:

// 3个售票员同时去票池抢票
public class App {
	public static void main(String[] args) {
		TicketPool pool = new TicketPool();
		Saler bob = new Saler(pool, "bob");
		Saler tom = new Saler(pool, "tom");
		Saler tim = new Saler(pool, "tim");
		bob.start();
		tom.start();
		tim.start();
	}
}

生产者消费者模式 

上述简单的例子主要是为了引出生产者消费者模式,此处有三个主要概念:资源池、生产者、消费者。

生产者不断向资源池中生产消息,消费者不断从资源池消费消息,为了保证资源池满了生产者不再生产,资源池中资源没了消费者不再消费消息,我们使用任意object对象的wait-notify机制,一般共享资源对象保证这一机制。

import java.util.LinkedList;

public class TicketPool {
    private LinkedList<Integer> pool = new LinkedList<>();
    private int MAX = 100;

    // 生产者调用,表示向池中生产消息
    public synchronized int add(Integer i) {

        // 防止wait() 伪唤醒,故使用while
        // 如果票池满了,生产者线程进入ticketPool锁旗标的等待队列,通过ticketPool.notify()唤醒
        while (pool.size() >= MAX) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 如果票池没满,生产者向里面添加
        pool.add(i);
        this.notify();  // 通知等待队列中的某一个线程
        return i;
    }
    // 消费者调用,表示消费池中的消息
    public synchronized int remove(){
        // 如果票池空了,则消费者线程进入等待队列
        while (pool.isEmpty()) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notify();  // 通知等待队列中的某一个线程
        // 如果票池没空,消费者消费信息
        return pool.removeFirst();
    }
}

public class Consumer extends  Thread {
    private String name;
    private TicketPool pool;

    public Consumer(String name, TicketPool pool) {
        this.name = name;
        this.pool = pool;
    }

    public void run(){
        while(true){
            int i = pool.remove();
            System.out.println(name + " remove : " + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Producer extends Thread {
    private static int index = 0;
    private String name;
    private TicketPool pool;

    public Producer(String name, TicketPool pool) {
        this.name = name;
        this.pool = pool;
    }

    public void run() {
        while (true) {
            int i = pool.add(index++);
            System.out.println(name + " add : " + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

最终测试代码如下:

public class App {
	public static void main(String[] args) {
		TicketPool pool = new TicketPool();
		Producer p1 = new Producer("p1", pool);
		Producer p2 = new Producer("p2", pool);
		Consumer c1 = new Consumer("c1", pool);
		p1.setName("p1");
		p2.setName("p2");
		c1.setName("c1");
		p1.start();
		p2.start();
		c1.start();
	}
}

以上代码如果生产者过多,消费者过少,有可能会出现所有消费者都在等待队列,但是资源池已经满了,导致所有线程都进入了等待队列,形成了死锁。或者消费者过多,生产者过少,也会形成死锁。

LockSupport.park / unpark方法

最后,关于生产者消费者思想,还可以使用LockSupport.park()和unpark()方法。

import java.util.concurrent.locks.LockSupport;

/** 三种线程协作通信的方式:suspend/resume、wait/notify、park/unpark */
public class ProducerConsumerLearn {
	/** 包子店 */
	public static Object baozidian = null;

	/** 正常的wait/notify */
	public void waitNotifyTest() throws Exception {
		// 启动线程
		new Thread(() -> {
			synchronized (this) {
				while (baozidian == null) { // 如果没包子,则进入等待
					try {
						System.out.println("1、进入等待");
						// Lambda表达式没有this指针
						this.wait();     // this指的是 new ProducerConsumerLearn(),其成员变量baozidian作为共享资源
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			System.out.println("2、买到包子,回家");
		}).start();
		// 3秒之后,生产一个包子
		Thread.sleep(3000L);
		baozidian = new Object();
		synchronized (this) {
			this.notifyAll();
			System.out.println("3、通知消费者");
		}
	}

	/** 会导致程序永久等待的wait/notify */
	public void waitNotifyDeadLockTest() throws Exception {
		// 启动线程
		new Thread(() -> {
			if (baozidian == null) { // 如果没包子,则进入等待
				try {
					Thread.sleep(5000L);
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
				synchronized (this) {
					try {
						System.out.println("1、进入等待");
						this.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			System.out.println("2、买到包子,回家");
		}).start();
		// 3秒之后,生产一个包子
		Thread.sleep(3000L);
		baozidian = new Object();
		// 消费者首先通知生产者
		synchronized (this) {
			this.notifyAll();
			System.out.println("3、通知消费者");
		}
	}

	/** 正常的park/unpark */
	public void parkUnparkTest() throws Exception {
		// 启动线程
		Thread consumerThread = new Thread(() -> {
			while (baozidian == null) { // 如果没包子,则进入等待
				System.out.println("1、进入等待");
				LockSupport.park();
			}
			System.out.println("2、买到包子,回家");
		});
		consumerThread.start();
		// 3秒之后,生产一个包子
		Thread.sleep(3000L);
		baozidian = new Object();
		LockSupport.unpark(consumerThread);
		System.out.println("3、通知消费者");
	}

	/** 死锁的park/unpark */
	public void parkUnparkDeadLockTest() throws Exception {
		// 启动线程
		Thread consumerThread = new Thread(() -> {
			if (baozidian == null) { // 如果没包子,则进入等待
				System.out.println("1、进入等待");
				// 当前线程拿到锁,然后挂起
				synchronized (this) {
					LockSupport.park();
				}
			}
			System.out.println("2、买到包子,回家");
		});
		consumerThread.start();
		// 3秒之后,生产一个包子
		Thread.sleep(3000L);
		baozidian = new Object();
		// 争取到锁以后,再恢复consumerThread
		synchronized (this) {
			LockSupport.unpark(consumerThread);
		}
		System.out.println("3、通知消费者");
	}

	public static void main(String[] args) throws Exception {

		// wait/notify要求再同步关键字里面使用,免去了死锁的困扰,但是一定要先调用wait,再调用notify,否则永久等待了
		// new ProducerConsumerLearn().waitNotifyTest();
		// new ProducerConsumerLearn().waitNotifyDeadLockTest();

		// park/unpark没有顺序要求,但是park并不会释放锁,所有再同步代码中使用要注意
		// new ProducerConsumerLearn().parkUnparkTest();
		new ProducerConsumerLearn().parkUnparkDeadLockTest();

	}
}

Kafka消息队列

// todo

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值