知识列表
- 线程与进程
- 线程安全
- 死锁
- 线程间通信
- 等待唤醒机制
- 停止线程
- 守护线程、join方法、优先级,yield
1)线程与进程:
进程:是一个正在执行中的程序。一个控制单元。
线程:就是进程中的一个独立的控制单元,线程控制进程的执行。
一个进程中至少有一个线程。
自定义线程方式一,继承Thread 步骤:
1,定义类继承Thread。
2,复写Thread类中的run方法。
3,调用线程的start方法,
发现运行结果每一次都不同。因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行。(多核除外),cpu在做着快速的切换,以达到看上去是同时运行的效果。
这就是多线程的一个特性:随机性。覆盖run方法的原因:
Thread类用于描述线程。
该类就定义了一个功能,用于存储线程要运行的代码,该功能就是run方法。自定义线程方式二,实现Runnable 步骤:
实现方式好处:避免了单继承的局限性。
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。
将线程要运行的代码存放在该run方法中。
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
在定义线程时,建议使用实现方式。两种方式的区别:
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable,线程代码存在接口的子类的run方法。
2)线程安全:
卖票程序:
1: class Ticket implements Runnable//extends Thread
2: {
3: private int tick = 100;
4: public void run()
5: {
6: while(true)
7: {
8: if(tick>0)
9: {
10: System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
11: }
12: }
13: }
14: }
15: class TicketDemo
16: {
17: public static void main(String[] args)
18: {
19: Ticket t = new Ticket();
20: Thread t1 = new Thread(t);//创建了一个线程;
21: Thread t2 = new Thread(t);//创建了一个线程;
22: Thread t3 = new Thread(t);//创建了一个线程;
23: Thread t4 = new Thread(t);//创建了一个线程;
24: t1.start();
25: t2.start();
26: t3.start();
27: t4.start();
28: }
29: }
?
多线程容易出现安全问题:
原因:
??? 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。解决办法:
??? 对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。通过加锁的方式来完成,synchronized(对象){需要被同步的代码}。1: class Ticket implements Runnable
2: {
3: private int tick = 1000;
4: Object obj = new Object();
5: public void run()
6: {
7: while(true)
8: {
9: synchronized(obj)//同步
10: {
11: if(tick>0)
12: {
13: //try{Thread.sleep(10);}catch(Exception e){}
14: System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
15: }
16: }
17: }
18: }
19: }
20: class TicketDemo2
21: {
22: public static void main(String[] args)
23: {
24:
25: Ticket t = new Ticket();
26:
27: Thread t1 = new Thread(t);
28: Thread t2 = new Thread(t);
29: Thread t3 = new Thread(t);
30: Thread t4 = new Thread(t);
31: t1.start();
32: t2.start();
33: t3.start();
34: t4.start();
35: }
36: }
37:
同步(synchronized)的前提:
1,必须要有两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。必须保证同步中只能有一个线程在运行。
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。同步有两种表现形式:同步函数和同步代码块。
同步时必须明确哪些是需要同步的,哪些是不需要同步的,并且要保证同步函数使用的是同一把锁,也就是synchronized(对象)中的对象是同一个。
静态函数同步使用的锁是所在类的字节码文件对象。
懒汉式中的安全问题:
1: public class TicketDemo {
2: private TicketDemo(){}
3: private static TicketDemo t = null;
4: public static TicketDemo getInstance(){
5: if(t==null){
6: synchronized(TicketDemo.class){
7: if(t==null)
8: t = new TicketDemo();
9: }
10: }
11: return t;
12: }
13: }
3)死锁
同步中嵌套同步时:
1:
2: class Ticket implements Runnable
3: {
4: private int tick = 1000;
5: Object obj = new Object();
6: boolean flag = true;
7: public void run()
8: {
9: if(flag)
10: {
11: while(true)
12: {
13: synchronized(obj)
14: {
15: show();
16: }
17: }
18: }
19: else
20: while(true)
21: show();
22: }
23: public synchronized void show()//this
24: {
25: synchronized(obj)
26: {
27: if(tick>0)
28: {
29: try{Thread.sleep(10);}catch(Exception e){}
30: System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
31: }
32: }
33: }
34: }
37: class DeadLockDemo
38: {
39: public static void main(String[] args)
40: {
42: Ticket t = new Ticket();
44: Thread t1 = new Thread(t);
45: Thread t2 = new Thread(t);
46: t1.start();
47: try{Thread.sleep(10);}catch(Exception e){}
48: t.flag = false;
49: t2.start();
52: }
53: }
?
?
4)线程间通信:
1: class Res
2: {
3: String name;
4: String sex;
5: boolean flag = false;
6: }
7: class Input implements Runnable
8: {
9: private Res r ;
10: Input(Res r)
11: {
12: this.r = r;
13: }
14: public void run()
15: {
16: int x = 0;
17: while(true)
18: {
19: synchronized(r)//加锁,对象是r
20: {
24: if(x==0)
25: {
26: r.name="mike";
27: r.sex="man";
28: }
29: else
30: {
31: r.name="丽丽";
32: r.sex = "女女女女女";
33: }
34: x = (x+1)%2;
35: r.flag = true;
36: r.notify();
37: }
38: }
39: }
40: }
41:
42: class Output implements Runnable
43: {
44: private Res r ;
45:
46: Output(Res r)
47: {
48: this.r = r;
49: }
50: public void run()
51: {
52: while(true)
53: {
54: synchronized(r)//加锁,对象是r
55: {
58: System.out.println(r.name+"...."+r.sex);
59: r.flag = false;
60: r.notify();
61: }
62: }
63: }
64: }
65: class InputOutputDemo
66: {
67: public static void main(String[] args)
68: {
69: Res r = new Res();
70:
71: Input in = new Input(r);
72: Output out = new Output(r);
73:
74: Thread t1 = new Thread(in);
75: Thread t2 = new Thread(out);
76:
77: t1.start();
78: t2.start();
79: }
80: }
正确结果为:
- mike….man
- 丽丽….女女女女女
交替打印
如果不加同步,或者同步使用的对象不是同一个,就会出现以下结果。
5)等待唤醒机制:
线程池:等待线程都存在线程池中。
1: class Res
2: {
3: String name;
4: String sex;
5: boolean flag = false;
6: }
7: class Input implements Runnable
8: {
9: private Res r ;
10: Input(Res r)
11: {
12: this.r = r;
13: }
14: public void run()
15: {
16: int x = 0;
17: while(true)
18: {
19: synchronized(r)
20: {
21:
22: if(r.flag)
23: //等待
24: try{r.wait();}catch(Exception e){}
25: if(x==0)
26: {
27: r.name="mike";
28: r.sex="man";
29: }
30: else
31: {
32: r.name="丽丽";
33: r.sex = "女女女女女";
34: }
35: x = (x+1)%2;
36: r.flag = true;
37: r.notify();//唤醒
38: }
39: }
40: }
41: }
42: class Output implements Runnable
43: {
44: private Res r ;
45:
46: Output(Res r)
47: {
48: this.r = r;
49: }
50: public void run()
51: {
52: while(true)
53: {
54: synchronized(r)
55: {
56: if(!r.flag)
57: //等待
58: try{r.wait();}catch(Exception e){}
59: System.out.println(r.name+"...."+r.sex);
60: r.flag = false;
61: r.notify();//唤醒
62: }
63: }
64: }
65: }
66: class InputOutputDemo
67: {
68: public static void main(String[] args)
69: {
70: Res r = new Res();
71:
72: Input in = new Input(r);
73: Output out = new Output(r);
74:
75: Thread t1 = new Thread(in);
76: Thread t2 = new Thread(out);
77:
78: t1.start();
79: t2.start();
80: }
81: }
?
wait;notify();notifyAll(); 都使用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。
这些方法之所以定义在Object类中是因为这些方法所使用的对象是任意对象,即任意对象都能调用这些方法。
这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,
只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。
不可以对不同锁中的线程进行唤醒。生产者消费者的例子:
1: class Resource
2: {
3: private String name;
4: private int count = 1;
5: private boolean flag = false;
6: // t1 t2
7: public synchronized void set(String name)
8: {
9: while(flag)
10: try{this.wait();}catch(Exception e){}//t1(放弃资格) t2(获取资格)
11: this.name = name+"--"+count++;
12:
13: System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
14: flag = true;
15: this.notifyAll();//唤醒所有等待线程,防止出现全部线程等待的情况
16: }
17:
18:
19: // t3 t4
20: public synchronized void out()
21: {
22: while(!flag)
23: try{wait();}catch(Exception e){}//t3(放弃资格) t4(放弃资格)
24: System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
25: flag = false;
26: this.notifyAll();//唤醒所有等待线程,防止出现全部线程等待的情况
27: }
28: }
29:
30: class Producer implements Runnable
31: {
32: private Resource res;
33:
34: Producer(Resource res)
35: {
36: this.res = res;
37: }
38: public void run()
39: {
40: while(true)
41: {
42: res.set("+商品+");
43: }
44: }
45: }
46:
47: class Consumer implements Runnable
48: {
49: private Resource res;
50:
51: Consumer(Resource res)
52: {
53: this.res = res;
54: }
55: public void run()
56: {
57: while(true)
58: {
59: res.out();
60: }
61: }
62: }
使用notifyAll之后虽然解决的出现全部等待的情况的问题,但是新问题又出现了,唤醒对方线程的同时,将本方线程也唤醒了。
JDK1.5中提供了多线程升级解决方法,将synchronized替换成lock,unlock,condition这些现实化的操作方式,将Object中的notify和notifyAll方法替换成了condition.signal和condition.signalAll方法,可以通过不同的condition实现只唤醒对方等待线程的操作,也就是一个锁对应多个condition。显示的锁机制和现实的等待唤醒机制。
6)停止线程:
stop方法已经过时,只用结束run方法结束线程,而要结束run方法,就要控制住循环。
特殊情况:
当线程处于了冻结状态。
就不会读取到标记。那么线程就不会结束。当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。interrupt()方法,强制让线程恢复到运行状态。会受到一个InterruptException异常。强制让线程恢复到运行状态中来后,可以操作标记让线程结束。
7)守护线程、join方法、优先级,yield
当所有的线程都是守护线程时,JVM就自动退出了。Thread t = new Thread();t.setDanemon();//将t设置为守护线程(经典的圣斗士星矢例子);
作后台线程讲更好理解。当所有的前台线程都结束后后台线程就结束。
join方法:
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------主线程运行到join方法时,join对应的线程执行完以后主线程才会继续执行。一般使用于临时加入线程的情况。
优先级:
1-10,默认为5,setPriority();优先级10通常写为Thread.MAX_PRIORITY,1写成MIN_PRIORITY,5写成NORM_PRIORITY。
yield:相当于强制释放CPU执行权。