Java实现多线程方式有两种:继承Thread类或者实现Runnable即可.线程启动时调用start()方法.
实现Runnable接口可以实现资源共享
下面让我们来看一下代码:
- 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的方式实现买票系统,在买票出现延迟时如:
- 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、同步代码块
- 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、同步方法
- 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