JavaSEDemo16线程通讯与线程池

易错练习题

  • 本文是2021/04/13整理的笔记
  • 赘述可能有点多,还请各位朋友耐心阅读
  • 本人的内容和答案不一定是最好最正确的,欢迎各位朋友评论区指正改进

练习1(第8题)

class MyThread1 extends Thread{
Object lock;
public MyThread1(Object lock){
this.lock = lock;
}
public void run(){
synchronized(lock){ //1
for(int i = 0; i<=10; i++){
try{
Thread.sleep( (int)(Math.random()*1000) );
}catch(Exception e){}
System.out.println(“$$$”);
}
}
}
}
class MyThread2 extends Thread{
Object lock;
public MyThread2(Object lock){
this.lock = lock;
}
public void run(){
synchronized(lock){ //2
for(int i = 0; i<=10; i++){
try{
Thread.sleep((int)(Math.random()*1000) );
}catch(Exception e){}
System.out.println(“###”);
}
}
}
}
public class TestMyThread{
public static void main(String args[]){
Object lock = new Object();
Thread t1 = new MyThread1(lock);
Thread t2 = new MyThread2(lock);
t1.start();
t2.start();
}
}
问:在//1 和//2 处加上的synchronized 起什么作用?如果不加synchronized,
运行程序有什么不同的地方?

答案
作用:使线程同步
区别:不加synchronized,两个线程会抢占cpu,$$$和###会交替输出。

练习2(第9题)

class MyThread extends Thread{
private String data;
public void run(){
synchronized(data){
for(int i = 0; i<10; i++){
try{
Thread.sleep((int)(Math.random()*1000) );
}catch(Exception e){}
System.out.println(data);
}
}
}
}
public class TestMyThread {
public static void main(String args[]){
Thread t1 = new MyThread(“hello”);
Thread t2 = new MyThread(“world”);
t1.start();
t2.start();
}
}
问:上述代码输出的结果是什么?
A. 先输出100 个hello,然后是100 个world
B. 先输出100 个world,然后是100 个hello
C. 线程不同步,因此交替输出hello 和world

答案
C
两个线程的锁的对象不同,所以不能同步。一个是hello,一个是world

线程死锁

  • 定义:
    不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要
    的同步资源,就形成了线程的死锁
    出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状
    态,无法继续
  • 比喻:
    面试官提问:什么是线程死锁?
    大学生:你给我发offer,我就给你解释
    面试官:你不给我解释,我就不给你发
    这就是死锁

解决死锁:

  • 避免嵌套同步
  • 减少同步资源
  • 打破死锁的条件

线程通讯

  • 定义:线程之间可以通讯,能够互相发送信号。

忙等待(使用了共享变量)

  • 忙等待的效率比较低
public class EggTest {
    //判断有没有蛋。初始没有蛋
    boolean hasEggs = false;
    //创建人类(捡蛋)线程对象
    Thread human = new  Thread(new Runnable() {
        @Override
        public void run() {
            while(true){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(!hasEggs){//没有蛋
                    System.out.println("人在等待蛋");
                }else {
                    System.out.println("人捡到蛋了");
                    hasEggs = false;
                }
            }
        }
    }
    );
    //创建鸡(下单)线程对象
    Thread hen = new Thread(new Runnable() {
        @Override
        public void run() {
            while(true){
                try {
                    Thread.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("鸡下完蛋了");
                hasEggs = true;
            }
        }
    });
}

使用wait和notify() 等待,唤醒

介绍

  • 介绍一下wait notify notifyAll三种方法
  • wait():在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
  • notify(): 唤醒在此对象监视器上等待的单个线程。(如果有多个线程,则随机唤醒)
  • notifyAll():唤醒在此对象监视器上等待的所有线程。

wait关键字的语法

wait
synchronized(obj){
while(条件){
obj.wait();//卡在这里不动,释放锁
}
}

notify关键字的语法

  • 由于notifyAll()方法和notify()方法使用类似,这里只对notify关键字的使用进行描述。
  • synchronized(obj){
    obj.notify();//唤醒obj.wait等待线程,继续执行wait后的代码
    }

注意

  • 调用wait和notify的线程必须是锁的拥有者,因此需要在同步代码块中调用wait和notify方法,JVM会判断线程是不是拥有者,不是则会抛出异常

鸡下蛋 调用wait和notify方法 有歧义

  • 一个线程调用wait方法时,会将锁打开,等待其他该锁的线程调用notify方法来唤醒自己。

wait和sleep的区别

  • sleep 方法由 Thread 类提供,它不会释放线程锁。
  • wait 方法由 Object 类提供,这就意味着所有的 Java 类都具备 wait 方法,wait 方法调用后会释放线程具备的锁,同一个对象的 notify和 notifyAll 方法能够唤醒线程,wait 方法和 notify 方法需要在同步块中调用

管道流实现线程通讯

Send类

import java.io.IOException;
import java.io.PipedWriter;
import java.util.Random;
public class Send extends  Thread{
    Random r = new Random();
    //创建一个管道对象,PipedWriter
     PipedWriter out = new PipedWriter();

    public PipedWriter getOut() {
        return out;
    }

    @Override
    public void run() {
   //向管道流中写字符
    for (char ch ='A';ch<='z';ch++){
        try {
            out.write(ch);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //线程睡觉
        try {
            Thread.sleep((r.nextInt(10)+1)*100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    }
}

Receive类

import java.io.IOException;
import java.io.PipedReader;
public class Receive extends Thread{
    PipedReader in;

    public Receive(Send send) {
        try {
            this.in = new PipedReader(send.getOut());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Receive() {


    }

    @Override
    public void run() {
        while(true){
            try {
                System.out.println((char) in.read());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

TestMain类

public class TestMain {
    public static void main(String[] args) {
        //创建发送线程对象
        Send send = new Send();
        //创建接收线程对象
        Receive receive = new Receive(send);
        //启动线程
        send.start();
        receive.start();
    }
}

线程池

定义

  • 在容器里放多个创建好的线程对象,这些线程对象随时可以执行.并且可以重复使用. 还可以随时扩充线程.当闲置时,可以随时减少线程的数目.

作用

  1. 线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复
    使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存
    在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应
    更快
  2. 通过适当的调整线程中的线程数目可以防止出现资源不足的情况
  3. 当一个服务器接受到大量短小线程的请求时,使用线程池技术是非常合适的,它可
    以大大减少线程的创建和销毁次数,提高服务器的工作效率.

包含的内容

  • 线程池管理器
  • 工作线程
  • 任务队列
  • 任务接口

信号量

概念

  • 信号量是进行资源协调调度的工具

作用

  • 解决多个线程对多个资源的并发访问的问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

香鱼嫩虾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值