`
aijuans
  • 浏览: 1549178 次
社区版块
存档分类
最新评论

多线程实现资源共享的问题学习与总结

 
阅读更多

我么知道Java传统多线程的实现有两种方法,继承Thread类或者实现Runnable即可.线程启动时调用start()方法.

实现Runnable接口相比继承Thread类有如下好处:

1.避免单继承的局限,一个类可以同时实现多个接口

2.适合资源的共享.

 

实现多线程模拟售票点卖票来说明实现Runnable即可可以达到资源共享的目的.

使用继承Thread类的多线程售票实现

package org.dennist.thread.demo;
/**
 *
 *  TicketThread.java    
 *
 *  @version : 1.1
 *  
 *  @author  : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
 *    
 *  @since     : 1.0        创建时间:    2013-2-24        下午02:22:49
 *     
 *  TODO     :    class TicketThread.java is used for ...
 *
 */
public class TicketThreadT extends Thread{
    
    private int num = 5;        //总共票数设定为5张
    
    @Override
    public void run() {
        for(int i=0; i<10; i++){
            if(this.num>0){        //打印买票信息
                System.out.println(Thread.currentThread().getName() + "买票: " + this.num--);
            }
        }
    }
    
    public static void main(String[] args) {
        TicketThreadT th1 = new TicketThreadT();        //线程一
        th1.setName("售票口一");    
        TicketThreadT th2 = new TicketThreadT();        //线程二
        th2.setName("售票口二");
        TicketThreadT th3 = new TicketThreadT();        //线程三
        th3.setName("售票口三");
        
        //分别启动三个线程
        th1.start();
        th2.start();
        th3.start();
    }
}

程序运行结果:

总共5张票,启动了三个线程,从打印结果可以看出,一共卖出去了15张票,线程之间没有进行资源共享

<!--EndFragment-->

实现Runnable的售票线程

<!--EndFragment-->

package org.dennist.thread.demo;
/**
 *
 *  TicketThreadR.java    
 *
 *  @version : 1.1
 *  
 *  @author  : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
 *    
 *  @since     : 1.0        创建时间:    2013-2-24        下午02:29:23
 *     
 *  TODO     :    class TicketThreadR.java is used for ...
 *
 */
public class TicketThreadR implements Runnable{
    
    private int num = 5;            //总共票数设定为5张
    
    @Override
    public void run() {
        for(int i=0; i<10; i++){
            if(this.num>0){            //打印买票信息
                System.out.println(Thread.currentThread().getName() + "买票: " + this.num--);
            }
        }
    }

    public static void main(String[] args) {
        TicketThreadR ticketThread = new TicketThreadR();
        
        Thread th1 = new Thread(ticketThread);    //线程一
        th1.setName("售票口一");
        Thread th2 = new Thread(ticketThread);    //线程二
        th2.setName("售票口二");
        Thread th3 = new Thread(ticketThread);    //线程三
        th3.setName("售票口三");
        
        th1.start();
        th2.start();
        th3.start();
    }
}

程序运行结果

虽然现在程序中有三个线程,但是三个线程总共卖出了5张票,也就是说使用Runnable实现的多线程可以达到资源共享的目的.

Java多线程访问共享方式

(1)如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做。

(2)如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:

1、将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。

2、将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。

3、上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。

4、总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。

(3)极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。

 

 

Thread类中存在以下的几个方法可以设置和取得名字.

设置名字: public final void setName(String name) 

public Thread(Runnable target, String name)

public Thread(String name)

取得名字: public final String getName()

在线程的操作中因为其操作的不确定性,所以提供了一个方法,可以取得当前的操作线程.

public static Thread currentThread()

说明:

对于线程的名字一般是在启动前进行设置,最好不要设置相同的名字,最好不要为一个线程改名字.

Java执行中一个Java程序至少启动2个线程:一个主线程和一个垃圾回收线程.

 

多线程的同步问题

上面的实现Runnable程序就真的没问题了吗?我们知道现实生活中买票总会有等待,跟延迟,那么我们模拟现实生活中的买票然后再来看上面的程序输出.

package org.dennist.thread.demo;
/**
 *
 *  TicketThreadR.java    
 *
 *  @version : 1.1
 *  
 *  @author  : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
 *    
 *  @since     : 1.0        创建时间:    2013-2-24        下午02:29:23
 *     
 *  TODO     :    class TicketThreadR.java is used for ...
 *
 */
public class TicketThreadR implements Runnable{
    
    private int num = 5;            //总共票数设定为5张
    
    @Override
    public void run() {
        for(int i=0; i<10; i++){
            try {
                Thread.sleep(200);    //休息200毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }        
            if(this.num>0){            //打印买票信息
                System.out.println(Thread.currentThread().getName() + "买票: " + this.num--);
            }
        }
    }

    public static void main(String[] args) {
        TicketThreadR ticketThread = new TicketThreadR();
        
        Thread th1 = new Thread(ticketThread);    //线程一
        th1.setName("售票口一");
        Thread th2 = new Thread(ticketThread);    //线程二
        th2.setName("售票口二");
        Thread th3 = new Thread(ticketThread);    //线程三
        th3.setName("售票口三");
        
        th1.start();
        th2.start();
        th3.start();
    }
}

如果想解决这样的问题,就必须使用同步,所谓的同步就是指多个操作在同一个时间段内只有一个线程进行,其他线程要等待此线程完成之后才可以继续执行.

可以通过同步代码的方法进行代码的加锁操作,同步的实现有2中方法:

JAVA多线程同步主要依赖于若干方法和关键字
1  wait方法

2  notify方法和notifyAll方法

3  synchronized关键字

4 atomic action(原子操作)

详细说明见:[http://www.cnblogs.com/dennisit/archive/2013/02/25/2931573.html]

此处针对上面情况使用同步关键字synchronized解决.同步关键字使用有2种方法

1.同步代码块

2.同步方法

<!--EndFragment-->

同步代码块

使用synchronized关键字进行同步代码块的声明,但是在使用此操作时必须明确的指出到底要锁定的是哪个对象,一般是以当前对象为主.

<!--EndFragment--> synchronized(对象){   //一般都是讲this锁定

         //锁定对象

     }

<!--EndFragment-->上面的问题使用同步代码块解决

package org.dennist.thread.demo;
/**
 *
 *  TicketThreadR.java    
 *
 *  @version : 1.1
 *  
 *  @author  : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
 *    
 *  @since     : 1.0        创建时间:    2013-2-24        下午02:29:23
 *     
 *  TODO     :    class TicketThreadR.java is used for ...
 *
 */
public class TicketThreadR implements Runnable{
    
    private int num = 5;            //总共票数设定为5张
    
    @Override
    public void run() {
        for(int i=0; i<10; i++){
            //使用同步代码块
            synchronized (this) {
                try {
                    Thread.sleep(300);    //休息300毫秒
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }        
                if(this.num>0){    
                    //打印买票信息
                    System.out.println(Thread.currentThread().getName() + "买票: " + this.num--);
                }
            }
            
        }
    }

    public static void main(String[] args) {
        TicketThreadR ticketThread = new TicketThreadR();
        
        new Thread(ticketThread,"售票口一").start();    //线程一
        new Thread(ticketThread,"售票口二").start();    //线程二
        new Thread(ticketThread,"售票口三").start();    //线程三
    }
}

 

同步方法

同步方法是在方法上增加synchronized关键字修饰
上面的问题使用同步代码块解决

package org.dennist.thread.demo;
/**
 *
 *  TicketThreadR.java    
 *
 *  @version : 1.1
 *  
 *  @author  : 苏若年    <a href="mailto:DennisIT@163.com">发送邮件</a>
 *    
 *  @since     : 1.0        创建时间:    2013-2-24        下午02:29:23
 *     
 *  TODO     :    class TicketThreadR.java is used for ...
 *
 */
public class TicketThreadR implements Runnable{
    
    private int num = 5;            //总共票数设定为5张
    
    @Override
    public void run() {
        for(int i=0; i<10; i++){
            sale();                    //调用同步方法
        }
    }
    
    //使用同步方法
    public synchronized void sale(){
        try {
            Thread.sleep(300);    //休息300毫秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }        
        if(this.num>0){    
            //打印买票信息
            System.out.println(Thread.currentThread().getName() + "买票: " + this.num--);
        }
    }
    
    public static void main(String[] args) {
        TicketThreadR ticketThread = new TicketThreadR();
        
        new Thread(ticketThread,"售票口一").start();    //线程一
        new Thread(ticketThread,"售票口二").start();    //线程一
        new Thread(ticketThread,"售票口三").start();    //线程一
    }
}

 

多个线程共享同一资源的时候需要进行同步,但是过多的同步会造成死锁.

 

什么叫死锁?死锁产生的主要原因是什么?死锁产生的必要条件,如何解决死锁?

死锁指在多道程序系统中,一组进程中的每一个进程均无限期的等待该被改组进程中的另一个进程所以占有且永远不会释放的资源,这种现象称为系统处于死锁状态.

死锁产生的原因主要有2:

1.竞争资源,系统提供的资源数量有限,不能满足每个进程的要求

2.多道程序运行时,.进程推进顺序不合理

产生死锁的必要条件

1.互斥使用资源

2.占用并等待资源

3.不可抢夺资源

4.循环等待资源

解决死锁的方法

1.预防死锁:破坏死锁产生的条件(除过互斥条件,因为破坏互斥条件不现实)

2.避免死锁

3.检测与排除

4.置之不理

 

转载请注明出处:[http://www.cnblogs.com/dennisit/archive/2013/02/24/2925288.html]

<!--EndFragment-->

在线交谈

分享到:
评论

相关推荐

    java多线程设计模式详解(PDF及源码)

    (注意,本资源附带书中源代码可供参考) 多线程与并发处理是程序设计好坏优劣的重要课题,本书通过浅显易懂的文字与实例来介绍Java线程相关的设计模式概念,并且通过实际的Java程序范例和 UML图示来一一解说,书中...

    python3爬虫中多线程的优势总结

    有些小伙伴跟小编讨论了python中使用多线程原理的问题,就聊到了关于python多线程的弊端问题,这点可能在使用的过程中大家会能感觉到。而且之前讲过的GIL也是对python多线程的一种限制。那么,我们为什么还要用多...

    Python控制多进程与多线程并发数总结

    只好学习控制线程数了,官方文档不好看,觉得结构不够清晰,网上找很多文章也都不很清晰,只有for全开线程,没有控制线程数的具体说明,最终终于根据多篇文章和官方文档算是搞明白基础的多线程怎么实现法了,怕长...

    java 编程入门思考

    1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段...

    vc++ 应用源码包_1

    多线程实现。 多线程高速文件搜索程序源码 VC++视频聊天系统源代码 实例简单,有用户登录、传输文件、视频、画质调节、禁音检测、回音消除、自动增益、噪音抑制、视频控制等、 VC++搜索指定文件夹中的文件 VC++...

    vc++ 应用源码包_2

    多线程实现。 多线程高速文件搜索程序源码 VC++视频聊天系统源代码 实例简单,有用户登录、传输文件、视频、画质调节、禁音检测、回音消除、自动增益、噪音抑制、视频控制等、 VC++搜索指定文件夹中的文件 VC++...

    vc++ 应用源码包_6

    多线程实现。 多线程高速文件搜索程序源码 VC++视频聊天系统源代码 实例简单,有用户登录、传输文件、视频、画质调节、禁音检测、回音消除、自动增益、噪音抑制、视频控制等、 VC++搜索指定文件夹中的文件 VC++...

    vc++ 应用源码包_5

    多线程实现。 多线程高速文件搜索程序源码 VC++视频聊天系统源代码 实例简单,有用户登录、传输文件、视频、画质调节、禁音检测、回音消除、自动增益、噪音抑制、视频控制等、 VC++搜索指定文件夹中的文件 VC++...

    vc++ 应用源码包_3

    多线程实现。 多线程高速文件搜索程序源码 VC++视频聊天系统源代码 实例简单,有用户登录、传输文件、视频、画质调节、禁音检测、回音消除、自动增益、噪音抑制、视频控制等、 VC++搜索指定文件夹中的文件 VC++...

    vc++ 开发实例源码包

    实现了自绘控件,云端控制主要在CnComm类多线程串口通讯库, camerads-DirectShow使用示例 演示了摄像头的使用 CatListBoxDemo ListBox控件与其它控件阙套使用方法 CCAMS系统是一种用于局域网下的CS模式的软件...

    Think in Java(中文版)chm格式

    14.1.2 针对用户界面的多线程 14.1.3 用主类合并线程 14.1.4 制作多个线程 14.1.5 Daemon线程 14.2 共享有限的资源 14.2.1 资源访问的错误方法 14.2.2 Java如何共享资源 14.2.3 回顾Java Beans 14.3 堵塞 ...

    Thinking in Java(中文版 由yyc,spirit整理).chm

    1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段...

    Thinking in Java 中文第四版+习题答案

    1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段...

    JAVA_Thinking in Java(中文版 由yyc,spirit整理).chm

    1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段...

    Java初学者入门教学

    1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段...

    JAVA_Thinking in Java

    1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段...

    thinkinjava

    1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段...

    ThinkInJava

    1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段...

    Thinking in Java简体中文(全)

    1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段...

    java联想(中文)

    1.9 多线程 1.10 永久性 1.11 Java和因特网 1.11.1 什么是Web? 1.11.2 客户端编程 1.11.3 服务器端编程 1.11.4 一个独立的领域:应用程序 1.12 分析和设计 1.12.1 不要迷失 1.12.2 阶段0:拟出一个计划 1.12.3 阶段...

Global site tag (gtag.js) - Google Analytics