>Java >java지도 시간 >Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

WBOY
WBOY앞으로
2022-06-17 13:46:482455검색

이 기사에서는 프로그램, 프로세스, 스레드, 스레드를 생성하는 세 가지 방법, 스레드 상태 등 멀티스레딩과 관련된 문제를 주로 소개하는 java에 대한 관련 지식을 제공합니다. 아래 내용이 모두에게 도움이 되기를 바랍니다.

Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

추천 학습: "java 동영상 튜토리얼"

1. 프로그램, 프로세스, 스레드

1.1 프로그램이란

프로그램(program): 특정 언어로 작성되어 특정 작업을 완료합니다. 작업 지침 세트는 정적 코드 조각입니다. (프로그램은 정적이다)

Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

1.2 프로세스란 무엇인가

Process(프로세스) : 프로그램의 실행 프로세스, 실행 중인 프로그램, 프로세스는 자원 할당의 단위이며, 각 프로세스는 서로 다른 메모리 영역을 할당합니다. (프로세스는 동적입니다.) 프로세스의 생명주기는 생성, 존재, 소멸이라는 고유한 프로세스를 가지고 있습니다

Java 멀티스레딩 메커니즘에 대해 자세히 알아보기
현재 운영 체제는 여러 프로세스를 지원하며 한 번에 여러 프로세스를 실행할 수 있습니다. 프로세스 ID로 구별
Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

1.3 스레드란

스레드(thread): 프로세스의 실행 경로이며 CUP의 기본 스케줄링 단위이기도 합니다. 프로세스는 하나 이상의 스레드로 구성됩니다. , 각각은 서로 다른 작업을 완료합니다. 동시에 여러 스레드에 의해 실행되는 작업을 멀티스레딩이라고 합니다.

Java 멀티스레딩 메커니즘에 대해 자세히 알아보기
Java 멀티스레딩 메커니즘에 대해 자세히 알아보기
스레드 구성

모든 스레드가 갖는 기본 구성 요소:

  • CPU 타임 슬라이스: 운영 체제(OS)는 실행 시간을 각 스레드에 할당합니다.
  • 실행 데이터: 힙 공간(스레드가 사용해야 하는 개체 저장, 여러 스레드가 힙에서 개체를 공유할 수 있음) 스택 공간(스레드가 사용해야 하는 지역 변수 저장, 각 스레드는 독립적인 스택을 가짐)

스레드의 특징

  • 스레드 선점형 실행(고효율, 단일 스레드가 장기간 CPU를 독점하는 것을 방지할 수 있음)
  • 싱글 코어 CPU가 실행될 때 타임 슬라이스에 따라 실행되며, 하나의 타임 슬라이스가 스레드에서 실행될 수 있습니다. 타임 슬라이스가 매우 짧기 때문에 우리가 느끼는 것은 이들이 "동시에" 발생한다는 것입니다.
  • 멀티 코어 CPU는 진정한 단일 타임 슬라이스에서 여러 스레드의 동시 실행을 달성합니다.
  • 싱글 코어 CPU에서는 거시적으로는 동시 실행, 미시적으로는 순차적 실행

1.4 프로세스와 스레드의 차이점

  • 프로세스는 운영체제의 자원 할당의 기본 단위이고, 스레드는 CPU의 기본 스케줄링 단위입니다.
  • 프로그램이 실행된 후에는 적어도 하나의 프로세스가 있습니다.
  • 프로세스에는 여러 스레드가 포함될 수 있습니다. 최소한 하나의 스레드여야 합니다. 그렇지 않으면 프로세스가 의미가 없습니다.
  • 데이터 세그먼트 주소는 프로세스 간에 공유될 수 없지만 동일한 프로세스의 스레드 간에는 공유될 수 있습니다.

2. 스레드를 생성하는 세 가지 방법

2.1 Thread 클래스 상속 및 run() 메서드 재정의

특정 구현

1. Thread 클래스 상속
2. run() 메서드 재정의
3 . 하위 클래스 객체 생성
4. start() 메소드 호출(PS: run() 메소드를 호출하지 마십시오. 이는 일반적으로 객체 메소드를 호출하고 스레드를 시작하는 것과 같습니다)

