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


0 0
原创粉丝点击