08 JAVA 线程
来源:互联网 发布:文案策划知乎 编辑:程序博客网 时间:2024/05/29 13:49
中国游泳队棒棒哒~
一、介绍
进程是a set of instructions which would be executed in sequence
线程是一个进程内部的顺序控制流(程序的不同执行路径)
进程 VS 线程
- 每个进程都有独立的代码和数据空间(进程上下文),是资源分配单位,线程共享进程的资源,进程间的切换会有很大的开销
- 线程可以看成是轻量级的进程,同一类线程共享代码和数据空间,但是每个线程有独立的运行栈和程序计数器,线程切换开销小
- 多进程,如OS同时执行多个应用任务
- 多线程,如同一个Application中有多个顺序流同时执行
二、JAVA中的实现
JAVA线程通过java.lang.Thread类来实现,我们可以通过它的实例来创建新的线程,每个线程都是通过线程对象所对应的run方法来定义操作的内容,run方法成为线程体,然后通过start方法来启动进程(不直接调用run,因为那样就变成了方法调用,并未开始线程,此时线程is not alive),VM启动时会调用一个由主方法main所定义的线程
Mark: 其实应用程序使用java的Executor框架来创建线程池~
1、Runnable接口
Runnable接口只有一个方法public void run()用来定义线程运行体,方法中可以调用Thread类的静态方法,如public static Thread currentThread()获取当前线程的引用。Thread thread = new Thread(target) 启动一个线程,target是Runnable接口类型对象(实现run方法):
public void run() {
if (target !=null) {
target.run();
}
}
Runnable接口可以为多个线程提供共享数据:
public class T1 {
public static void main(String[] args) {
Runner rn =new Runner();
Thread t =new Thread(rn);
Thread t1 =new Thread(rn);
t.start();//启动线程
t1.start();//启动线程
}
}
class Runner implements Runnable {
int num = 0;
public void run() {
num ++;
System.out.println(Thread.currentThread().getName()+" "+num);
}
}
结果:Thread-0 2
Thread-1 2
2、Thread类
我们可以继承Thread类,然后重写run方法,因为继承Thread类的子类本身就是一个Thread类型,我们可以直接实例化子类创建线程
public class T1 {
public static void main(String[] args) {
Runner rn =new Runner();
Runner rn1 =new Runner();
rn.start();
rn1.start();
System.out.println(Thread.currentThread().getName());
}
}
class Runner extends Thread {
int num = 0;
public void run() {
num ++;
System.out.println(Thread.currentThread().getName()+" "+num);
}
}
main
Thread-1 1
Thread-0 1
3、实现Runnable接口 VS 继承Thread类
我们可以继承Thread类或者实现Runnable接口来实现线程运行体,最好用实现Runnable接口,因为这样可以“多实现”(实现多个),而子类只能够是单继承
- 静态方法sleep(),会抛出异常,因为可能被打断(重写方法时不能throw出不同的异常)
interrupt改变中断的状态,而并不能中断一个运行的线程,是给受阻塞的线程抛出一个中断信号,让受阻线程退出阻塞的状态,比如进程被Object.wait, Thread.join和Thread.sleep阻塞(这个期间会不断的检查中断状态值),就可以用interrupt中断这个阻塞线程,并抛出InterruptedException,如果线程没有被阻塞,interrupt则无效。final stop方法(已经被废弃了),比interrupt方法粗暴,立马死掉,连处理异常的机会都没有(对于interrupt异常,我们还可以处理异常,比如关闭某个打开的资源等),但是如果进程死锁当机,也只能stop了~
我们希望可以循环执行线程运行体,直到某个点停止,此时我们可以使用flag=true, while(flag),然后在某个合适的地方将flag复制为false~
- join(),合并线程,先执行完一个再执行另外一个
- yield(),表示退让一下(让出CPU一下)
- 优先级
线程调度器会监控程序中启动后进入就绪状态的所有线程,线程调度器按照线程的优先级决定应该优先调度哪一个线程,优先级的范围是1~10,缺省5
静态变量:
MIN_PRIORITY = 1
MAX_PRIORITY = 10
NORM_PRIORITY = 5
int getPriority() //返回优先级
void setPriority(int priority) //设置优先级
三、线程同步
小明和阿华共同知道某张卡的账号和密码(卡里只有3000块钱),然后小明去ATM机上取2000块钱,而阿华同时想要网上转账给小黄,此时ATM刚验证了卡里有>=2000的资金决定给小明钱的时候,阿华正好转了2000给小黄了,3000还没有减去2000的时候,ATM又吐了2000给小明,此时还是3000-2000,然后账号还剩1000。。。这样不就乱了吗?我们该怎么做?保证一个人取钱的时候,另一个人不能同时取不就行了~
synchronized 方法 {
执行方法的过程中,当前调用者(类实例对象)被锁定
}
方法 {
synchronized(this) {
当执行某区域的过程过,不会被另一个线程打断
}
}
每个对象都带有一个内存锁,类也有一个
两个没什么区别,只是粒度大小的问题,synchronized表示必须获取当前对象的锁才能执行,出了作用域就是释放锁。访问一个对象的同步代码时,就不能访问这个对象的其他同步代码,但是可以访问这个对象的非同步代码,yooooo~
我们可以同步静态方法和其他任意引用对象~
synchronized(Object A) {
A.method();
}
例子:
四、死锁
标记锁,这个标记保证在任意一个时刻,只有一个线程访问该对象,保证共享数据操作的完整性,每个对象都应该有这个标记
public class TestDeadLock implements Runnable {static Object o1 = new Object(), o2 = new Object();boolean flag = true;public static void main(String[] args) {TestDeadLock tdl1 = new TestDeadLock();TestDeadLock tdl2 = new TestDeadLock();tdl2.flag = false;Thread t1 = new Thread(tdl1);Thread t2 = new Thread(tdl2);t1.start();t2.start();}public void run() {System.out.println(flag);if(flag) {synchronized(o1) {try {Thread.sleep(500);} catch(InterruptedException ie) {ie.printStackTrace();}synchronized(o2) {System.out.println("o2");}}} else {synchronized(o2) {try {Thread.sleep(500);} catch(InterruptedException ie) {ie.printStackTrace();}synchronized(o1) {System.out.println("o1");}}}}}
哲学家吃饭问题:
class Philosopher implements Runnable{int id;Chopstick chopstick;boolean flag = true;Philosopher(int id, Chopstick chopstick) {this.id = id;this.chopstick = chopstick;}void eat(){while(!chopstick.getChopstick(id)) {}System.out.println("Philosopher"+id+" is eating");try {Thread.sleep(1000);} catch(InterruptedException ie) {ie.printStackTrace();}chopstick.releaseChopstick(id);}void think() {System.out.println("Philosopher"+id+" is thinking");try {Thread.sleep(5000);} catch(InterruptedException ie) {ie.printStackTrace();}}public void run() {int i = 0;System.out.println("Philosopher"+id+" starts");while(flag) {eat();think();i++;if(i>5) shutdown();}}public void shutdown() {flag = false;}}class Chopstick {boolean[] chopstick;Chopstick() {chopstick = new boolean[6];for(int j = 0; j < 6; j++)chopstick[j] = false;}synchronized boolean getChopstick(int id) {int right = (id+1) % chopstick.length, left = id;if((!chopstick[left]) && (!chopstick[right])) {chopstick[left] = true;chopstick[right] = true;System.out.println("Philosopher"+id+" gets chopstick"+left+" and chopstick"+right);return true;}return false;}synchronized void releaseChopstick(int id) {int right = (id+1) % chopstick.length, left = id;chopstick[left] = false;chopstick[right] = false;}}public class PhilosopherEat {public static void main(String[] args) {Chopstick chopstick = new Chopstick();for(int i = 0; i < 6; i++) {new Thread(new Philosopher(i, chopstick)).start();}}}
面试题:
如以下程序,运行m2方法是哪一个线程?main函数
public class TT implements Runnable {int b = 100;public synchronized void m1() throws Exception{//Thread.sleep(2000);b = 1000;Thread.sleep(5000);System.out.println("b = " + b);}public void m2() throws Exception {Thread.sleep(2500);b = 2000;}public void run() {try {m1();} catch(Exception e) {e.printStackTrace();}}public static void main(String[] args) throws Exception {TT tt = new TT();Thread t = new Thread(tt);t.start();tt.m2();System.out.println(tt.b);}}结果:
2000
b = 2000
多生产者多消费者:this.wait(),阻塞当前访问对象的线程(一个对象必须获取锁之后才能使用wait方法,因为没锁住,怎么能阻塞,阻塞之后,锁就不再归我所有,只有被叫醒之后才重新有锁,这个与sleep方法不同,因为sleep阻塞之后,不释放锁)
this.notify(),叫醒一个正在等待我这个对象的线程
this.notifyAll(),不叫醒自己
class Producer implements Runnable{int id;Stack stack = null;int pid = 1;Producer(int id, Stack stack) {this.id = id;this.stack = stack;}public void run() {for(int i = 0; i < 5; i++) {System.out.println("Producer"+id+" Product:"+id+":"+pid);stack.push(id+":"+pid);pid ++;try {Thread.sleep((long)(Math.random()*500));} catch(InterruptedException ie) {ie.printStackTrace();}}}}class Consumer implements Runnable{int id;Stack stack = null;Consumer(int id, Stack stack) {this.id = id;this.stack = stack;}public void run(){for(int i = 0; i < 5; i++) {Product p = stack.pop();System.out.println("Consumer"+id+" "+p);try {Thread.sleep((long)(Math.random()*1000));} catch(InterruptedException ie) {ie.printStackTrace();}}}}class Stack {Product[] products = new Product[6];int index = 0;synchronized void push(String id) {while(index == 6) { //采用if的哈,如果发生异常跳出if之后,继续处理下面部分,所以采用while比较好try {this.wait(); //释放锁} catch (InterruptedException e) {e.printStackTrace();}}Product p = new Product(id);products[index] = p;index++;this.notifyAll(); //通知等待这个对象的线程,就是检测到index==0那个线程}synchronized Product pop() {while(index == 0) {//采用if的哈,如果发生异常跳出if之后,继续处理下面部分,所以采用while比较好try {this.wait(); //释放锁} catch (InterruptedException e) {e.printStackTrace();}}index--;this.notifyAll();//通知等待这个对象的线程,就是发现index == 6那个线程return products[index];}}class Product {String id;Product(String id) {this.id = id;}public String toString() {return "Product:"+id;}}public class ProducerConsumer {public static void main(String[] args) {Stack stack = new Stack();Thread t = new Thread(new Producer(1, stack));Thread t1 = new Thread(new Producer(2, stack));Thread t2 = new Thread(new Consumer(1, stack));Thread t3 = new Thread(new Consumer(2, stack));t.start();t1.start();t2.start();t3.start();}}
JAVA还提供了很多其他的API来来实现线程的暂停,恢复,结束等,但是几乎都被废止了,因为会引入一些缺点,比如stop方法会引入一个ThreadDeath异常,之前所有的获得锁都会被释放,会造成inconsistency
Reference:
1. http://blog.csdn.net/hudashi/article/details/6958550
2. 马士兵JAVA基础教程
3. http://m.blog.csdn.net/blog/u010802573/38661719
- 08 JAVA 线程
- Java线程:什么是线程
- Java线程:线程池
- java线程--线程退出
- JAVA-线程/线程锁
- Java线程:什么是线程
- Java线程:线程中断
- Java线程:线程状态
- Java线程: 线程调度
- Java线程:线程交互
- java--线程--线程池
- java 线程
- Java线程
- java线程
- java线程
- Java线程
- Java线程
- java线程
- ios UIWebView截获html并修改便签内容
- codeforces 25C. Roads in Berland
- Kalman Filter - Kalman滤波器从原理到实现
- Linux 任务后台运行
- Linux下map hash_map和unordered_map效率比较
- 08 JAVA 线程
- ffmpeg里获取完整一帧的例子
- 有关 #!/usr/bin/python 你不得不知道的知识
- r8051xc2定时器2的用法
- Cell上添加UITextField出现的问题解决
- Java中JDBC环境配置
- Happy Programming Contest zoj3703 dp
- 机器学习评估与优化方法
- Tcp流套接字两个需要注意的问题:粘包和包分段