多线程 通信_多线程工作处理能力

多线程 通信_多线程工作处理能力

线程通信

一、线程通信的概念

  在操作系统中每个线程都是一个独立的个体,线程通信就是将这些线程个体常量起来的,只有具备了线程通信机制,线程才更有意义。线程通信不仅提高了CPU的利用率,还为程序开发提供了线程过程的监督与把控。

二、等待/通知机制概述

  wait()方法是Object类的方法,它能使当前执行的线程进行等待,将线程置入“与执行队列”中,代码在wait()方法所在的行停止。

  notify()方法需要在同步方法或者同步代码块中进行调用,线程必须获得对象级别的锁,notify()方法用于通知处于等待状态的线程继续执行。notify()方法执行后,不会马上释放锁,需要等待notify()方法所在的线程执行完synchronized代码块后才会释放锁。

  notifyAll()方法使处于等待的线程进入可运行状态,但是哪个线程获得执行需要看CPU分配资源的结果。

1、wait()/notify()方法

  线程类

public class WaitAndNontifyTreadA extends Thread{ private Object lock; public WaitAndNontifyTreadA(Object lock) { this.lock = lock; } @Override public void run() { super.run(); synchronized(lock){ SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("线程:" + Thread.currentThread().getName() + ",开始执行解锁方法," + "时间:" + dateformat.format(System.currentTimeMillis())); lock.notify(); System.out.println("线程:" + Thread.currentThread().getName() + ",开始sleep," + "时间:" + dateformat.format(System.currentTimeMillis())); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程:" + Thread.currentThread().getName() + ",解锁结束," + "时间:" + dateformat.format(System.currentTimeMillis())); } } } public class WaitAndNotifyThreadB extends Thread { private Object lock; public WaitAndNotifyThreadB(Object lock) { this.lock = lock; } @Override public void run() { super.run(); synchronized(lock){ SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); try { System.out.println("线程:" + Thread.currentThread().getName() + ",开始执行," + "时间:" + dateformat.format(System.currentTimeMillis()) + "。"); lock.wait(); System.out.println("线程:" + Thread.currentThread().getName() + ",继续执行," + "时间:" + dateformat.format(System.currentTimeMillis()) + "。"); } catch (InterruptedException e) { e.printStackTrace(); } } } } 

  测试类

@SpringBootTest public class WaitAndNotifyTest { @Test public static void main(String[] args) { Object lock = new Object(); WaitAndNontifyTreadA threadA = new WaitAndNontifyTreadA(lock); threadA.setName("threadA"); WaitAndNotifyThreadB threadB = new WaitAndNotifyThreadB(lock); threadB.setName("threadB"); threadB.start(); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } threadA.start(); } } 

  控制台输出结果

线程:threadB,开始执行,时间:2020-08-31 19:24:15。 线程:threadA,开始执行解锁方法,时间:2020-08-31 19:24:18 线程:threadA,开始sleep,时间:2020-08-31 19:24:18 线程:threadA,解锁结束,时间:2020-08-31 19:24:20 线程:threadB,继续执行,时间:2020-08-31 19:24:20。 

  从控制台输出结果可知,ThreadA解锁后,即退出synchronized代码块后ThreadB才继续执行wait()后面的代码。

2、验证wait()释放锁

  service类

public class WaitReleaseLockService { public void testMethod(Object lock){ synchronized(lock){ SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("线程:" + Thread.currentThread().getName() + ",开始执行," + "时间:" + dateformat.format(System.currentTimeMillis()) + "。"); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程:" + Thread.currentThread().getName() + ",结束执行," + "时间:" + dateformat.format(System.currentTimeMillis()) + "。"); } } } 

  线程类

public class WaitReleaseLockThread extends Thread{ private Object lock; public WaitReleaseLockThread(Object lock) { this.lock = lock; } @Override public void run() { super.run(); WaitReleaseLockService service = new WaitReleaseLockService(); service.testMethod(lock); } } 

  测试类

@SpringBootTest public class WaitReleaseThreadTest { @Test public static void main(String[] args) { Object lock = new Object(); WaitReleaseLockThread threadA = new WaitReleaseLockThread(lock); threadA.setName("threadA"); threadA.start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } WaitReleaseLockThread threadB = new WaitReleaseLockThread(lock); threadB.setName("threadB"); threadB.start(); } } 

  控制台输出结果

线程:threadA,开始执行,时间:2020-08-31 20:17:33。 线程:threadB,开始执行,时间:2020-08-31 20:17:34。 

  结论:从控制台的输出结果可以看出,当threadA线程wait后,thtreadB线程还能获得锁,并执行同步方法。因此,可知threadA线程执行了wait()方法后释放了锁。

  拓展:当把上面的例子中的线程类的wait方法该为sleep方法,则两个线程同步执行。因此,threadB线程被阻塞。

3、notify()与notifyAll()

