线程之间的几种通信方式

1. 什么是线程间的通信

线程间通信其实就是多个线程在操作同一个资源时,多个线程之间不断切换执行时所发出的信号。例如:需要创建两个线程,一条线程只打印1-26的数字,另一条只打印A-Z的字母,最终打印结果为1A2B3C4D…26Z时,这时就需要使用线程之间的通信来交替完成。

2. 不同实现方式的demo

首先创建一个共用的线程池去管理和运行这两个线程:

public class ThreadPoolTest {
	// 创建一个定长的线程池
    private static final ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
	// 创建一个数字的数组
    public static String[] buildNoArr(int max) {
        String[] noArr = new String[max];
        for(int i=0;i
  1. 第一种方式使用 synchronized/wait/notify关键字:是使用共享内存的思想,主要利用的是同步机制的原子性,大致意思就是多个线程需要访问同一个共享变量,谁拿到了锁(获得了访问权限),谁就可以执行。再通过Object的wait()方法使当前线程进入阻塞状态,再调用Object的notify()方法时阻塞的线程进入就绪状态。
public class ThreadTest1  {
	// 创建线程池对象
    ThreadPoolTest poolTest = new ThreadPoolTest();
    // 创建一个空的集合对象用来存放打印的字符,并用该对象生成一个对象锁
    private final List list = new ArrayList<>();

    public Runnable newThreadOne() {
        final String[] noArr = poolTest.buildNoArr(26);
        return new Runnable() {
            public void run() {
                for (int i = 0; i < noArr.length; i++) {
                    synchronized (list) {
                        if (list.size() % 2 == 1) {
                            try {
                                list.wait();  //调用该锁对象的wait()方法,使当前线程进入等待状态,该线程后面的逻辑暂停执行
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        poolTest.printStr(noArr[i]);
                        list.add(noArr[i]);
                        list.notify(); //调用该锁对象的notify()方法,此时会唤醒newThreadTwo线程,使newThreadTwo线程进入就绪状态,当newThreadTwo重新获取到cup时,则继续从上次暂停的代码处往下执行
                    }
                }
            }
        };
    }

    public Runnable newThreadTwo() {
        final String[] charArr = poolTest.buildCharArr(26);
        return new Runnable() {
            public void run() {
                for (int i = 0; i < charArr.length; i++) {
                    synchronized (list) {
                        if (list.size() % 2 == 0) {
                            try {
                                list.wait();  //调用该锁对象的wait()方法,使当前线程进入等待状态,该线程后面的逻辑暂停执行
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        poolTest.printStr(charArr[i]);
                        list.add(charArr[i]);
                        list.notify();//调用该锁对象的notify()方法,此时会唤醒newThreadOne线程,使newThreadOne线程进入就绪状态,当newThreadOne重新获取到cup时,则继续从上次暂停的代码处往下执行
                    }
                }
            }
        };
    }
	// 测试
    public static void main(String args[]) {
        ThreadTest1 three = new ThreadTest1();
        ThreadPoolTest poolTest = new ThreadPoolTest();
        poolTest.run(three.newThreadOne());
        poolTest.run(three.newThreadTwo());
        poolTest.shutdown();
    }
}
  1. 第二种方式使用 volatile 关键字:是使用共享内存的思想,主要利用的是同步机制的可见性,大致意思就是多个线程同时监听一个变量,当这个变量发生变化的时候 ,其他线程能够感知并执行相应的业务。
public class ThreadTest2 {

    static volatile int value = 1;
    int one = 0;
    int two = 0;
    ThreadPoolTest poolTest = new ThreadPoolTest();

    public Runnable newThreadOne() {
        final String[] noArr = poolTest.buildNoArr(26);
        return new Runnable() {
            public void run() {
                while (true) {
                    if (value == 1 && one < noArr.length) {
                        poolTest.printStr(noArr[one]);
                        one++;
                        value = 2;
                    }
                }
            }
        };
    }

    public Runnable newThreadTwo() {
        final String[] charArr = poolTest.buildCharArr(26);
        return new Runnable() {
            public void run() {
                while (true) {
                    if (value == 2 && two < charArr.length) {
                        poolTest.printStr(charArr[two]);
                        two++;
                        value = 1;
                    }
                }
            }
        };
    }

    public static void main(String args[]) {
        ThreadTest2 three = new ThreadTest2();
        ThreadPoolTest poolTest = new ThreadPoolTest();
        poolTest.run(three.newThreadOne());
        poolTest.run(three.newThreadTwo());
        poolTest.shutdown();
    }

}
  1. 第三种方式使用ReentrantLock 结合 Condition,原理类似synchronized/wait/notify。
public class ThreadTest3 {

    private static volatile int value = 1;
    private Lock lock = new ReentrantLock(true);
    private Condition condition = lock.newCondition();
    ThreadPoolTest poolTest = new ThreadPoolTest();

    public Runnable newThreadOne() {
        final String[] noArr = poolTest.buildNoArr(26);
        return new Runnable() {
            public void run() {
                for (int i = 0; i < noArr.length; i++) {
                    try {
                        lock.lock(); //newThreadOne线程获取锁
                        if (value == 2) {
                            condition.await(); // newThreadOne线程进入等待阻塞
                        }
                        poolTest.printStr(noArr[i]);
                        value = 2;
                        condition.signal(); // 唤醒调用condition.await()进入阻塞的线程
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock(); // 手动释放锁
                    }
                }
            }
        };
    }

    public Runnable newThreadTwo() {
        final String[] charArr = poolTest.buildCharArr(26);
        return new Runnable() {
            public void run() {
                for (int i = 0; i < charArr.length; i++) {
                    try {
                        lock.lock(); // newThreadTwo线程获取锁
                        if (value == 1) {
                            condition.await(); // newThreadTwo线程进入等待阻塞
                        }
                        poolTest.printStr(charArr[i]);
                        value = 1;
                        condition.signal(); // 唤醒调用condition.await()进入阻塞的线程
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock(); // 手动释放锁
                    }
                }
            }
        };
    }

    public static void main(String args[]) {
        ThreadTest3 three = new ThreadTest3();
        ThreadPoolTest poolTest = new ThreadPoolTest();
        poolTest.run(three.newThreadOne());
        poolTest.run(three.newThreadTwo());
        poolTest.shutdown();
    }
}
  1. 第四种方式使用AtomicIntger 原子类,其主要原理是对 int 类型的一个封装,提供原子性的访问和更新操作,其原子性操作的实现是基于 CAS(compare-and-swap)技术,确保程序在被多个线程并发访问时可以得到正确的结果。
public class ThreadTest4  {

    private AtomicInteger value = new AtomicInteger(1);
    int one = 0;
    int two = 0;
    ThreadPoolTest poolTest = new ThreadPoolTest();

    public Runnable newThreadOne() {
        final String[] noArr = poolTest.buildNoArr(26);
        return new Runnable() {
            public void run() {
                while (true) {
                    if (value.get() == 1 && one < noArr.length) {
                        poolTest.printStr(noArr[one]);
                        one++;
                        value.set(2);
                    }
                }
            }
        };
    }

    public Runnable newThreadTwo() {
        final String[] charArr = poolTest.buildCharArr(26);
        return new Runnable() {
            public void run() {
                while (true) {
                    if (value.get() == 2 && two < charArr.length) {
                        poolTest.printStr(charArr[two]);
                        two++;
                        value.set(1);
                    }
                }
            }
        };
    }

    public static void main(String args[]) {
        ThreadTest4 three = new ThreadTest4();
        ThreadPoolTest poolTest = new ThreadPoolTest();
        poolTest.run(three.newThreadOne());
        poolTest.run(three.newThreadTwo());
        poolTest.shutdown();
    }
}

你可能感兴趣的:(Java,java)