一篇搞懂Java多线程(全网最细)(三)

简介: 一篇搞懂Java多线程(全网最细)(三)

五、线程同步

1. 概念

       即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该地址进行操作,而其他线程又处于等待状态,实现线程同步的方法有很多种。

1、为什么要创建多线程?

在一般情况下,创建一个线程是不能提高程序执行效率的,所以要创建多个线程。

2、为什么要线程同步?

多个线程同时运行的时候可能调用线程函数,在多个线程同时对同一个内存地址进行写入,由于CPU时间调度上的问题,写入数据会被多次的覆盖,所以就要使线程同步。

3、线程同步是什么意思?

同步就是协同步调,按预定的先后次序进行运行。如:你做完,我再做。

错误理解 “同”字从字面上容易理解为一起动作,其实不是。“同”字应是指协同、协助、互相配合。

正确理解 :所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回,同时其他线程也不能调用这个方法。

线程同步的作用 :

  • 线程有可能和其他线程共享一些资源,比如:内存、文件、数据库等
  • 当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。
  • 线程同步的真是意思其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。

2. 同步互斥访问(synchronized)

       基本上所有解决线程安全问题的方式都是采用“序列化临界资源访问”的方式,即在同一时刻只有一个线程操作临界资源,操作完了才能让其他线程进行操作,也称同步互斥访问。

在Java中一般采用synchronized和Lock来实现同步互斥访问。

3. Synchronized关键字

首先了解一下互斥锁 :就是能达到互斥访问目的的锁

  1. 如果对一个变量加上互斥锁,那么在同一时刻,该变量只能有一个线程能访问,即当一个线程访问临界资源时,其他线程只能等待。
  2. 在Java中,每一个对象都有一个锁标记(monitor),也被称为监视器,当多个线程访问对象时,只有获取了对象的锁才能访问。
  3. 在我们编写代码的时候,可以使用synchronized修饰对象的方法或者代码块,当某个线程访问这个对象synchronized方法或者代码块时,就获取到了这个对象的锁,这个时候其他对象是不能访问的,只能等待获取到锁的这个线程执行完该方法或者代码块之后,才能执行对象的方法。

4. synchronized同步代码块实现

  1. 第一种:synchronized代码块方式(灵活)
  2. 第二种:在实例方法上使用synchronized
  3. 第三种 : 在静态方法上使用synchronized
package com.thread;
/**
 * synchronized同步代码块实现
 * 
 * @author 云村小威
 *
 * @2023年7月21日 下午12:37:53
 */
public class Text07 {
  // 第一种
  public void myThread1(Thread thread) {
    // this代表当前对象
    synchronized (this) {
      for (int i = 0; i < 5; i++) {
        System.out.println(thread.getName() + ":" + i);
      }
    }
  }
  // 第二种
  public synchronized void myThread2(Thread thread) {
    // this代表当前对象
    for (int i = 0; i < 5; i++) {
      System.out.println(thread.getName() + ":" + i);
    }
  }
  // 第三种
  public static synchronized void myThread3(Thread thread) {
    // this代表当前对象
    for (int i = 0; i < 5; i++) {
      System.out.println(thread.getName() + ":" + i);
    }
  }
}

5. 什么是类锁和对象锁

5.1 概念

对象锁:在java中每个对象都有一个唯一的锁,对象锁用于对象实例方法或者一个对象实例上面的   —— 一个对象一把锁,100个对象100把锁。

类锁:是用于一个类静态方法或者class对象的,一个类的实例对象可以有多个,但是只有一个class对象 —— 100个对象,也只是1把锁。

注意上述第三种方式synchronized同步代码块实现在静态方法添加synchronized这把锁属于类了,所有这个类的对象都共享这把锁,这就叫类锁。

5.2 释放锁时机

       如果一个方法或者代码块被synchronized关键字修饰,当线程获取到该方法或代码块的锁,其他线程是不能继续访问该方法或代码块的。而其他线程想访问该方法或代码块,就必须要等待获取到锁的线程释放这个锁,而在这里释放锁只有两种情况 :

  1. 线程执行完代码块,自动释放锁;
  2. 程序报错,JVM让线程自动释放锁;

举例:这里我调用了上述的第一种和第二种实现同步方式

public static void main(String[] args) {
    new Text07().myThread1(new Thread());
    new Text07().myThread2(new Thread());
  }

6. 线程等待与线程唤醒

Object类 作用
Void wait() 让活动在当前对象的线程无限等待(释放之前占有的锁)
Void notify() 唤醒当前对象正在等待的线程(只提示唤醒,不会释放锁)
Void notifyAll() 唤醒在此对象监视器上等待的所有线程。

方法详解 :

       wait和notify方法不是线程对象的方法,是java中任何一个java对象都有的方法,因为这两个方法是object类自带的。wait和notify方法不是通过线程对象调用的;

六、线程安全问题(生产者和消费者模式)

1、什么是 “ 生产者和消费者模式 ” ?

  1. 生产线负责生产,消费线程负责消费。
  2. 生产线程和消费线程要达到均衡
  3. 这是一种特殊的业务需求,在这种特殊的情况下需要使用wait方法和notify方法。

2、模拟一个业务需求

  • 仓库我们采用List集合
  • List集合中假设只能存储1个元素
  • 1个元素就表示仓库满了
  • 如果List集合中元素个数是0,就表示仓库空了
  • 保证List集合永远都是最多存储1个元素
  • 必须做到这种效果:生产1个消费1个。

模拟生产者与消费者案例:

       首先我创建了一个产品类来定义我们需要生产的对象,接着创建一个工厂类写了生产和出售两个方法并实现同步锁,通过put方法将生产的鸡脚保存到容器里如果容器小于10就消费者等待(wait() 方法),如果大于就唤醒( notifyAll()方法 )所有等待的消费者。然后再定义生产者类它需要不停做事,它什么时候休息根据工厂类生产的鸡脚是否大于20,否则不可休息得一直做事。最后是消费者调用工厂类的sale方法只要保存鸡脚的容器大于10就开始购买对容器进行减少,如果没有了就让消费者进行等待。

package com.thread;
import java.util.Vector;
/**
 * 生产者与消费者案例
 * 
 * @author 云村小威
 *
 * @2023年7月22日 上午10:51:47
 */
public class Text08 {
  public static void main(String[] args) {
    // 店铺运营
    // 创建一个工厂
    Factory f = new Factory();
    // 招三个成产员工
    Producer p1 = new Producer(f);
    Producer p2 = new Producer(f);
    Producer p3 = new Producer(f);
    new Thread(p1, "工作人员A").start();
    new Thread(p2, "工作人员B").start();
    new Thread(p3, "工作人员C").start();
    // 引入三个消费者
    Sale s1 = new Sale(f);
    Sale s2 = new Sale(f);
    Sale s3 = new Sale(f);
    new Thread(s1, "消费者A").start();
    new Thread(s2, "消费者B").start();
    new Thread(s3, "消费者C").start();
  }
}
/**
 * 消费者
 */
class Sale implements Runnable {
  Factory f = null;
  public Sale(Factory f) {
    this.f = f;
  }
  public void run() {
    while (true) {
      // 让消费者延迟购买的时间
      try {
        Thread.sleep(1500);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      f.sale();
    }
  };
}
/**
 * 生产类
 */
class Producer implements Runnable {
  Factory f = null;
  public Producer(Factory f) {
    this.f = f;
  }
  @Override
  public void run() {
    // 需要不停的做事
    while (true) {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      // 调用生产数据的方法
      f.put(new Product(1, "鸡脚"));
    }
  }
}
/**
 * 工厂类
 */
class Factory {
  // 准备储存产品的集合容器,多线程优先使用Vector集合,该集合类中的方法大部分都是带有同步的概念机制。
  Vector<Product> vc = new Vector<Product>();
  // 专门定义一个生产数据方法
  public synchronized void put(Product product) {
    // 判断产品于目标数量是否相同
    if (vc.size() >= 20) {
      // 产品数量满足让工作人员处于等待状态
      try {
        this.wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    } else {
      // 让等待的工作人员生产产品
      this.notifyAll();
      System.out.println("正在生产中...当前货架的鸡脚数目是:" + vc.size());
      vc.add(product);
      System.out.println("货架鸡脚总数目是:" + vc.size());
    }
  }
  // 专门定义一个负责消费数据方法
  public synchronized void sale() {
    // 只要容器存在货物就可以进行购买
    if (vc.size() > 10) {
      this.notifyAll(); // 唤醒所有等待的消费者
      System.out.println("当前货架上的鸡脚数目是:" + vc.size() + ",正在出售鸡脚。");
      vc.remove(0);
      System.out.println("\t目前货架上的鸡脚数目是:" + vc.size());
    } else {
      // 没有鸡脚,让消费者处于等待状态
      try {
        this.wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}
/**
 * 产品类
 */
class Product {
  private int pid;
  private String name;
  public Product() {
    // TODO Auto-generated constructor stub
  }
  public int getPid() {
    return pid;
  }
  public void setPid(int pid) {
    this.pid = pid;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public Product(int pid, String name) {
    super();
    this.pid = pid;
    this.name = name;
  }
}

七、多线程小案例

1. 老虎机案例

package com.thread;
import java.util.Random;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
/**
 * 老虎机游戏程序
 * @author 云村小威
 *
 * @2023年7月22日 上午11:27:56
 */
public class Game extends Application {
  Random rd = new Random(); // 随机数对象
  boolean flag = true; // 控制开关默认打开
  int timer = 50; // 控制事件变量
  // 通过数组保存没有规律的多张图片
  String[] images = { "lib\\7.png", "lib\\橙子.png", "lib\\柠檬.png", "lib\\苹果.png", "lib\\西瓜.png", "lib\\香蕉.png", };
  // 总布局
  Pane pane = new Pane();
  private Label a1 = new Label("", new ImageView(new Image("lib\\苹果.png", 150, 150, false, false)));
  private Label a2 = new Label("", new ImageView(new Image("lib\\西瓜.png", 150, 150, false, false)));
  private Label a3 = new Label("", new ImageView(new Image("lib\\柠檬.png", 150, 150, false, false)));
  private Button btn1 = new Button("开始游戏");
  private Button btn2 = new Button("点击停止");
  {
    a1.setStyle("-fx-border-color:#ccc;");
    a1.setLayoutX(60);
    a1.setLayoutY(40);
    pane.getChildren().add(a1);
    a2.setStyle("-fx-border-color:#ccc;");
    a2.setLayoutX(225);
    a2.setLayoutY(40);
    pane.getChildren().add(a2);
    a3.setStyle("-fx-border-color:#ccc;");
    a3.setLayoutX(390);
    a3.setLayoutY(40);
    pane.getChildren().add(a3);
    btn1.setLayoutX(180);
    btn1.setLayoutY(220);
    btn1.setPrefSize(100, 40);
    pane.getChildren().add(btn1);
    btn2.setLayoutX(340);
    btn2.setLayoutY(220);
    btn2.setPrefSize(100, 40);
    pane.getChildren().add(btn2);
  }
  @Override
  public void start(Stage stage) throws Exception {
    // 创建一个场景
    Scene scene = new Scene(pane, 600, 300);
    stage.setScene(scene);
    stage.show();
    /**
     * 开始游戏点击事件
     */
    btn1.setOnAction(e -> {
      flag = true;
      new Thread() {
        public void run() {
          while (flag) {
            try {
              Thread.sleep(timer);
            } catch (InterruptedException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
            }
            Platform.runLater(new Runnable() {
              @Override
              public void run() {
                int sj1 = rd.nextInt(6);
                a1.setGraphic(new ImageView(new Image(images[sj1])));
                int sj2 = rd.nextInt(6);
                a2.setGraphic(new ImageView(new Image(images[sj2])));
                int sj3 = rd.nextInt(6);
                a3.setGraphic(new ImageView(new Image(images[sj3])));
              }
            });
            timer += 6;
            if (timer >= 300) {
              timer = 50;
              break;
            }
          }
        }
      }.start();
    });
    /**
     * 停止点击事件
     */
    btn2.setOnAction(e -> {
      flag = false;
    });
  }
  public static void main(String[] args) {
    launch();
  }
}

运行效果:

2. FX版无限聊天

1. 服务器

package com.net;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
/**
 * 服务器
 * 
 * @author 云村小威
 *
 * @2023年7月19日 下午9:40:54
 */
public class ServerFx extends Application {
  /**
   * 总布局 BorderPane
   */
  BorderPane bor = new BorderPane();
  // 上
  HBox hb = new HBox();
  TextField t = new TextField();
  Button btn = new Button("启动服务");
  {
    hb.getChildren().addAll(t, btn);
    hb.setAlignment(Pos.CENTER);
    hb.setPadding(new Insets(20));
    hb.setSpacing(20);
    btn.setPrefSize(100, 40);
    bor.setTop(hb);
  }
  // 中
  TextArea ta = new TextArea();
  {
    ta.setEditable(false); // 文本域不可修改
    ta.setStyle("-fx-border-color: #ccc;-fx-border-width: 10 10 10 10;");
    bor.setCenter(ta);
  }
  // 下
  HBox hb2 = new HBox();
  TextField text = new TextField();
  Button btn1 = new Button("发送");
  {
    hb2.getChildren().addAll(text, btn1);
    hb2.setAlignment(Pos.CENTER);
    hb2.setPadding(new Insets(20));
    hb2.setSpacing(20);
    bor.setBottom(hb2);
  }
  /**
   * 将通信对象声明在start方法外面
   */
  Socket s = null;
  ServerSocket ss = null;
  /**
   * 定义所需的流对象 扩大权限方便关闭流
   */
  InputStream inputStream = null;
  BufferedReader br = null;
  OutputStream outputStream = null;
  BufferedWriter bw = null;
  @Override
  public void start(Stage stage) throws Exception {
    stage.setTitle("服务器");
    Scene scene = new Scene(bor, 800, 700);
    stage.setScene(scene);
    stage.show();
    /**
     * 启动服务点击事件
     */
    btn.setOnAction(e -> {
      // 创建服务器
      try {
        ss = new ServerSocket(Integer.parseInt(t.getText()));
      } catch (IOException e1) {
        e1.printStackTrace();
      }
      ta.appendText("服务已启动,等待客户端连接...\n");
      // 开启一个线程 自动接受客户端发送的信息
      new Thread() {
        public void run() {
          // 多线程控制接受客户端信息
          try {
            s = ss.accept();
            ta.appendText("客户端已连接...\n");
          } catch (IOException e) {
            e.printStackTrace();
          }
          while (true) {
            // 读取 获取输入流
            try {
              inputStream = s.getInputStream();
              br = new BufferedReader(new InputStreamReader(inputStream));
              String count = br.readLine();
              ta.appendText("\t客户端:" + count + "\n");
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        }
      }.start();
    });
    /**
     * 发送信息点击事件
     */
    btn1.setOnAction(e -> {
      try {
        outputStream = s.getOutputStream();
        bw = new BufferedWriter(new OutputStreamWriter(outputStream));
        // 获取文本框内容
        String count = text.getText();
        bw.write(count);
        bw.newLine();
        bw.flush();
        ta.appendText("服务器:" + count + "\n");
        text.setText("");
      } catch (IOException e1) {
        e1.printStackTrace();
      }
    });
  }
  public static void main(String[] args) {
    launch();
  }
}

2. 客户端

package com.net;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
/**
 * 客户端
 * 
 * @author 云村小威
 *
 * @2023年7月19日 下午9:41:46
 */
public class ClientFx extends Application {
  /**
   * 总布局 BorderPane
   */
  BorderPane bor = new BorderPane();
  // 上
  HBox hb = new HBox();
  Text ip = new Text("IP");
  TextField t = new TextField("127.0.0.1");
  Text tcp = new Text("端口号");
  TextField t2 = new TextField();
  Button btn = new Button("连接");
  {
    t.setDisable(true);// 设置ip地址不可编辑
    hb.getChildren().addAll(ip, t, tcp, t2, btn);
    hb.setAlignment(Pos.CENTER);
    hb.setPadding(new Insets(20));
    hb.setSpacing(20);
    btn.setPrefSize(100, 40);
    bor.setTop(hb);
  }
  // 中
  TextArea ta = new TextArea();
  {
    ta.setEditable(false); // 文本域不可修改
    ta.setStyle("-fx-border-color: #ccc;-fx-border-width: 10 10 10 10;");
    bor.setCenter(ta);
  }
  // 下
  HBox hb2 = new HBox();
  TextField text = new TextField();
  Button btn1 = new Button("发送");
  {
    hb2.getChildren().addAll(text, btn1);
    hb2.setAlignment(Pos.CENTER);
    hb2.setPadding(new Insets(20));
    hb2.setSpacing(20);
    bor.setBottom(hb2);
  }
  /**
   * 定义所需的流对象 扩大权限方便关闭流
   */
  InputStream inputStream = null;
  BufferedReader br = null;
  OutputStream outputStream = null;
  BufferedWriter bw = null;
  // 定义客户端对象
  Socket s = new Socket();
  @Override
  public void start(Stage stage) throws Exception {
    stage.setTitle("客户端");
    stage.setResizable(false); // 设置窗口不可动
    Scene scene = new Scene(bor, 800, 700);
    stage.setScene(scene);
    stage.show();
    /**
     * 连接点击事件
     */
    btn.setOnAction(e -> {
      String ip = t.getText(); // 获取ip地址
      int port = Integer.parseInt(t2.getText());
      try {
        s = new Socket(ip, port);
        ta.appendText("客户端成功连接服务器...\n");
      } catch (IOException e1) {
        e1.printStackTrace();
      }
      // 开启一个线程 (让它自动接受数据)
      new Thread() {
        public void run() {
          while (true) {
            try {
              inputStream = s.getInputStream();
              br = new BufferedReader(new InputStreamReader(inputStream));
              String count = br.readLine();
              ta.appendText("\t服务器:" + count + "\n");
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        }
      }.start();
    });
    /**
     * 发送信息点击事件
     */
    btn1.setOnAction(e -> {
      try {
        outputStream = s.getOutputStream();
        bw = new BufferedWriter(new OutputStreamWriter(outputStream));
        // 获取文本框内容
        String count = text.getText();
        bw.write(count);
        bw.newLine();
        bw.flush();
        ta.appendText("客户端:" + count + "\n");
        text.setText("");
      } catch (IOException e1) {
        e1.printStackTrace();
      }
    });
  }
  public static void main(String[] args) {
    launch();
  }
}

运行效果:

相关文章
|
5天前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
4天前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。
|
4天前
|
Java 开发者
Java多线程编程的艺术与实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的技术文档,本文以实战为导向,通过生动的实例和详尽的代码解析,引领读者领略多线程编程的魅力,掌握其在提升应用性能、优化资源利用方面的关键作用。无论你是Java初学者还是有一定经验的开发者,本文都将为你打开多线程编程的新视角。 ####
|
3天前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####
|
9天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
32 9
|
6天前
|
安全 Java 开发者
Java多线程编程中的常见问题与解决方案
本文深入探讨了Java多线程编程中常见的问题,包括线程安全问题、死锁、竞态条件等,并提供了相应的解决策略。文章首先介绍了多线程的基础知识,随后详细分析了每个问题的产生原因和典型场景,最后提出了实用的解决方案,旨在帮助开发者提高多线程程序的稳定性和性能。
|
12天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
9天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
12天前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
26 3
|
11天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
下一篇
无影云桌面