java多线程
Posted 编程语言之旅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java多线程相关的知识,希望对你有一定的参考价值。
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
运行结果如下:
一共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
输出结果如下:
如果解决这样的问题就必须使用同步,即:过个操作在同一个时间段内只有一个线程进行。
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
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、多道程序运行时,进程推进顺序不合理
产生死锁的必要条件:
1、互斥资源使用
2、占用并等待资源
3、不可抢夺资源
4、循环等待资源
以上是关于java多线程的主要内容,如果未能解决你的问题,请参考以下文章