Java多线程如何实现高效的并发控制?

2026-05-22 08:121阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2112个文字,预计阅读时间需要9分钟。

Java多线程如何实现高效的并发控制?

进程(process)和线程(Thread)是程序执行的基本单元。其中,main方法称为主线程,它是程序的入口,用于启动整个程序。

线程创建的三种方式:

1.继承Thread类:`public class Test extends Thread`

2.实现Runnable接口:`public class Test implements Runnable`

3.实现Callable接口:`public class Test implements Callable`

这些方式均可以创建线程,但实现方式不同。

Java多线程如何实现高效的并发控制?

进程 process 线程 Thread

main称之为主线程,为系统的入口,用于执行整个程序

线程创建的三种方式:

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口

继承Thread类:

public class Test extends Thread { @Override public void run() { //run 方法线程体 for (int i = 0; i < 20; i++) { System.out.println(i); } } public static void main(String[] args) { //创建一个线程对象 Test t=new Test(); //开启线程 t.start(); //知道跟run方法的区别 //main线程 主线程 for (int i = 0; i < 2000; i++) { System.out.println("main方法"+i); } } }

分析调用start跟run方法的区别:

  调用start方法主线程跟子线程会并行交替执行。 run方法主线程会先执行完之后再执行子线程。

总结:注意,线程开启不一定立即执行,是由cpu调度的。

实现Runnable接口:

  实现该接口,重写run方法,执行线程需要丢入runnable接口实现类。调用start方法

public class Test implements Runnable { @Override public void run() { //run 方法线程体 for (int i = 0; i < 20; i++) { System.out.println(i); } } public static void main(String[] args) { //创建Runnable接口的实现类对象 Test t=new Test(); //创建线程对象,通过线程对象来开启我们的线程,代理 Thread t1=new Thread(t); t1.start(); //main线程 主线程 for (int i = 0; i < 2000; i++) { System.out.println("main方法"+i); } } }

推荐使用Runnable接口实现多线程

小结:

关于线程的不安全性示例代码:

public class Test implements Runnable { private int ticketNum=10; @Override public void run() { while(true){ if (ticketNum<=0){break;} try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } //run 方法线程体 System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum--+"张票"); } } public static void main(String[] args) { //创建Runnable接口的实现类对象 Test t=new Test(); //创建线程对象,通过线程对象来开启我们的线程,代理 new Thread(t,"小红").start(); new Thread(t,"小明").start(); new Thread(t,"小张").start(); } }

输出结果为:

此时可以看到 同一张票被两个不同的人抢到,此时就表明存在线程安全问题。当多个线程操作一个对象的时候会出现线程安全问题。

继续看以下龟兔赛跑代码示例:

public class Test3 implements Runnable{ private String winner; @Override public void run() { for (int i = 0; i < 101; i++) { //模拟兔子休息 if (Thread.currentThread().getName().equals("兔子")&&i%10==0){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } if (GameOver(i)){ break; } System.out.println(Thread.currentThread().getName()+"--->跑了"+i+"步"); } } //判断比赛是否结束 private Boolean GameOver(int step){ if (winner!=null){ return true; }else { if (step>=100){ winner=Thread.currentThread().getName(); System.out.println("胜利者是---->"+winner); } } return false; } public static void main(String[] args) { Test3 t=new Test3(); new Thread(t,"乌龟").start(); new Thread(t,"兔子").start(); } }

当线程为兔子时模拟兔子睡觉,即Thread.sleep()。胜利者一定是乌龟。

实现Callable接口:

  

---------------------------------------------------------------------------------------

扩展知识:

关于静态代理:

    静态代理模式总结:真实对象跟代理对象都要实现同一个接口,代理对象要代理真实角色。

    好处:代理对象可以在做很多真实对象无法实现的事情,真实对象可以专注自己可以做的事。

Thread类的底层实现原理就是使用静态代理类。

函数式接口:

  • 任何接口只包含唯一一个抽象方法,那么它就是一个函数式接口。
  • 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。

-----------------------------------------------------------------------------------------

线程状态:

  

线程方法:

  

停止线程:

线程礼让:yield 方法。因为线程执行由cpu调度,所以礼让不一定成功,看cpu心情。

线程插队:join 方法。即两个线程在执行的时候,本来主线程会先执行完毕再执行子线程,但是此刻我设定一个条件当主线程执行到某一步的时候

我使用join方法,此时主线程会进行等待,等子线程执行完毕之后才会继续执行。

关于线程的状态有以下几种;

线程优先级:

线程分为 用户线程 守护线程

关于线程锁:

    每个对象都有一把锁

  重点:synchronized

1. 同步方法,锁的是This。上面抢票示例代码会出现线程安全问题,当我们在“抢票”的方法上使用 synchronized 时,表示该方法

在执行时会被上锁,只有该方法执行完之后,锁会被释放,后续对象才可以拿到该锁进行处理。Buy()方法上使用锁代表锁的是BuyTicket对象。

2 . 同步块

示例代码:

public class Test2 { public static void main(String[] args) throws InterruptedException { List<String> arrList=new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(( )->{ arrList.add(Thread.currentThread().getName()); }).start(); } Thread.sleep(2000); System.out.println(arrList.stream().count()); } }

结果:

从输出结果可看出并未讲10000个线程对象添加到集合中,原因是 两个线程在同一时间将同一个对象添加到了同一个内存位置,因此集合中的对象会出现覆盖的情况,所以产生了此种情况。

处理方法:

public class Test2 { public static void main(String[] args) throws InterruptedException { List<String> arrList=new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(( )->{ synchronized(arrList){ arrList.add(Thread.currentThread().getName()); } }).start(); } Thread.sleep(2000); System.out.println(arrList.stream().count()); } }

将“arrList”对象添加锁,同步代码块。

JUC:CopyOnWriteArrayList 此集合为一个线程安全的集合,与arrayList集合不同。是java.util.concurrent包下的

关于死锁:

不要在一个锁还未被释放的时候就想着去取另一个锁,这种情况就容易造成死锁的产生。即在“synchronized”代码块中嵌套"synhronized"

Lock锁:

Lock锁与synchronized对比:

生产消费者模型:

1.管程法(生产者,消费者,产品,缓冲区)

public class Test3 { public static void main(String[] args) { SynContainer synContainer=new SynContainer(); new Productor(synContainer).start(); new Customer(synContainer).start(); } } //生产者 class Productor extends Thread{ SynContainer synContainer; public Productor(SynContainer synContainer){ this.synContainer=synContainer; } @Override public void run() { //生产100只鸡仔 for (int i = 1; i < 100; i++) { try { synContainer.push(new Chicken(i)); System.out.println("生产了---->"+i+"只鸡仔"); } catch (InterruptedException e) { e.printStackTrace(); } } } } //消费者 class Customer extends Thread{ SynContainer synContainer; public Customer(SynContainer synContainer){ this.synContainer=synContainer; } @Override public void run() { //消费100只鸡仔 for (int i = 1; i < 100; i++) { try { System.out.println("消费了"+synContainer.Consumer().id+"鸡仔"); } catch (InterruptedException e) { e.printStackTrace(); } } } } //产品 class Chicken{ int id; public Chicken(int id) { this.id = id; } } //缓冲区 class SynContainer{ //定义一个缓冲区的大小 Chicken[] chickens=new Chicken[10]; //定义鸡的个数 int count=0; //生产鸡仔 public synchronized void push(Chicken chicken) throws InterruptedException { //当生产的鸡仔达到容量时 if (count== chickens.length){ //此时应该暂停生产 this.wait(); } //如果没有满 我们需要丢入鸡仔 chickens[count]=chicken; count++; //通知消费者可以开始消费 this.notifyAll(); } //消费鸡仔 public synchronized Chicken Consumer() throws InterruptedException { //当没有鸡仔可以消费时 if (count==0){ //此时消费者应该处于等待,等待生产者继续生产 this.wait(); } //否则鸡仔将被消费 count--; Chicken chicken = chickens[count]; //并告知生产者鸡仔已经被消费 this.notifyAll(); return chicken; } }

线程池:

本文共计2112个文字,预计阅读时间需要9分钟。

Java多线程如何实现高效的并发控制?

进程(process)和线程(Thread)是程序执行的基本单元。其中,main方法称为主线程,它是程序的入口,用于启动整个程序。

线程创建的三种方式:

1.继承Thread类:`public class Test extends Thread`

2.实现Runnable接口:`public class Test implements Runnable`

3.实现Callable接口:`public class Test implements Callable`

这些方式均可以创建线程,但实现方式不同。

Java多线程如何实现高效的并发控制?

进程 process 线程 Thread

main称之为主线程,为系统的入口,用于执行整个程序

线程创建的三种方式:

  1. 继承Thread类
  2. 实现Runnable接口
  3. 实现Callable接口

继承Thread类:

public class Test extends Thread { @Override public void run() { //run 方法线程体 for (int i = 0; i < 20; i++) { System.out.println(i); } } public static void main(String[] args) { //创建一个线程对象 Test t=new Test(); //开启线程 t.start(); //知道跟run方法的区别 //main线程 主线程 for (int i = 0; i < 2000; i++) { System.out.println("main方法"+i); } } }

分析调用start跟run方法的区别:

  调用start方法主线程跟子线程会并行交替执行。 run方法主线程会先执行完之后再执行子线程。

总结:注意,线程开启不一定立即执行,是由cpu调度的。

实现Runnable接口:

  实现该接口,重写run方法,执行线程需要丢入runnable接口实现类。调用start方法

public class Test implements Runnable { @Override public void run() { //run 方法线程体 for (int i = 0; i < 20; i++) { System.out.println(i); } } public static void main(String[] args) { //创建Runnable接口的实现类对象 Test t=new Test(); //创建线程对象,通过线程对象来开启我们的线程,代理 Thread t1=new Thread(t); t1.start(); //main线程 主线程 for (int i = 0; i < 2000; i++) { System.out.println("main方法"+i); } } }

推荐使用Runnable接口实现多线程

小结:

关于线程的不安全性示例代码:

public class Test implements Runnable { private int ticketNum=10; @Override public void run() { while(true){ if (ticketNum<=0){break;} try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } //run 方法线程体 System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum--+"张票"); } } public static void main(String[] args) { //创建Runnable接口的实现类对象 Test t=new Test(); //创建线程对象,通过线程对象来开启我们的线程,代理 new Thread(t,"小红").start(); new Thread(t,"小明").start(); new Thread(t,"小张").start(); } }

输出结果为:

此时可以看到 同一张票被两个不同的人抢到,此时就表明存在线程安全问题。当多个线程操作一个对象的时候会出现线程安全问题。

继续看以下龟兔赛跑代码示例:

public class Test3 implements Runnable{ private String winner; @Override public void run() { for (int i = 0; i < 101; i++) { //模拟兔子休息 if (Thread.currentThread().getName().equals("兔子")&&i%10==0){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } if (GameOver(i)){ break; } System.out.println(Thread.currentThread().getName()+"--->跑了"+i+"步"); } } //判断比赛是否结束 private Boolean GameOver(int step){ if (winner!=null){ return true; }else { if (step>=100){ winner=Thread.currentThread().getName(); System.out.println("胜利者是---->"+winner); } } return false; } public static void main(String[] args) { Test3 t=new Test3(); new Thread(t,"乌龟").start(); new Thread(t,"兔子").start(); } }

当线程为兔子时模拟兔子睡觉,即Thread.sleep()。胜利者一定是乌龟。

实现Callable接口:

  

---------------------------------------------------------------------------------------

扩展知识:

关于静态代理:

    静态代理模式总结:真实对象跟代理对象都要实现同一个接口,代理对象要代理真实角色。

    好处:代理对象可以在做很多真实对象无法实现的事情,真实对象可以专注自己可以做的事。

Thread类的底层实现原理就是使用静态代理类。

函数式接口:

  • 任何接口只包含唯一一个抽象方法,那么它就是一个函数式接口。
  • 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。

-----------------------------------------------------------------------------------------

线程状态:

  

线程方法:

  

停止线程:

线程礼让:yield 方法。因为线程执行由cpu调度,所以礼让不一定成功,看cpu心情。

线程插队:join 方法。即两个线程在执行的时候,本来主线程会先执行完毕再执行子线程,但是此刻我设定一个条件当主线程执行到某一步的时候

我使用join方法,此时主线程会进行等待,等子线程执行完毕之后才会继续执行。

关于线程的状态有以下几种;

线程优先级:

线程分为 用户线程 守护线程

关于线程锁:

    每个对象都有一把锁

  重点:synchronized

1. 同步方法,锁的是This。上面抢票示例代码会出现线程安全问题,当我们在“抢票”的方法上使用 synchronized 时,表示该方法

在执行时会被上锁,只有该方法执行完之后,锁会被释放,后续对象才可以拿到该锁进行处理。Buy()方法上使用锁代表锁的是BuyTicket对象。

2 . 同步块

示例代码:

public class Test2 { public static void main(String[] args) throws InterruptedException { List<String> arrList=new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(( )->{ arrList.add(Thread.currentThread().getName()); }).start(); } Thread.sleep(2000); System.out.println(arrList.stream().count()); } }

结果:

从输出结果可看出并未讲10000个线程对象添加到集合中,原因是 两个线程在同一时间将同一个对象添加到了同一个内存位置,因此集合中的对象会出现覆盖的情况,所以产生了此种情况。

处理方法:

public class Test2 { public static void main(String[] args) throws InterruptedException { List<String> arrList=new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(( )->{ synchronized(arrList){ arrList.add(Thread.currentThread().getName()); } }).start(); } Thread.sleep(2000); System.out.println(arrList.stream().count()); } }

将“arrList”对象添加锁,同步代码块。

JUC:CopyOnWriteArrayList 此集合为一个线程安全的集合,与arrayList集合不同。是java.util.concurrent包下的

关于死锁:

不要在一个锁还未被释放的时候就想着去取另一个锁,这种情况就容易造成死锁的产生。即在“synchronized”代码块中嵌套"synhronized"

Lock锁:

Lock锁与synchronized对比:

生产消费者模型:

1.管程法(生产者,消费者,产品,缓冲区)

public class Test3 { public static void main(String[] args) { SynContainer synContainer=new SynContainer(); new Productor(synContainer).start(); new Customer(synContainer).start(); } } //生产者 class Productor extends Thread{ SynContainer synContainer; public Productor(SynContainer synContainer){ this.synContainer=synContainer; } @Override public void run() { //生产100只鸡仔 for (int i = 1; i < 100; i++) { try { synContainer.push(new Chicken(i)); System.out.println("生产了---->"+i+"只鸡仔"); } catch (InterruptedException e) { e.printStackTrace(); } } } } //消费者 class Customer extends Thread{ SynContainer synContainer; public Customer(SynContainer synContainer){ this.synContainer=synContainer; } @Override public void run() { //消费100只鸡仔 for (int i = 1; i < 100; i++) { try { System.out.println("消费了"+synContainer.Consumer().id+"鸡仔"); } catch (InterruptedException e) { e.printStackTrace(); } } } } //产品 class Chicken{ int id; public Chicken(int id) { this.id = id; } } //缓冲区 class SynContainer{ //定义一个缓冲区的大小 Chicken[] chickens=new Chicken[10]; //定义鸡的个数 int count=0; //生产鸡仔 public synchronized void push(Chicken chicken) throws InterruptedException { //当生产的鸡仔达到容量时 if (count== chickens.length){ //此时应该暂停生产 this.wait(); } //如果没有满 我们需要丢入鸡仔 chickens[count]=chicken; count++; //通知消费者可以开始消费 this.notifyAll(); } //消费鸡仔 public synchronized Chicken Consumer() throws InterruptedException { //当没有鸡仔可以消费时 if (count==0){ //此时消费者应该处于等待,等待生产者继续生产 this.wait(); } //否则鸡仔将被消费 count--; Chicken chicken = chickens[count]; //并告知生产者鸡仔已经被消费 this.notifyAll(); return chicken; } }

线程池: