专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

多线程(线程间的数据共享、线程安全、线程通信)

1、线程间的数据共享

(1)利用继承Thread类的方法实现模拟售票系统:

public class MyThread extends Thread{       //继承自Thread类

    private int tickets=10;
    public void run() {//重写run方法
        while(true){
            if(tickets>0)
                System.out.println(getName()+"售票窗口,售卖了"+tickets--+"号票");

else
System.exit(0);


} } }
public class Test {
public static void main(String[] args) {
    MyThread window1 = new MyThread();
    MyThread window2 = new MyThread();
    MyThread window3 = new MyThread();
    window1.start();
    window2.start();
    window3.start();

}
}

35_1.png

由运行结果可以看出,一张票在每一个窗口买了一次,在三个窗口卖了三次。因为在测试类中创建了三个线程对象,每个对象有各自的属性,他们处理的数据是相互独立的。因此,用这种方法处理售票系统是不成立的。

(2)实现Runnable接口,模拟售票系统:

package pers.zhb.runnable;

public class MyThread implements Runnable {
    private int tickets = 10;

    public void run() {
        while (true) {
            if (tickets > 0)
                System.out.println(Thread.currentThread().getName()
                        + "售票窗口,售卖了" + tickets-- + "号票");

else
System.exit(0);


} } }
package pers.zhb.runnable;

public class RunnableDemo {
    public static void main(String[] args) throws InterruptedException {
        MyThread mt=new MyThread();
        Thread thread1 = new Thread(mt);
        Thread thread2 = new Thread(mt);
        thread1.start();
        thread2.start();

    }
}

35_2.png

此方法建立了三个线程,虽然他们是来自同一个对象的,即每一个线程调用的是同一个run方法,有相同的数据,但是依旧存在线程安全问题。

2、线程安全

(1)概念:

单线程相当于一个人做一件事,而多线程程序中,当需要多个线程共享同一个数据时,一个线程对共享的数据进行修改时,在他没有完成相关操作之前不允许其他线程进行相关操作,否则会出现线程安全问题。当对存在安全隐患的数据加锁后,会导致程序的性能降低,所以,如果是多个线程操作同一个数据,一定要选择线程安全的方法,如果不是,可以选择线程不安全的方式来处理问题。

(2)线程安全问题:

package pers.zhb.runnable;

public class MyThread implements Runnable {
    private int tickets = 10;

    public void run() {
        while (true) {
            if (tickets > 0) System.out.println(Thread.currentThread().getName() + "售票窗口,售卖了" + tickets-- + "号票"); else System.exit(0); } } }
package pers.zhb.runnable;

public class RunnableDemo {
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        Thread thread1 = new Thread(mt);
        Thread thread2 = new Thread(mt); Thread thread3 = new Thread(mt); Thread thread4 = new Thread(mt); thread1.start(); thread2.start(); thread3.start(); thread4.start(); } }

35_3.png

可以看出,10号票在被1号窗口卖出后,又被2号窗口卖了一次,显然这是不符合常理的。产生的原因是这样的:2号线程开始执行,tickets为10,进行了 if (tickets > 0);语句的执行,可以继续往下执行了,但是他没有抢夺到CPU资源,接着是1号线程读取数据10,进行判断可以执行,1号线程执行完后,2号线程依旧没有抢夺到CPU资源,只能继续等待。接着是3号线程、0号线程进行相关操作。当2号线程抢夺到CPU资源时,他的数据还是10,就造成了有重复票的情况。

(3)添加同步语句:

package pers.zhb.runnable;

public class MyThread implements Runnable {
    private int tickets = 10;
    Object lock = new Object();

    public void run() {

        synchronized (lock) { while (true) { if (tickets > 0) System.out.println(Thread.currentThread().getName() + "售票窗口,售卖了" + tickets-- + "号票"); else System.exit(0); } } } }
package pers.zhb.runnable;

public class RunnableDemo {
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        Thread thread1 = new Thread(mt);
        Thread thread2 = new Thread(mt); Thread thread3 = new Thread(mt); Thread thread4 = new Thread(mt); thread1.start(); thread2.start(); thread3.start(); thread4.start(); } }

35_4.png

(4)创建同步方法:

package pers.zhb.runnable;

public class MyThread implements Runnable {
    private int tickets = 10;
    Object lock = new Object();

    public void run() {
        while (true) lock(); } public synchronized void lock() { while (true) { if (tickets > 0) System.out.println(Thread.currentThread().getName() + "售票窗口,售卖了" + tickets-- + "号票"); else System.exit(0); } } }
package pers.zhb.runnable;

public class RunnableDemo {
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        Thread thread1 = new Thread(mt);
        Thread thread2 = new Thread(mt); Thread thread3 = new Thread(mt); Thread thread4 = new Thread(mt); thread1.start(); thread2.start(); thread3.start(); thread4.start(); } }

35_5.png

(5) ReentrantLock (重入锁):

可以对资源重复加锁:

package pers.zhb.runnable;

import java.util.concurrent.locks.ReentrantLock;

public class MyThread implements Runnable {
    private int tickets = 10;

    ReentrantLock lock = new ReentrantLock();

    public void run() {

        while (true) { lock.lock(); if (tickets > 0) System.out.println(Thread.currentThread().getName() + "售票窗口,售卖了" + tickets-- + "号票"); else System.exit(0); } } }
package pers.zhb.runnable;

public class RunnableDemo {
    public static void main(String[] args) {
        MyThread mt=new MyThread();
        Thread thread1 = new Thread(mt);
        Thread thread2 = new Thread(mt); Thread thread3 = new Thread(mt); Thread thread4 = new Thread(mt); thread1.start(); thread2.start(); thread3.start(); thread4.start(); } }

35_6.png

3、线程之间的通信

(1)常用方法:

wait():将正在执行的线程停止,并存放到线程池,直到被唤醒或等待时间到。

sleep() :是使线程停止一段时间的方法。在sleep 时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行,除非”醒来”的线程具有更高的优先级 或正在运行的线程因为其它原因而阻塞。

notify():唤醒在线程池中等待的线程。

notifyAll():唤醒全部。

(2)举例:

package pers.Thread.conn;

public class Tickets {
    private int size;
    private int num = 0;
    public Tickets(int size){
        this.size=size; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } boolean flag = true; public synchronized void put() { if (flag) //还有剩余,等待 try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } num++;//等待结束后开始执行,增加票的操作 System.out.println("存入第" + num + "号票"); flag = true;//加票之后,有票,进入等待状态 notify();//唤醒等待的线程 } public synchronized void sell() { if (!flag) //没有剩余,等待 try { wait(); } catch (InterruptedException e) { e.printStackTrace(); }if(num>=1) System.out.println("卖出第" + num + "号票"); flag = false;//减票之后,没有票,进入等待状态 notify();//唤醒另外一个等待的线程 if(num==size)num=size+1; } }
package pers.Thread.conn;

public class Add extends Thread{
Tickets t=null;
public  Add(Tickets t){//为了保证卖出和增加的为统一数据对象
    this.t=t;
}
public void run(){
    while(t.getNum()<t.getSize()) t.put(); } }
package pers.Thread.conn;

public class Sell extends Thread{
    Tickets t=null;
    public  Sell(Tickets t){//为了保证卖出和增加的为统一数据对象
        this.t=t;
    }
    public void run(){
        while(t.getNum()<=t.getSize()) t.sell(); } }
package pers.Thread.conn;

public class Test {
public static void main(String[] args) {
    Tickets t=new Tickets(15);
    new Add(t).start();//卖出和增加调用的为同一个对象
    new Sell(t).start();

}
}

文章永久链接:https://tech.souyunku.com/22636

未经允许不得转载:搜云库技术团队 » 多线程(线程间的数据共享、线程安全、线程通信)

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们