  notify()方法一次只能随机唤醒一个线程,notifyAll()方法能唤醒所有线程。

  线程类

public class NotifyService { public void method(Object lock){ synchronized(lock){ SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("线程:" + Thread.currentThread().getName() + ",开始执行," + "时间:" + dateformat.format(System.currentTimeMillis()) + "。"); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程:" + Thread.currentThread().getName() + ",结束执行," + "时间:" + dateformat.format(System.currentTimeMillis()) + "。"); } } public void notifyMethod(Object lock){ synchronized(lock){ lock.notify(); // lock.notifyAll(); } } } 

  测试类

@SpringBootTest public class NotifyTest { @Test public static void main(String[] args) { Object lock = new Object(); NotifyService service = new NotifyService(); Thread threadA = new Thread(new Runnable() { @Override public void run() { service.method(lock); } },"threadA"); threadA.start(); Thread threadB = new Thread(new Runnable() { @Override public void run() { service.method(lock); } },"threadB"); threadB.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } Thread notifyThread = new Thread(new Runnable() { @Override public void run() { service.notifyMethod(lock); } },"notifyThread"); notifyThread.start(); } } 

  控制台输出结果

// notif()输出结果 线程:threadA,开始执行,时间:2020-08-31 21:04:29。 线程:threadB,开始执行,时间:2020-08-31 21:04:29。 // notifAll()输出结果 线程:threadA,开始执行,时间:2020-08-31 20:58:49。 线程:threadB,开始执行,时间:2020-08-31 20:58:49。 线程:threadB,结束执行,时间:2020-08-31 20:58:51。 线程:threadA,结束执行,时间:2020-08-31 20:58:51。 

4、wait(long)方法

5、生产者消费者模型

(1)、单生产者单消费者

  产品栈类

public class ProduceStack { private List stackList = new ArrayList(); synchronized public void push(){ while (stackList.size() == 1){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } stackList.add("push = " + Math.random()); this.notify(); System.out.println("执行进栈后 stackList size = " + stackList.size()); } synchronized public void pop(){ while (stackList.size() == 0){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } stackList.remove(0); this.notify(); System.out.println("执行出栈后stackList size = " + stackList.size()); } } 

  生产者&消费者线程类

// 生产者线程类 public class ProducerThread extends Thread{ private ProduceStack producerList; public ProducerThread(ProduceStack producerList) { this.producerList = producerList; } @Override public void run() { super.run(); while(true){ producerList.push(); } } } // 消费者线程类 public class ConsumerThread extends Thread { private ProduceStack producerList; public ConsumerThread(ProduceStack producerList) { this.producerList = producerList; } @Override public void run() { super.run(); while(true){ producerList.pop(); } } } 

  测试累

@SpringBootTest public class ProducerAndConsumerTest { @Test public static void main(String[] args) { ProduceStack producerList = new ProduceStack(); ProducerThread producerThread = new ProducerThread(producerList); ConsumerThread consumerThread = new ConsumerThread(producerList); producerThread.setName("producerThread"); consumerThread.setName("consumerThread"); producerThread.start(); consumerThread.start(); } } 

  控制台输出结果

执行进栈后 stackList size = 1 执行出栈后stackList size = 0 执行进栈后 stackList size = 1 执行出栈后stackList size = 0 执行进栈后 stackList size = 1 执行出栈后stackList size = 0 执行进栈后 stackList size = 1 执行出栈后stackList size = 0 执行进栈后 stackList size = 1 ... 

(2)、线程假死

  在多生产者,多消费者模式下,容易出现线程假死的情况。线程假死是因为生产者和消费者的线程都处于WAITING状态中,程序不再往下执行了。修改上面例子中的测试类验证线程假死。

  测试类修改

@SpringBootTest public class ProducerAndConsumerTest { @Test public static void main(String[] args) { ProduceStack producerList = new ProduceStack(); ProducerThread[] producerThreadList = new ProducerThread[2]; ConsumerThread[] consumerThreadList = new ConsumerThread[2]; for (int i = 0; i < 2 ; i++){ producerThreadList[i] = new ProducerThread(producerList); producerThreadList[i].setName("生产者" + (i + 1)); consumerThreadList[i] = new ConsumerThread(producerList); consumerThreadList[i].setName("消费者" + (i + 1)); producerThreadList[i].start(); consumerThreadList[i].start(); } try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } Thread[] threadList = new Thread[Thread.currentThread().getThreadGroup().activeCount()]; Thread.currentThread().getThreadGroup().enumerate(threadList); for (int i = 0; i < threadList.length; i++){ System.out.println(threadList[i].getName() + " " + threadList[i].getState()); } } } 

