有关多线程与线程共享数据的简单实现

首先讨论一下一个简单直接的问题,为什么要学多线程?

那么原因很简单,会了多线程,你就能涨工资或者找到更好的工作啊!!!

开个玩笑。

好吧,其实不怎么好笑。

多线程能解决什么问题?

举个例子,现在有两个任务A,B在同时进行,A需要10s,B需要2s。没有多线程之前,通常的做法无非两种,先执行A再执行B,或者先执行B再执行A,时间上的结果都是需要12s。那么有没有可能,A和B同时进行呢?有的!用多线程来实现。这个时候,AB任务的完成时间就是10s,节约了2s。

接下来要讲实现了。

讲实现之前,区分一下,进程和线程。

进程是什么?最直接的理解就是windows任务管理列表里面的一个.exe执行单元,系统进行资源分配和调度的一个单位。线程是什么?线程就是进程中独立运行的子任务。

比如,QQ.exe是一个进程,打开QQ,可以传输文件的同时,与好友聊天,是不同的线程。

在JAVA里面,多线程主要有两种实现,继承自Thread类,或者是实现Runnable接口,下面分别看一下。

继承自Thread类

package com.java.multiThread;

public class ThreadSay extends Thread {

    @Override
    public void run()
    {
        try
        {
            for(int i =0;i<10;i++)
            {
                int time = (int) Math.random()*1000;
                Thread.sleep(time);
                System.out.println("T run name:"+Thread.currentThread().getName());
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }

    }
}

重载Run方法,业务代码就放到run方法里面。

测试一下

public static void threadDemoFunc()
    {
        try
        {
            ThreadSay ts = new ThreadSay();
            ts.setName("thread say");
            ts.start();
            for(int i=0;i<10;i++)
            {
                int time = (int) Math.random()*1000;
                Thread.sleep(time);
                System.out.println("Main run name:"+Thread.currentThread().getName());
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }

结果是什么样子呢

T run name:thread say
Main run name:main
T run name:thread say
Main run name:main
T run name:thread say
Main run name:main
T run name:thread say
Main run name:main
T run name:thread say
T run name:thread say
Main run name:main
T run name:thread say
Main run name:main
T run name:thread say
Main run name:main
T run name:thread say
Main run name:main
T run name:thread say
Main run name:main
Main run name:main

从上面的结果还可以看出,CPU执行哪个线程具有不确定性,也就是线程具有随机特性。

接着看看Runnable接口怎么实现多线程。

package com.java.multiThread;

public class RunnableSay implements Runnable
{

    @Override
    public void run()
    {
        System.out.println("Runnable say");
    }

}

也是实现run方法的重载

测试一下

public static void runnableDemoFunc()
{
    RunnableSay rs = new RunnableSay();
    Thread t = new Thread(rs);
    t.start();
}

上述代码可以看到,rs不能直接start,需要放到Thread来启动。

结果就不演示了,和Thread类继承的实现是一样的。

使用多线程的时候需要特别注意的是线程间的共享数据。

先看一个例子。

package com.java.multiThread;

public class DataShare extends Thread
{
    private int count = 5;

    public void run()
    {
        count --;
        System.out.println("count:"+count);
    }
}

然后我如下图所示,这么调用

    public static void dataShareDemoFunc()
    {
        DataShare ds = new DataShare();
        Thread a = new Thread(ds,"A");
        Thread b = new Thread(ds,"B");
        Thread c = new Thread(ds,"C");
        Thread d = new Thread(ds,"D");
        Thread e = new Thread(ds,"E");
        a.start();
        b.start();
        c.start();
        d.start();
        e.start();
    }

结果会是怎样呢?

count:3
count:1
count:2
count:0
count:3

结果就会有可能出现下图类似的场景,数据就错乱了。

为什么,因为count--实际上不是一个一步到位的原子操作,它可以分为3步:取到count,count-1,把count-1赋值给count。因此一个线程在读修改count的时候,可能另一个线程正在读取count。

这种情况其实很严重,比如在一个销售管理系统中,商品出库是一个多线程程序,员工A,B,C,D,E都有出库权,商品总库存需要根据出库操作变化,如果程序如上图那么写,那么员工拿到的库存数据就是一个脏读数据,后果很严重。

怎么处理?这里需要引入synchronized参数做处理,把它加在需要同步锁的方法或者代码块里面。

package com.java.multiThread;

public class DataShare extends Thread
{
    private int count = 5;

    synchronized public void run()
    {
        count --;
        System.out.println("count:"+count);
    }
}

再执行的结果

count:4
count:3
count:2
count:1
count:0

就是一个顺序的结果了。因为synchronized能够保证其内部操作的原子性,线程再执行synchronized方法或者代码块的时候,会锁住这个方法,然后其他线程需要等待当前线程释放锁(执行完毕),才能拿到锁来执行方法或者代码块。

多线程数据贡献还需要了解另一个关键字volatile,一个变量声明为volatile,就意味着这个变量被修改时其他所有使用到此变量的线程都立即能见到变化(称之为可见性)。具体是在每次使用前都要先刷新,以保证别的线程中的修改已经反映到本线程工作内存中,因此可以保证执行时的一致性。以下例子展现了volatile的作用:

public class StoppableTask extends Thread {
  private volatile boolean pleaseStop;  

  public void run() {
    while (!pleaseStop) {
      // do some stuff...
    }
  }  

  public void tellMeToStop() {
    pleaseStop = true;
  }
}
时间: 2024-08-26 17:54:46

有关多线程与线程共享数据的简单实现的相关文章

多线程06-多线程共享数据的方式(经验小结)

1.案例分析-01 通过代码实现火车票出售的例子 在实现代码之前先对问题进行分析:火车票出售应该是在多个窗口进行的(即多个线程),以一个车的班次来说,该班次的火车票张数即为多个窗口共享的数据 即这份共享数据为出售特定班次的火车票,这个动作在多个窗口都是不变的,变更的只有火车票的剩余张数.代码实现如下: package org.lkl.thead; /** * * Function : 多线程共享数据 * * @author : Liaokailin CreateDate : 2014-6-13

多线程訪问共享数据(1)

多线程訪问共享数据解决方式: 一.什么是多线程 ?线程是程序中一个单一的顺序控制流程.在单个程序中同一时候运行多个线程完毕不同的工作,称为多线程. ?全部的线程尽管在微观上是串行运行的,可是在宏观上你全然能够觉得它们在并行运行 二.多线程訪问共享数据解决方式 1,假设每一个线程运行的代码同样,能够使用同一个Runnable对象,这个Runnable对象中有那个共享数据. 比如:卖票系统. 2,假设每一个线程运行的代码不同,这个时候须要用不同的Runnable对象,有例如以下三种方式来实现这些Ru

线程共享数据的安全问题

加同步代码:加了同步代码后,线程进同步判断锁,获取锁,出同步释放锁,导致了线程运行速度的下降 方式一:同步代码块 解决线程安全问题,java程序提供了线程同步技术 synchronized(任意对象){ 线程要操作的共享数据 } 任意对象:通俗来讲叫同步锁或者对象监视器 同步的作用:保证线程的安全性,没有锁的线程是不能执行的,只能等待带锁的线程执行完拿到同步锁才能进入同步代码块中执行. 举个例子: 现在有3个线程需要进入代码块中执行,当 线程1 遇到synchronized以后,线程1 会去判断

管理线程共享数据

方法1: 将共享数据封装在一个类的对象中,然后将这个对象传递给各个 Runnable对象 每个线程对共享数据的操作方法也分配到相应的对象身上去完成 示例代码: 1 public class Test { 2 private static ThreadLocal<MyThreadScopeData> myThreadScopeData = new ThreadLocal<MyThreadScopeData>(); 3 public static void main(String[]

JAVA 并发编程-多个线程之间共享数据(六)

多线程共享数据的方式: 1,如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,卖票系统就可以这么做. 2,如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,例如,设计4个线程.其中两个线程每次对j增加1,另外两个线程对j每次减1,银行存取款 有两种方法来解决此类问题: 将共享数据封装成另外一个对象,然后将这个对象逐一传递给各个Runnable对象,每个线程对共享数据的操作方法也分配到那个对象身上完成,这样容易实现针对

多线程共享数据

线程间的数据共享 多个线程共享数据, A线程生产物品,产品数量增加,B线程使用,那么产品数量减少,B线程如何知道产品buf是否为空呢?A线程如何判断产品buf为满?1 轮询机制      线程每隔一定时间检查缓冲区,如果有产品就取出,这种机制需要控制好轮询间隔,查询太频繁会导致占用cpu过多,浪费:查询太不频繁导致缓冲区满,生产者放不进去产品  int buf[MAX];  int ProductionCount;  while(1)  {  Msleep(100);     if(Produc

5、Java并发性和多线程-相同线程

以下内容转自http://tutorials.jenkov.com/java-concurrency/same-threading.html(使用谷歌翻译): 相同线程(同一线程)是一种并发模型,其中单线程系统扩展到N个单线程系统.结果是并行运行的N个单线程系统. 同一个线程系统不是一个纯粹的单线程系统,因为它包含多个线程.但是,每个线程都像单线程系统一样运行. 为什么是单线程系统? 你可能会想知道为什么有人今天会设计单线程系统.单线程系统得到普及,因为它们的并发模型比多线程系统简单得多.单线程

多线程访问共享对象和数据的方式

在多线程访问共享对象和数据时候大致可以分为两大类. 1:如果每个线程执行的代码相同,可以使用同一个runnable对象,这个runnable对象中有那个共享对象.如:买票系统. 1 public class MulteThreadlShareData { 2 public static void main(String[] args) { 3 ShareData shareData = new ShareData(); 4 new Thread(shareData).start(); 5 new

Java多线程之线程同步

一.多线程出现的安全问题: 1.问题的原因: 多个线程执行的不确定性引起执行结果的不稳定.当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误.2.解决的办法: 对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行. 二.Synchronized的使用方法: 1.操作共享数据的代码,即为需要被同步的代码.不能包含代码多或者少.2.共享数据:多个线程共同操作的变量.比如:ticket就是共