상속 클래스

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i " + i);
        }
    }}

테스트 클래스

public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        for (int i = 1; i "+i);
        }
    }}

Result
Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

스레드 ID 및 이름 가져오기

getId()//스레드 ID 가져오기, 각 스레드에는 고유한 ID가 있습니다. idgetId()//获取线程的id,每个线程都有自己的id
getName()//获取线程名字
Thread.currentThread()//获取当前线程

代码

public class TestThread {

	public static void main(String[] args) {
		MyThread t=new MyThread();
		t.start();
        //只能在继承Thread类的情况下用
		System.out.println("线程id:"+t.getId());
		System.out.println("线程名字:"+t.getName());
        //调用Thread类的静态方法获取当前线程(这里获取的是主线程)
		System.out.println("线程id:"+Thread.currentThread().getId());
		System.out.println("线程名字:"+Thread.currentThread().getName());
	}}

Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

修改线程名称

只能修改线程的名称,不能修改线程的id(id是由系统自动分配)
1、使用线程子类的构造方法赋值
2、调用线程对象的setName()getName() //스레드 이름 가져오기

Thread.currentThread()//현재 스레드 가져오기

code

🎜
public class MyThread extends Thread{
	public MyThread() {}//无参构造器
	public MyThread(String name) {
		super(name);
	}
	public void run() {
		for(int i=1;i🎜<img src="https://img.php.cn%20/upload/article/000/000/067/57e2f75a769022a530da131db2d02e06-6.png" alt="여기에 그림 설명 삽입">🎜🎜스레드 이름 수정🎜🎜🎜이름만 스레드는 수정이 가능하지만 스레드의 id는 수정이 불가능합니다. (id는 시스템에서 자동으로 할당됩니다.)🎜 1. 스레드 서브클래스의 생성자 메소드를 사용하여 값을 할당합니다🎜 2. <code>setName()을 호출합니다. </code> 스레드 객체의 메서드🎜🎜🎜🎜Code🎜🎜<pre class="brush:php;toolbar:false">public class MyThread extends Thread{
	public MyThread() {}//无参构造器
	public MyThread(String name) {
		super(name);
	}
	public void run() {
		for(int i=1;i<pre class="brush:php;toolbar:false">public class TestThread {

	public static void main(String[] args) {
		MyThread t1=new MyThread("子线程1");//通过构造方法
		MyThread t2=new MyThread();
		t2.setName("子线程2");
		System.out.println("线程t1的id:"+t1.getId()+" 名称:"+t1.getName());
		System.out.println("线程t2的id:"+t2.getId()+" 名称:"+t2.getName());
	}}

Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

2.2 实现Runnable接口实现run()方法

具体实现

1.实现Runnable接口
2.实现run()方法
3.创建实现类对象
4.创建线程类对象
5.调用start()方法

实现接口

public class MyRunnable implements Runnable{//实现接口
	@Override
	public void run() {//实现run方法
		// TODO Auto-generated method stub
		for(int i=1;i<p><strong>测试类</strong></p><pre class="brush:php;toolbar:false">public class TestRunnable {
	public static void main(String[] args) {
		//1.创建MyRunnable对象,表示线程执行的功能
		Runnable runnable=new MyRunnable();
		//2.创建线程对象
		Thread th=new Thread(runnable);
		//3.启动线程
		th.start();
		for(int i=1;i<p><img src="https://img.php.cn/upload/article/000/000/067/beb0b9e2f551ceb35ac7ab846edfec3d-8.png" alt="Java 멀티스레딩 메커니즘에 대해 자세히 알아보기"></p><h3>使用匿名内部类</h3><blockquote><p>如果一个线程方法我们只使用一次,那么就不必设置一个单独的类,就可以使用匿名内部类实现该功能</p></blockquote><pre class="brush:php;toolbar:false">public class TestRunnable {
	public static void main(String[] args) {
		Runnable runnable=new Runnable() {			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for(int i=1;i<h2>2.3 实现Callable接口</h2><h3>Callable和Thread、Runnable比较</h3><blockquote>
<p>对比继承<code>Thread</code>类和实现<code>Runnable</code>接口创建线程的方式发现,都需要有一个<code>run()</code>方法,但是这个run()方法有不足:</p>
<ul>
<li>没有返回值</li>
<li>不能抛出异常</li>
</ul>
<p>基于上面的两个不足,在JDK1.5以后出现了第三种创建线程的方式:实现<code>Callable</code>接口</p>
<p>实现<code>Callable</code>接口的好处:</p>
<ul>
<li>有返回值</li>
<li>能抛出异常</li>
</ul>
<p>缺点:</p>
<ul><li>创建线程比较麻烦</li></ul>
</blockquote><blockquote>
<p>1.实现<code>Callable</code>接口,可以不带泛型,如果不带泛型,那么call方法的返回值就是<code>Object</code>类型</p>
<p>2.如果带泛型,那么call的返回值就是泛型对应的类型</p>
<p>3.从call方法看到:方法有返回值,可以抛出异常</p>
</blockquote><h3>具体实现</h3><p><strong>实现接口</strong></p><pre class="brush:php;toolbar:false">import java.util.Random;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class TestCallable implements Callable<integer>{

	@Override
	public Integer call() throws Exception {
		// TODO Auto-generated method stub
		return new Random().nextInt(10);
	}}</integer>

测试类

import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class Test {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		TestCallable tc=new TestCallable();
		FutureTask<integer> ft=new FutureTask(tc);
		//创建线程对象
		Thread th=new Thread(ft);
		th.start();
		//获取线程得到的返回值
		Integer In=ft.get();
		System.out.println(In);
	}}</integer>

三、线程的状态

3.1 基本四状态

Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

3.2 等待状态

Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

3.3 阻塞状态

Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

四、线程常用的方法

  • 休眠(当前线程主动休眠millis毫秒)public static void sleep(long millis)

  • 放弃(当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片)public static void yield()

  • 加入(当一个线程调用了join方法,这个线程就会先被执行,它执行结束以后才可以去执行其余的线程)public final void join()//必须先start(),在join(),才有效

  • 优先级(线程优先级为1–10,默认为5,优先级越高,表示获取CPU机会越多)线程对象.setPriority()

  • 守护线程

    • 线程对象.setDaemon(true);设置为守护线程
    • 线程有两类:用户线程(前台线程)、守护线程(后台线程)
    • 如果程序中所有前台线程都执行完毕了,后台线程也会自动结束
    • 垃圾回收器线程属于守护线程

4.1 线程休眠(sleep)

public static void sleep(long millis)当前线程主动休眠millis毫秒

子线程

public class SleepThread extends Thread{
    @Override
    public void run() {
        for (int i = 1; i <p><mark>PS:sleep()的异常在run方法中是不能抛出的,只能用try–catch处理</mark><br><strong>测试类</strong></p><pre class="brush:php;toolbar:false">public class Test01 {
    public static void main(String[] args) {
        SleepThread sleepThread = new SleepThread();
        sleepThread.start();
    }}

结果:每次间隔100ms输出一次
Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

4.2 线程放弃(yield)

public static void yield()当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片

子线程

public class YieldThread extends Thread{
    @Override
    public void run() {
        for (int i=1;i<p><strong>测试类</strong></p><pre class="brush:php;toolbar:false">public class Test01 {
    public static void main(String[] args) {
        YieldThread yieldThread01 = new YieldThread();
        YieldThread yieldThread02 = new YieldThread();
        yieldThread01.start();
        yieldThread02.start();
    }}

结果:基本都会交替进行,也会有一个线程连输出
Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

4.3 线程加入(join)

当一个线程调用了join方法,这个线程就会先被执行,它执行结束以后才可以去执行其余的线程,必须先start,再join才有效

子线程

public class JoinThread extends Thread{
    @Override
    public void run() {
        for (int i=1;i<p><strong>测试类</strong></p><pre class="brush:php;toolbar:false">public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        for (int i=1;i<p><strong>结果:当主线程打印到5的时候,这时候子线程加入进来,就先执行完子线程,在执行主线程</strong><br><img src="https://img.php.cn/upload/article/000/000/067/da3747d2bd3ee6a154b0a90e8cc9f0a7-14.png" alt="Java 멀티스레딩 메커니즘에 대해 자세히 알아보기"></p><h2>4.4 守护线程(setDaemon)</h2><blockquote><p>将子线程设置为主线程的伴随线程,主线程停止的时候,子线程也不要继续执行了<br> 注意:<strong>先设置,在启动</strong></p></blockquote><p><strong>子线程</strong></p><pre class="brush:php;toolbar:false">public class TestThread extends Thread{
    @Override
    public void run() {
        for(int i=1;i<p><strong>测试类</strong></p><pre class="brush:php;toolbar:false">public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        TestThread daemonThread = new TestThread();
        daemonThread.setDaemon(true);//设置守护线程
        daemonThread.start();
        for (int i=1;i<p><strong>结果:当主线程结束时,子线程也跟着结束,并不会继续执行下去打印输出</strong><br><img src="https://img.php.cn/upload/article/000/000/067/da3747d2bd3ee6a154b0a90e8cc9f0a7-15.png" alt="Java 멀티스레딩 메커니즘에 대해 자세히 알아보기"></p><h2>4.5 线程优先级(setPriority)</h2><blockquote><p>线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多<br><img src="https://img.php.cn/upload/article/000/000/067/da3747d2bd3ee6a154b0a90e8cc9f0a7-16.png" alt="Java 멀티스레딩 메커니즘에 대해 자세히 알아보기"></p></blockquote><p><strong>子线程</strong></p><pre class="brush:php;toolbar:false">public class TestThread extends Thread{
    @Override
    public void run() {
        for(int i=1;i<p><strong>测试</strong></p><pre class="brush:php;toolbar:false">public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        TestThread th1 = new TestThread();
        TestThread th2 = new TestThread();
        TestThread th3 = new TestThread();
        th1.setPriority(10);//设置线程1优先级10
        th1.start();
        th2.start();//线程2优先级默认不变,为5
        th3.setPriority(1);//设置线程3优先级为1
        th3.start();
    }}

结果:优先级(th1>th2>th3)线程3应该在最后打印
Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

五、线程安全问题

5.1 卖票案例

需求:模拟三个窗口,每个窗口有100个人,同时抢10张票
Java 멀티스레딩 메커니즘에 대해 자세히 알아보기
使用继承Runnable接口的方法

public class BuyTicketRunnable implements Runnable{
	
	private int ticketNum=10;
	@Override
	public void run() {
		for(int i=1;i<p><strong>测试方法</strong></p><pre class="brush:php;toolbar:false">public class BuyTicketTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Runnable runnable=new BuyTicketRunnable();
		
		Thread th1=new Thread(runnable,"窗口1");
		Thread th2=new Thread(runnable,"窗口2");
		Thread th3=new Thread(runnable,"窗口3");
		
		th1.start();
		th2.start();
		th3.start();
	}}

结果
Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

我们发现,不同窗口会抢到同一张票!!!,这在实际情况是不允许的,这是因为多个线程,在争抢资源的过程中,导致共享的资源出现问题。一个线程还没执行完,另一个线程就参与进来了,开始争抢。(但窗口2抢到第10张票,还没来得及ticketNum--操作,时间片就用完了,随后被窗口三抢到CPU资源,此时的票数还是10,窗口三也抢到第十张票,也还没来得及ticketNum--操作窗口三时间片由完了,窗口一抢到CPU资源,还是买到了第10张票)

多线程安全问题:

  • 当多线程并发访问临界资源时,如果破坏原子操作,可能会造成数据不一致
  • 临界资源:共享资源(同一对象),一次只能允许一个线程使用,才可以保证其正确性
  • 原子操作:不可分割的多步操作,被视作一个整体,其顺序和步骤不可被打乱或缺省

5.2 同步代码块

synchronized(同步监视器)

  • 必须是引用数据类型,不能是基本数据类型
  • 也可以创建一个专门的同步监视器,没有任何业务含义 (new Object)
  • 一般使用共享资源做同步监视器即可
  • 在同步代码块中不能改变同步监视器对象的引用
  • 尽量不要String和包装类Integer做同步监视器,建议使用final修饰同步监视器

对卖票案例改进

public class BuyTicketRunnable implements Runnable{
	static Object obj=new Object();
	private int ticketNum=10;
	@Override
	public void run() {
		for(int i=1;i<blockquote><ul>
<li>多个代码块使用了同一个同步监视器(锁),锁住一个代码块的同时,也锁住所有使用该锁的所有代码块,其他线程无法访问其中的任何一个代码块</li>
<li>多个代码块使用了同一个同步监视器(锁),锁住一个代码块的同时,也锁住所有使用该锁的所有代码块, 但是没有锁住使用其他同步监视器的代码块,其他线程有机会访问其他同步监视器的代码块</li>
</ul></blockquote><h2>5.3 同步方法</h2><blockquote>
<p><code>synchronized</code>(同步方法)</p>
<ul>
<li>不要将run()定义为同步方法</li>
<li>非静态同步方法的同步监视器是this;静态同步方法(static)的同步监视器是 类名.class 字节码信息对象</li>
<li>同步代码块的效率要高于同步方法(原因:同步方法是将线程挡在了方法的外部,而同步代码块锁将线程挡在了代码块的外部,但是却是方法的内部)</li>
<li>同步方法的锁是this,一旦锁住一个方法,就锁住了所有的同步方法;同步代码块只是锁住使用该同步监视器的代码块,而没有锁住使用其他监视器的代码块</li>
</ul>
</blockquote><p><strong>买票案例改进</strong></p><pre class="brush:php;toolbar:false">public class BuyTicketRunnable implements Runnable{
	private int ticketNum=10;
	@Override
	public void run() {
		for(int i=1;i0) {
			System.out.println("在"+Thread.currentThread().getName()+"买到了第"+ticketNum+"张票!");
			ticketNum--;	
		}
	}}

5.4 Lock锁

Lock锁:

  • DK1.5后新增新一代的线程同步方式:Lock锁,与采用synchronized相比,lock可提供多种锁方案,更灵活
  • synchronized是Java中的关键字,这个关键字的识别是靠JVM来识别完成的呀。是虚拟机级别的。
    但是Lock锁是API级别的,提供了相应的接口和对应的实现类,这个方式更灵活,表现出来的性能优于之前的方式。

对买票案例改进

import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class BuyTicketRunnable implements Runnable{
	private int ticketNum=10;
	Lock lock=new ReentrantLock();//接口=实现类  可以使用不同的实现类
	@Override
	public void run() {
		for(int i=1;i0) {
					System.out.println("在"+Thread.currentThread().getName()+"买到了第"+ticketNum+"张票!");
					ticketNum--;	
				}
			}catch(Exception e) {
				e.printStackTrace();
			}finally {
				 //关闭锁:--->即使有异常,这个锁也可以得到释放
				lock.unlock();
			}
		}
	}}

Lock和synchronized的区别

  • Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁
  • Lock只有代码块锁,synchronized有代码块锁和方法锁
  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

5.5 线程死锁

  • 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
  • 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续

*案例:男孩女孩一起去吃饭,但是桌子上只有两根筷子,如果两个人同时抢到一根筷子而不放弃,这样两个人都吃不上饭,这样就形成死锁了;必须要有一个人放弃争抢,等待另一个人用完,释放资源,这个人之后才会获得两根筷子,两个人才能都吃上饭 *

package 多线程;class Eat{
    //代表两个筷子
	public static Object o1=new Object();
	public static Object o2=new Object();
	public static void eat() {
		System.out.println("可以吃饭了");
	}}class BoyThread extends Thread{
	public void run() {
		synchronized (Eat.o1) {
			System.out.println("男孩拿到了第一根筷子!");
			synchronized (Eat.o2) {
				System.out.println("男孩拿到了第二根筷子!");
				Eat.eat();
			}
		}
	}}class GirlThread extends Thread{
	public void run() {
		synchronized (Eat.o2) {
			System.out.println("女孩拿到了第二根筷子!");
			synchronized (Eat.o1) {
				System.out.println("女孩拿到了第一根筷子!");
				Eat.eat();
			}
		}
	}}public class MyLock {
	public static void main(String[] args) {
		BoyThread boy=new BoyThread();
		GirlThread girl=new GirlThread();
		boy.start();
		girl.start();
	}}

结果
Java 멀티스레딩 메커니즘에 대해 자세히 알아보기
解决办法

先让男孩拿到筷子,线程休眠一下,等待男孩用完筷子,在启动女孩线程

public class MyLock {
	public static void main(String[] args) {
		BoyThread boy=new BoyThread();
		GirlThread girl=new GirlThread();
		boy.start();
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		girl.start();
	}}

Java 멀티스레딩 메커니즘에 대해 자세히 알아보기
在写程序中要避免这种死锁:减少同步资源的定义,避免嵌套同步

六、线程通信问题

在Java对象中,有两种池

  • 锁池(synchronized
  • 等待池(wait();notify();notifyAll()

如果一个线程调用了某个对象的wait方法,那么该线程进入到该对象的等待池中(并且已经将锁释放);
如果未来的某个时刻,另外一个线程调用了相同的对象notify方法或者notifyAll方法,那么该等待池中的线程就会被唤醒,然后进入到对象的锁池里面去获得该对象的锁;
如果获得锁成功后,那么该线程就会沿着wait方法之后的路径继续执行。注意:沿着wait方法之后执行

6.1 wait()和wait(long timeout)

  • wait():的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。直到其他线程调用此对象的notify() 方法或 notifyAll() 方法,当前线程被唤醒(进入就绪状态)
  • wait(long timeout):让当前线程处于“等待(阻塞)状态,直到其他线程调用此对象的notify()方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入就绪状态)

sleep和wait的区别:sleep进入阻塞状态没有释放锁,wait进入阻塞状态但是同时释放了锁

6.2 notify()和notifyAll()

notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程

  • notify()是唤醒单个线程
  • notifyAll()是唤醒所有的线程

6.3 生产者和消费者问题

案例:
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费。
如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止。
如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。
Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

功能分解一:商品类

public class Product {//商品类
    private String name;//名字
    private String brand;//品牌
    boolean flag = false;//设置标记,false表示商品没有,等待生产者生产

    public synchronized void setProduct(String name, String brand) {//生产商品,同步方法,锁住的是this
        if (flag == true) {//如果flag为true,代表有商品,不生产,等待消费者消费
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //生产商品
        this.setName(name);
        this.setBrand(brand);

        System.out.println("生产者生产了" +this.getBrand() +this.getName());
        //生产完,设置标志
        flag = true;
        //唤醒消费线程
        notify();
    }

    public synchronized void getProduct() {
        if (flag == false) {//如果是false,则没有商品,等待生产者生产
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果有商品,消费
        System.out.println("消费者消费了" + this.getBrand() +this.getName());
        //设置标志
        flag = false;
        //唤醒线程
        notify();
    }


    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getBrand() {
        return brand;
    }}

功能分解二:生产者线程

public class ProducterThread extends Thread {//生产者线程
    private Product p;

    public ProducterThread(Product p) {
        this.p = p;
    }

    @Override
    public void run() {
        for (int i = 1; i <p><strong>功能分解三:消费者线程</strong></p><pre class="brush:php;toolbar:false">public class CustomerThread extends Thread {//消费者线程
    private Product pro;

    public CustomerThread(Product pro) {
        this.pro = pro;
    }

    @Override
    public void run() {
        for (int i = 1; i <p><strong>功能分解四:测试类</strong></p><pre class="brush:php;toolbar:false">public class Test {
    public static void main(String[] args) {
        Product p = new Product();
        ProducterThread pth = new ProducterThread(p);
        CustomerThread cth = new CustomerThread(p);
        pth.start();
        cth.start();
    }}

结果:生产者生产一件商品,消费者消费一件商品,交替进行

Java 멀티스레딩 메커니즘에 대해 자세히 알아보기

추천 학습: "java 비디오 튜토리얼"

위 내용은 Java 멀티스레딩 메커니즘에 대해 자세히 알아보기의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 csdn.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제