您的位置:首页 > 编程语言 > Java开发

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



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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: