08 JAVA 线程
2015-08-03 23:55
393 查看
中国游泳队棒棒哒~
一、介绍
进程是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
![](https://documents.lucidchart.com/documents/db777df5-cf36-4847-a36a-abeb2b82902f/pages/0_0?a=5280&x=137&y=70&w=506&h=220&store=1&accept=image%2F*&auth=LCA%2039230a881017e3f1964ba73adadda7f9a86614ff-ts%3D1438584091)
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接口,因为这样可以“多实现”(实现多个),而子类只能够是单继承
![](http://img.blog.csdn.net/20150803153438675)
![](http://img.blog.csdn.net/20150803153447665)
- 静态方法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();
}
例子:
![](https://documents.lucidchart.com/documents/db777df5-cf36-4847-a36a-abeb2b82902f/pages/0_0?a=5582&x=133&y=82&w=594&h=396&store=1&accept=image%2F*&auth=LCA%20f385e33d93b5a65ab1d74e3963ff3ce4de9b2cea-ts%3D1438584091)
四、死锁
标记锁,这个标记保证在任意一个时刻,只有一个线程访问该对象,保证共享数据操作的完整性,每个对象都应该有这个标记
哲学家吃饭问题:
面试题:
如以下程序,运行m2方法是哪一个线程?main函数
2000
b = 2000
多生产者多消费者:
this.wait(),阻塞当前访问对象的线程(一个对象必须获取锁之后才能使用wait方法,因为没锁住,怎么能阻塞,阻塞之后,锁就不再归我所有,只有被叫醒之后才重新有锁,这个与sleep方法不同,因为sleep阻塞之后,不释放锁)
this.notify(),叫醒一个正在等待我这个对象的线程
this.notifyAll(),不叫醒自己
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
一、介绍
进程是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
相关文章推荐
- spring 事务传播属性
- Java学习笔记(九) Bitset
- JDK动态代理浅析
- 转!! Java中ThreadLocal的设计与使用
- Eclipse中的java、xml代码提示功能设置
- Java线程的六种状态
- 转!!java中关键字volatile的作用
- eclipse导入到android studio的正确姿势
- java 枚举类小结 Enum
- dom4j将xml转换成对象
- Eclipse将引用了第三方jar包的Java项目打包成jar文件的两种方法
- Java基础-----面向对象
- eclipse源代码热部署
- Java面向对象--继承
- Struts_json插件配置参数
- HeadFirst Java读后感
- JAVA拾遗——基本语法,汇总及练习。
- Java--传参的值传递和引用传递问题
- java notepad++
- 转!!java线程状态