多线程实现资源共享

Java实现多线程方式有两种:继承Thread类或者实现Runnable即可.线程启动时调用start()方法. 
实现Runnable接口可以实现资源共享 
下面让我们来看一下代码:

public class Thread1 extends Thread{

    private int num = 5;

    @Override
    public void run() {
        for(int i=0;i<10;i++)
        {
            if(this.num>0)
            {
                System.out.println("剩余的票1:"+num--);
            }
        }
    }

}

public class Thread2 implements Runnable{

    private int num = 5;

    public void run() {
        for(int i=0;i<10;i++)
        {
            if(this.num>0){
                System.out.println("剩余的票2:"+num--);
            }
        }
    }

}


    /**
     * @author 付玉伟
     * @time 2015-4-9 下午09:37:56
     * @param args
     */
    public static void main(String[] args) {
        Thread1 t1 = new Thread1();
        t1.setName("售票窗口1");
        Thread1 t2 = new Thread1();

        t1.setName("售票窗口2");
        Thread1 t3 = new Thread1();
        t1.setName("售票窗口3");

        t1.start();
        t2.start();
        t3.start();


        Thread2 t1_ = new Thread2();
        Thread t1_1 = new Thread(t1_);
        t1_1.setName("售票窗口1_");

        Thread t2_2 = new Thread(t1_);
        t2_2.setName("售票窗口2_");

        Thread t3_3 = new Thread(t1_);
        t1_1.setName("售票窗口3_");

        t1_1.start();
        t2_2.start();
        t3_3.start();
    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

运行结果如下: 
这里写图片描述 
一共5张票,线程1卖了15次,显然资源没有共享,而线程2只买了5次。 
Java多线程访问共享资源的方式: 
1、如果每一个线程执行的代码相同,可以使用同一个runnable对象,这个对象中有那个共享数据(买票系统) 
2、如果每一个线程执行的代码不相同,这时候需要不同的Runnable对象,有以下两种方式来实现这些Runnable对戏之间的数据共享。 
(1)、将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。 
  (2)、将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。 
  (3)、上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。 
  (4)、总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。 
3、极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享 
在线程操作中由于其操作的不确定性,所以提供了一个方法,可以取得当前操作线程: 
public static Thread currentThread(); 
说明: 
对于线程的名字一般是在启动前进行设置,最好不要设置相同的名字,最好不要为一个线程改名字. 
在Java执行中一个Java程序至少启动2个线程:一个主线程和一个垃圾回收线程. 
多线程的同步问题 
如果使用Runnable的方式实现买票系统,在买票出现延迟时如:

public class Thread2 implements Runnable{

    private int num = 5;

    public void run() {
        for(int i=0;i<10;i++)
        {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(this.num>0){
                System.out.println("剩余的票2:"+num--);
            }
        }
    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

输出结果如下: 
这里写图片描述 
如果解决这样的问题就必须使用同步,即:过个操作在同一个时间段内只有一个线程进行。 
Java多线程同该方法步主要依赖于若干方法和关键字 
1、wait方法 
Object的方法,作用是使得当前调用wait方法所在部分的线程停止执行,并释放当前所获得调用wait的代码块的锁,并在其他线程调用notify或者notifyAll方法时恢复到竞争锁状态 
wait被调用的时候必须在拥有锁(synchronized修饰)的代码块中 
恢复执行后,从wait的下一条语句开始执行,因为wait方法总是应当在while循环中调用,以免出现恢复执行后继续执行的条件却不满足继续执行的条件。
若wait方法参数中带时间,则除了notify和notifyAll被调用能激活处于wait状态的线程进入锁竞争外,在其他线程中interrupt它或者参数时间到了之后,该线程也将激活到竞争状态。 
wait方法被调用的线程必须获得之前执行到wait时释放掉用的锁重新获得才能够恢复执行。 
2、notify方法和notifyAll方法 
notify方法通过调用了wait方法,但是尚未激活一个线程调度队列(即进入竞争锁),不是立即执行。并且具体是哪一个线程不能保证。另外一点就是被唤醒的这个线程一定是等待wait所释放的锁。 
notifyAll方法则唤醒所有调用wait方法,尚未激活的进程进入竞争队列。 
3、synchronized关键字 
用来标识一个普通方法时,表示一个线程要执行该方法必须取得该方法所在对象的锁。 
用来 标识一个静态方法时,表示一个线程要执行该方法必须取得该方法所在类的类锁 
修饰一个代码块。类似synchronized(Obj){}表示一个线程要执行的代码块必须获得Object的锁,这样做的目的是减小锁的粒度,保证当不同块所需的锁不冲突时不用对整个对象加锁。利用零长度的byte数组对象做obj非常经济。 
4、atomic action 原子操作 
在Java中,以下都是原子操作,但是在C和C++中不是 
对引用变量和除了long和double之外的原始数据类型变量进行读写 
对所有声明为volatile的变量(包括long和double)的读写 
另外在Java.util.concurrent和java.util.concurrent.atomic包中提供了一些不依赖于同步机制的线程安全类和方法。 
下面使用synchronized来解决同步的问题: 
1、同步代码块

public class Thread2 implements Runnable{

    private int num = 5;

    public void run() {
        for(int i=0;i<10;i++)
        {
            // 使用同步代码块
            synchronized(this){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if(this.num>0){
                System.out.println("剩余的票2:"+num--);
            }
        }
    }

}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

2、同步方法

public class Thread3 implements Runnable{

    private int num = 5;

    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(num > 0 ){
            System.out.println(Thread.currentThread().getName()+"买票"+this.num--);
        }
    }

    public static void main(String[] args){
        Thread3 t3 = new Thread3();

        new Thread(t3,"售票窗口1").start();
        new Thread(t3,"售票窗口2").start();
        new Thread(t3,"售票窗口3").start();
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

多线程之间资源共享需要使用同步,但是过多的同步会造成死锁。 
死锁是在多道程序系统中,一组进程中的每一个进程都无限期的等待另一个线程,所以占有且永远不会释放的资源。 
死锁产生的原因: 
1、竞争资源,系统提供的资源有限,不能满足每一个进程的要求 
2、多道程序运行时,进程推进顺序不合理 
产生死锁的必要条件: 
1、互斥资源使用 
2、占用并等待资源 
3、不可抢夺资源 
4、循环等待资源 
转自http://www.cnblogs.com/dennisit/archive/2013/02/24/2925288.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值