  控制台输出结果

... 执行出栈后stackList size = 0 执行进栈后 stackList size = 1 执行出栈后stackList size = 0 执行进栈后 stackList size = 1 执行出栈后stackList size = 0 执行进栈后 stackList size = 1 执行出栈后stackList size = 0 执行进栈后 stackList size = 1 执行出栈后stackList size = 0 执行进栈后 stackList size = 1 main RUNNABLE Monitor Ctrl-Break RUNNABLE 生产者1 WAITING 消费者1 WAITING 生产者2 WAITING 消费者2 WAITING 

  造成线程假死的原因是生产者唤醒了同类线程,消费者同样唤醒了同类线程,造成生产者和消费者两边同时处于等待状态。假死是个概率事件,为了消除线程假死的影响,可以使用notifyAll()方法,当一个线程释放锁后,将其他线程全部唤醒。

6、线程通信——管道

  管道(pipeStream)是一种特殊的流,用于线程间的通信。一个线程发送数据到管道,另外一个线程从管道中读取数据。

  (1)、字节流管道

  读写类

public class WriteData { public void writeMethode(PipedOutputStream out){ try { System.out.println("write: "); for (int i = 0; i < 300; i++) { String outData = "" + (i + 1); out.write(outData.getBytes()); System.out.print(outData); } System.out.println(); out.close(); } catch (IOException e) { e.printStackTrace(); } } } public class ReadData { public void readMethod(PipedInputStream input){ try { System.out.println("read: "); byte[] byteArray = new byte[200]; int readLenght = input.read(byteArray); while(readLenght != -1){ String newData = new String(byteArray, 0, readLenght); System.out.println(newData); readLenght = input.read(byteArray); } System.out.println(); input.close(); } catch (IOException e) { e.printStackTrace(); } } } 

  线程类

public class ThreadWrite extends Thread{ private WriteData write; private PipedOutputStream out; public ThreadWrite(WriteData write, PipedOutputStream out) { this.write = write; this.out = out; } @Override public void run() { write.writeMethode(out); } } public class ThreadRead extends Thread{ private ReadData read; private PipedInputStream input; public ThreadRead(ReadData read, PipedInputStream input) { this.read = read; this.input = input; } @Override public void run() { read.readMethod(input); } } 

  测试类

@SpringBootTest public class PipedTest { @Test public static void main(String[] args) { try { WriteData writeData = new WriteData(); ReadData readData = new ReadData(); PipedInputStream inputStream = new PipedInputStream(); PipedOutputStream outputStream = new PipedOutputStream(); outputStream.connect(inputStream); ThreadRead threadRead = new ThreadRead(readData,inputStream); threadRead.start(); Thread.sleep(2000); ThreadWrite threadWrite = new ThreadWrite(writeData,outputStream); threadWrite.start(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } 

  控制台输出结果

read: write:   000009300 000 00 00 0 

  从控制台输出结果可以看出,读取和写入两个线程时异步运行。

三、join()方法

  join()方法能让线程排队运行,具有类似同步的效果。join内部使用的是wait()方法,当线程isActive状态时在无限循环中,只有线程销毁后才退出循环继续后续代码的执行。

1、join()和synchronized的区别

  synchronized内部使用的时对象监听器。因此,join和synchronized两种的原理不一样。

  任务类

public class TaskA { synchronized public void methodA(){ System.out.println("-------------运行methodA方法---------------"); } public void methodB(){ synchronized(this){ for (int i = 0; i < 5; i++){ System.out.println("线程:" + Thread.currentThread().getName() + ";i = " + i + ";"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } } 

  测试类

@SpringBootTest public class JoinSynTest { @Test public static void main(String[] args) { try { TaskA task = new TaskA(); Thread threadA = new Thread(new Runnable() { @Override public void run() { task.methodB(); } },"threadA"); Thread threadB = new Thread(new Runnable() { @Override public void run() { task.methodB(); } },"threadB"); threadA.start(); System.out.println("--------线程:" + Thread.currentThread().getName() + ",开始时间" + System.currentTimeMillis() + "---------"); threadA.join(3000); System.out.println("--------线程:" + Thread.currentThread().getName() + ",结束时间" + System.currentTimeMillis() + "---------"); threadB.start(); } catch (InterruptedException e) { e.printStackTrace(); } } } 

  控制台输出结果

--------线程:main,开始时间56--------- 线程:threadA;i = 0; 线程:threadA;i = 1; 线程:threadA;i = 2; --------线程:main,结束时间57--------- 线程:threadA;i = 3; 线程:threadA;i = 4; 线程:threadB;i = 0; 线程:threadB;i = 1; 线程:threadB;i = 2; 线程:threadB;i = 3; 线程:threadB;i = 4; 

  从控制台输出结果可以看出,因为methdB()方法的对象锁是task,join()方法的锁是对象threadA,因此两个线程异步运行。threadA和threadB的对象锁都是task,因此两个线程同步执行。

