多线程基础(三)
一、线程通信
多线程之间通讯,其实就是多个线程在操作同一个资源,但是操作的动作不同。
1、共享内存方式通信(while轮询的方式)
public class ListAdd1 {
private static volatile List<String> list = new ArrayList<>();
public static void main(String[] args) {
Thread t1 = new Thread(()->{
for (int i = 0; i <10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add("element");
System.out.println(Thread.currentThread().getName() + "向list中添加了" + (i + 1) + "个元素");
}
},"t1");
Thread t2 = new Thread(()->{
System.out.println("进入t2线程...");
while(true) {
if (list.size() ==5) {
System.out.println("t2线程收到通知,线程结束...");
throw new RuntimeException();
}
}
},"t2");
t2.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.start();
}
}
这种方式t1线程会不断地改变条件,线程t2不停的通过while语句检测list1.size() == 5是否成立,从而实现线程间的通信。但是这种方式会浪费CPU的资源。
2、wait/notify机制
使用wait/notify方法实现线程间的通信。(注意这两个方法都是object类的方法,换句话说java为所有的对象都提供了这两个方法)
① wait和notify必须配合synchronized关键字使用
② wait方法释放锁,notify方法不释放锁
③ wait和notify要被同一个对象调用
public class ListAdd1 {
private static volatile List<String> list = new ArrayList<>();
public static void main(String[] args) {
//实例化出来一个 lock,当使用wait 和 notify 的时候 , 一定要配合着synchronized关键字去使用
final Object lock = new Object();
Thread t1 = new Thread(()->{
synchronized (lock){
for (int i = 0; i <10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add("element");
System.out.println(Thread.currentThread().getName() + "向list中添加了" + (i + 1) + "个元素");
if (i == 4) {
System.out.println("t1向t2发出通知");
lock.notify();
}
}
}
},"t1");
Thread t2 = new Thread(()->{
synchronized (lock) {
System.out.println("进入t2线程...");
if (list.size() != 5) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2线程收到通知,线程结束...");
throw new RuntimeException();
}
},"t2");
t2.start();
t1.start();
}
}
这种方式实时性比较差,可以使用CountDownLatch进行改进。
public class ListAdd1 {
private static volatile List<String> list = new ArrayList<>();
public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(1);
Thread t1 = new Thread(()->{
for (int i = 0; i <10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
list.add("element");
System.out.println(Thread.currentThread().getName() + "向list中添加了" + (i + 1) + "个元素");
if (i == 4) {
System.out.println("t1向t2发出通知");
countDownLatch.countDown();
}
}
},"t1");
Thread t2 = new Thread(()->{
System.out.println("进入t2线程...");
if (list.size() != 5) {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t2线程收到通知,线程结束...");
throw new RuntimeException();
},"t2");
t2.start();
t1.start();
}
}
二、使用wait/notify模型LinkedBlockQueue
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class MyQueue {
// 1 需要一个承装元素的集合
private LinkedList<Object> list = new LinkedList<Object>();
// 2 需要一个计数器
private AtomicInteger count = new AtomicInteger(0);
// 3 需要制定上限和下限
private final int minSize = 0;
private final int maxSize;
// 4 构造方法
public MyQueue(int size) {
this.maxSize = size;
}
// 5 初始化一个对象 用于加锁
private final Object lock = new Object();
// put(anObject):
// 把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断,直到BlockingQueue里面有空间再继续.
public void put(Object obj) {
synchronized (lock) {
while (count.get() == this.maxSize) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 1 加入元素
list.add(obj);
// 2 计数器累加
count.incrementAndGet();
// 3 通知另外一个线程(唤醒)
lock.notify();
System.out.println("新加入的元素为:" + obj);
}
}
// take:
// 取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的数据被加入.
public Object take() {
Object ret = null;
synchronized (lock) {
while (count.get() == this.minSize) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 1 做移除元素操作
ret = list.removeFirst();
// 2 计数器递减
count.decrementAndGet();
// 3 唤醒另外一个线程
lock.notify();
}
return ret;
}
public int getSize() {
return this.count.get();
}
public static void main(String[] args) {
final MyQueue mq = new MyQueue(5);
mq.put("a");
mq.put("b");
mq.put("c");
mq.put("d");
mq.put("e");
System.out.println("当前容器的长度:" + mq.getSize());
Thread t1 = new Thread(() -> {
mq.put("f");
mq.put("g");
}, "t1");
t1.start();
Thread t2 = new Thread(() -> {
Object o1 = mq.take();
System.out.println("移除的元素为:" + o1);
Object o2 = mq.take();
System.out.println("移除的元素为:" + o2);
}, "t2");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
三、单例模式
1、double check instance
public class DubbleSingleton {
private volatile static DubbleSingleton ds;
// 1、私有构造方法
private DubbleSingleton() {
}
// 2、公共方法内部,双重校验
public static DubbleSingleton getDs() {
if (ds == null) {
try {
// 模拟初始化对象的准备时间...
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (DubbleSingleton.class) {
if (ds == null) {
ds = new DubbleSingleton();
}
}
}
return ds;
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(DubbleSingleton.getDs().hashCode());
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(DubbleSingleton.getDs().hashCode());
}
}, "t2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(DubbleSingleton.getDs().hashCode());
}
}, "t3");
t1.start();
t2.start();
t3.start();
}
}
2、static inner class
public class Singletion {
private Singletion() {
}
private static class InnerSingletion {
private static Singletion single = new Singletion();
}
public static Singletion getInstance() {
return InnerSingletion.single;
}
}