多线程学习笔记
看着马士兵的视频学习了一些简单的多线程知识,
就着《Java核心技术1》 把相关知识梳理一下。
-
1 线程和进程的区别:
机器上的每一个exe文件,打开之后就会形成一个“进程”,进程是一个运行中的程序,是系统分配资源的单位,而“线程”,则是进程中多条指令集执行的路径,线程之间共享数据。与进程相比,线程更为轻量级。如果把进程比作一条马路,那么线程就是各个车道。 -
2 实现多线程的方法:
-
1 定义Class t 实现Runnable接口 并且重写run方法 在主线程中New Thread 并且将t传入Thread()
1 2 3 4 5 6 7 8class R implements Runnable{ public void run(){} } public void static void main(String[]args){ R r = new R(); Thread t = new Thread(r); t.start(); } -
2 定义Class MyThread 继承Thread类 并且重写run方法
1 2 3 4 5 6 7class MyThread extends Thread{ public void run(){} } public void static void main(String[]args){ MyThread mt = new MyThread(); mt.start(); }需要注意的是 直接调用对象的run方法是方法调用 而不会启动新线程
-
- 3 线程的一些属性和状态:
- 1 优先级:
一个线程的子类继承其父类的优先级,优先级的范围从1~10,线程调度器在有机会选择新线程时,会优先选择优先级别较高的线程。MIN_PRIORITY = 1,MAX_PRIORITY = 10,可以用setPriority方法来改变线程的优先级。 - 2 状态:
- 1 New
- 2 Runnable Runnable不代表线程正在运行! 只能说线程具有获得运行时间的能力
- 3 Blocked 被阻塞 当线程试图获得一个内部对象锁 而该锁被其他线程所持有时 该线程被阻塞
- 4 Wating 等待 Object.wait或Thread.join 使线程“挂起” 等待通知 即Object.notify
- 5 Timed Wating Waiting状态的计时版 当时间超出设定或接到通知 终止该态度
- 6 Terminated 被终止 两种情况 因为run方法运行结束而自然死亡 和因未捕获的异常而异常死亡
- 1 优先级:
-
4 线程同步:
线程同步是为了解决,因多线程而导致代码片段执行不完整,造成的异常状况。
实现线程同步的方法即给代码块加锁,保证其能够完整的一次执行完,而不会因为CPU的切换造成线程中断从而导致数据出错。
加锁的方法有两种:-
1 Java SE 5.0 引入了ReentrantLock类 可以用来保护代码块
1 2 3 4 5 6 7 8 9myLock.lock();//a ReentrantLock object try{ //critical section }catch{ } finally{ myLock.unlock()l; //make sure the lock is unlocked even if an exception is thown } -
2 利用synchronized关键字来保护方法的执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20int b = 100; public synchronized void m1(){ b = 1000; try{ Thread.sleep(5000); }catch(InterruptedException e ){ e.printStackTrace(); } System.out.println("m1 b = " + b ); } public synchronized void m2(){ try{ Thread.sleep(3000); }catch(InterruptedException e ){ e.printStackTrace(); } b = 2000; System.out.println("m2 b = " + b ); }在方法前加sychronized关键字来声明,那么对象的锁将保护该方法,如果该对象的其他方法试图获得该锁,那么他将进入阻塞状态,需等待拥有锁的方法执行完毕才能获得锁。
Tips:1 所有阻塞式方法 sleep,wait,await都有可能抛出InterruptedException 需要处理**2 sychronized关键字使用起来较为方便 应尽可能的多使用它**
-
-
5 条件对象和死锁
-
1 条件对象
实际在设计多线程同步的程序的时候,如设计一个银行相互转账的系统,需要用同步方法来保证存取款时不被打断,金额才能正确。
不过在取款之前,需要设计一个变量,检测账户是否有足够的余额供取出才能继续执行取出操作。1 2 3 4 5public sychronized void getMoney(acount[],amount){ while(accounts[from] < amount){ this.wait(); //当取出金额大于被取出账户余额的时候 调用wait方法 挂起线程 } }当无法执行操作时,调用Object类的wait方法,使该线程放弃锁进入等待集,当锁可用时,该线程不能马上解除,相反,它仍会处于阻塞状态,需要等待另一个线程的通知”notify”
1 2 3public sychronized void saveMoney(acount[];amount){ this.notify();// this.notifyAll(); }当有用户向账户中存钱时,需要调用notify方法,通知被阻塞的取钱的线程,检查是否有足够金额可以取出。
注意区别wait和sleep方法:wait方法会释放对象锁 而sleep仍持有锁 wait方法是属于Object类的 而sleep是属于Thread类的 -
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 31 32 33 34 35class TestDeadLock extends Runnable{ static Object o1 = new Object(); static Object o2 = new Object(); public int flag = 1; public void run(){ if( flag == 1){ sychronized(o1){ } sychronized(o2){ System.out.println("1"); } } if(flag == 0){ sychronized(o2){ } sychronized(o1){ System.out.println("0"); } } } } public static void main(String[]args){ TestDeadLock td1 = new TestDeadLock(); TestDeadLock td2 = new TestDeadLock(); td1.flag = 1; td2.falg = 0; Thread t1 = new Thread(td1); Thread t2 = new Thread(td2); t1.start(); t2.start(); }当td1的run方法执行 锁住o1对象后试图锁住o2 而t2线程锁住了o2 t1无法得到o2的锁
而t2也无法得到o1的锁 于是….就发生了线程死锁 两个线程都被阻塞了 这样就是死锁 (DeadLock)
令人悲伤是,Java并没有任何东西可以完全避免或者打破死锁,必须仔细设计程序,确保不会出现死锁现象。
-
差不多就学到了这里,线程的学习就暂时告一段落,日后应用再多多钻研。
加油~