  另外,上面例子还使用了join(long)方法,当线程isAlive将一直等待,即使设置时间到了还是处于等待状态。

  join()方法

public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } } 

2、join()和sleep()的区别

  join内部是实现了wait方法,因此是释放锁的,sleep是不释放锁的。

  测试类

@SpringBootTest public class JoinTest { @Test public static void main(String[] args) { Thread threadA = new Thread(new Runnable() { @Override public void run() { System.out.println("线程:" + Thread.currentThread().getName() + "执行方法A。" + System.currentTimeMillis()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } },"threadA"); threadA.start(); try { threadA.join(); // Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("主线程:" + Thread.currentThread().getName() + "执行方法B。" + System.currentTimeMillis()); } } 

  控制台输出结果

线程:threadA执行方法A。10 主线程:main执行方法B。11 

四、ThreadLocl类

  ThreadLocl类使每个线程都绑定自己的值,主要做的是变量在不同线程间的隔离性。

1、ThreadLocl的隔离性

  ThreadLocl具有隔离性,每个线程都能取到自己的私有值。从这个例子的控制台打印结果可以看出,两个线程set和get到了自己私有值。

  常量类

public class ConstantUtil { public static ThreadLocal<String> t1 = new ThreadLocal(); } 

  测试类

@SpringBootTest public class ThreadLoclTest { @Test public static void main(String[] args) { Thread threadA = new Thread(new Runnable(){ @Override public void run() { try { for (int i = 0; i < 5; i++){ ConstantUtil.t1.set("线程:" + Thread.currentThread().getName() + ",时间:" + System.currentTimeMillis()); System.out.println(ConstantUtil.t1.get()); Thread.sleep(200); } } catch (InterruptedException e) { e.printStackTrace(); } } },"ThreadA"); Thread threadB = new Thread(new Runnable(){ @Override public void run() { try { for (int i = 0; i < 5; i++){ ConstantUtil.t1.set("线程:" + Thread.currentThread().getName() + ",时间:" + System.currentTimeMillis()); System.out.println(ConstantUtil.t1.get()); Thread.sleep(200); } } catch (InterruptedException e) { e.printStackTrace(); } } },"ThreadB"); threadA.start(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } threadB.start(); } } 

  控制台输出结果

线程:ThreadA,时间:41 线程:ThreadB,时间:40 线程:ThreadA,时间:42 线程:ThreadB,时间:41 线程:ThreadA,时间:42 线程:ThreadB,时间:41 线程:ThreadB,时间:42 线程:ThreadB,时间:42 

2、重写initialValue()方法,解决第一次get值为null问题

  继承ThreadLocal类

public class InitialThreadLocal extends ThreadLocal{ @Override protected Object initialValue() { return "线程:" + Thread.currentThread().getName() + ",时间:" + System.currentTimeMillis(); } } 

  测试类

@SpringBootTest public class InitialValueTest { @Test public static void main(String[] args) { InitialThreadLocal threadLocal = new InitialThreadLocal(); System.out.println(threadLocal.get()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Thread threadA = new Thread(new Runnable() { @Override public void run() { System.out.println(threadLocal.get()); } },"threadA"); threadA.start(); } } 

  控制台输出结果

线程:main,时间:16 线程:threadA,时间:17 

五、InheritableThreadLocal类

  InheritableThreadLocal类继承了ThreadLocal类,提供了子线程从父线程取值的方法,子线程可以通过InheritableThreadLocal的get方法取到父线程的值。同时通过重写childValue()方法,子线程也可以再父线程的基础上对数据进行加工。

  继承类

public class InheritableThreadLocal1 extends InheritableThreadLocal { @Override protected Object initialValue() { return System.currentTimeMillis(); } @Override protected Object childValue(Object parentValue) { return super.childValue(parentValue) + " i am the child value"; } } 

  测试类

@SpringBootTest public class InitialValueTest { @Test public static void main(String[] args) { InheritableThreadLocal1 it1 = new InheritableThreadLocal1(); System.out.println( "线程:" + Thread.currentThread().getName() + ",时间:" + it1.get()); Thread threadA = new Thread(new Runnable() { @Override public void run() { System.out.println( "线程:" + Thread.currentThread().getName() + ",时间:" + it1.get()); } },"threadA"); threadA.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println( "线程:" + Thread.currentThread().getName() + ",时间:" + it1.get()); } } 

  控制台输出结果

线程:main,时间:41 线程:threadA,时间:41 i am the child value 线程:main,时间:41 

2024最新激活全家桶教程,稳定运行到2099年,请移步至置顶文章:https://sigusoft.com/99576.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。 文章由激活谷谷主-小谷整理,转载请注明出处:https://sigusoft.com/169184.html

(0)
上一篇 2024年 7月 2日 下午9:08
下一篇 2024年 7月 2日

相关推荐

关注微信