Java Thread

1,线程的基本概念java

线程是一个程序内部的顺序控制流。多线程

线程和进程的区别:(资源分配和处理器分配的基本单元)ide

①,每一个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销;
②,线程能够当作是轻量级进程,同一类线程共享代码和数据空间,每一个线程有独立的运行栈和程序计数器(PC),线程切换的开销小;
③,多进程:操做系统中能同时运行多个任务(程序);
④,多线程:在同一应用程序中有多个顺序流同时执行;

Java线程是经过java.lang.Thread类来实现的。VM启动时会有一个由主方法(public static void main(){})所定义的线程。能够经过建立Thread的实例来建立新的线程。每一个线程都是经过某个特定的Thread对象所对应的方法run()来完成其操做的,方法run()称为线程体。经过调用Thread类的start()方法来启动一个线程。
注意:
要注意方法调用和运行线程的区别。方法调用,主程序会等方法运行完成程序再继续往下走;运行线程:至关于新建一个分支,主程序与线程并行执行。函数

2,线程的建立和启动this

第一种:定义线程类实现Runnable接口spa

Thread thread = new Thread(target);//target为Runnable接口类型 操作系统

Runnable中只有一个方法: public void run();用以定义线程运行体。 线程

使用Runnable接口能够为多个线程提供共享的数据。在实现Runnable接口的类的run方法定义中可使用Thread的静态方法: 调试

public static Thread currentThread()获取当前线程的引用。code

例如:

public static void main(String[] args) { Thread t = new Thread(new MyThread()); t.start();//线程启动
    t.run();//方法调用
} class MyThread implements Runnable { public void run() { ... } }

第二种:定义一个Thread的子类,并重写其run方法,如:

    class MyThread extends Thread {
        public void run() {...}
    }
而后生成该类的对象:MyThread thread = new MyThread(...);

例如:

public static void main(String[] args) { Thread t = new MyThread(); t.start(); } class MyThread extends Thread { public void run() { ... } }

3,线程的状态

3.1,线程的状态类型:

①,新建状态(New):建立了一个线程对象,它仅仅做为一个对象实例存在,JVM没有为其分配CPU时间片等线程运行资源; ②,就绪状态(Runnable):在处于建立状态的线程中调用start方法将线程的状态转换为就绪状态。这时,线程已经获得除CPU时间以外的其它系统资源,只等JVM的线程调度器按照线程的优先级对该线程进行调度,从而使该线程拥有可以得到CPU时间片的机会; ③,运行状态(Running):就绪状态的线程获取了CPU,执行程序代码; ④,阻塞状态(Blocked):阻塞状态是线程由于某种缘由放弃CPU使用权,暂时中止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的状况分三种: (1),等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池(waitting queue)中; (2),同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中; (3),其余阻塞:运行的线程执行sleep()或join()方法,或者发出I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超市、join()等待线程终止或者超时、或者I/O处理完毕时,线程从新转入就绪状态; ⑤,挂起状态(Suspend):能够经过调用suspend方法(已过期)将线程的状态转换为挂起状态。这时,线程将释放占用的全部资源,由JVM调度转入临时存储空间,直至应用程序调用resume方法(已过期)恢复线程运行; ⑥,死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

3.2,线程状态转换图


3.3,阻塞和挂起的区别:

操做系统中挂起和阻塞的区别以下: (1),挂起是一种主动行为,所以恢复也应该要主动完成,而阻塞则是一种被动行为,是在等待事件或资源时任务的表现,你不知道他何时被阻塞(pend),也就不能确切 的知道他何时恢复阻塞。并且挂起队列在操做系统里能够当作一个,而阻塞队列则是不一样的事件或资源(如信号量)就有本身的队列; (2),阻塞(pend)就是任务释放CPU,其余任务能够运行,通常在等待某种资源或信号量的时候出现。挂起(suspend)不释放CPU,若是任务优先级高就永远轮不到其余任务运行,通常挂起用于程序调试中的条件中断,当出现某个条件的状况下挂起,而后进行单步调试; (3),阻塞(pend)是task主动去等一个事件或消息,suspend是直接悬挂task,之后这个task和你没任何关系,任何task间的通讯或者同步都和这个suspended task没任何关系了,除非你resume task; (4),任务调度是操做系统来实现的,任务调度时,直接忽略挂起状态的任务,可是会顾及处于pend下的任务,当pend下的任务等待的资源就绪后,就能够转为ready了。ready只须要等待CPU时间,固然,任务调度也占用开销,可是不大,能够忽略。能够这样理解,只要是挂起状态,操做系统就不在管理这个任务了; (5),挂起是主动的,通常须要用挂起函数进行操做,若没有resume的动做,则此任务一直不会ready。而阻塞是由于资源被其余任务抢占而处于休眠态。二者的表现方式都是从就绪态里“清掉”,即对应标志位清零,只不过实现方式不同。 

3.4,wait和sleep 的区别

(1),这两个方法来自不一样的类分别是,sleep来自Thread类,和wait来自Object类。sleep是Thread的静态类方法,谁调用的谁去睡觉,即便在a线程里调用了b的sleep方法,实际上仍是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。 (2),最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其余线程可使用同步控制块或者方法。sleep不出让系统资源;wait是进入线程等待池等待,出让系统资源,其余线程能够占用CPU。通常wait不会加时间限制,由于若是wait线程的运行资源不够,再出来也没用,要等待其余线程调用notify/notifyAll唤醒等待池中的全部线程,才会进入就绪队列等待OS分配系统资源。sleep(milliseconds)能够用时间指定使它自动唤醒过来,若是时间不到只能调用interrupt()强行打断。Thread.Sleep(0)的做用是“触发操做系统马上从新进行一次CPU竞争”。 (3),使用范围:wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep能够在任何地方使用 synchronized(x){ x.notify() //或者wait()
 } (4),sleep必须捕获异常,而wait,notify和notifyAll不须要捕获异常

3.5,线程控制的基本方法

 isAlive() 判断线程是否还“活着”,即线程是否还未终止
 getPriority() 得到线程的优先级数值
 setPriority(int newPriority) 设置线程的优先级数值

 sleep(long millis)

 sleep(long millis, int nanos)

将当前线程睡眠指定时间
 join() 调用某线程的该方法,将当前线程与该线程“合并”,即等待该线程结束,再恢复当前线程的运行
 yield() 让出CPU,当前线程进入就绪队列等待调度
 wait() 当前线程进入线程等待池(wait pool)

 notify()

 notifyAll()

唤醒对象的wait pool中的一个/全部等待线程

 

3.5.1,线程优先级

Java提供一个线程调度器来监控程序中就绪状态的全部线程。线程调度器按照线程的优先级决定应调度哪一个线程来执行。线程的优先级用数字标志,范围1~10,缺省优先级为5

public static final int MIN_PRIORITY = 1; public static final int NORM_PRIORITY = 5; public static final int MAX_PRIORITY = 10; 使用如下方法得到或这只线程对象的优先级: public final void setPriority(int paramInt) { checkAccess(); if ((paramInt > 10) || (paramInt < 1)) { throw new IllegalArgumentException(); } ThreadGroup localThreadGroup; if ((localThreadGroup = getThreadGroup()) != null) { if (paramInt > localThreadGroup.getMaxPriority()) { paramInt = localThreadGroup.getMaxPriority(); } setPriority0(this.priority = paramInt); } } public final int getPriority() { return this.priority; }

3.5.2,sleep/join/yield方法

(1),sleep:能够调用Thread的静态方法sleep使得当前线程休眠,因为是静态方法,sleep能够直接由类名直接调用。

例子:

public class Test { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); try { Thread.sleep(10000);//主线程睡眠 
        } catch (InterruptedException e) { e.printStackTrace(); } thread.interrupt();//打断子线程(不推荐的方法,还有stop()) //thread.flag = false;
 } } class MyThread extends Thread { boolean flag = true; public void run() { while(flag) { System.out.println("====" + new Date() + "===="); try { sleep(1000); } catch (InterruptedException e) { return;//sleep时出现异常则线程直接结束
 } } } }

结果:

====Tue Jun 14 11:05:30 CST 2016====
====Tue Jun 14 11:05:31 CST 2016====
====Tue Jun 14 11:05:32 CST 2016====
====Tue Jun 14 11:05:33 CST 2016====
====Tue Jun 14 11:05:34 CST 2016====
====Tue Jun 14 11:05:35 CST 2016====
====Tue Jun 14 11:05:36 CST 2016====
====Tue Jun 14 11:05:37 CST 2016====
====Tue Jun 14 11:05:38 CST 2016====
====Tue Jun 14 11:05:39 CST 2016====

源码:

public static native void sleep(long paramLong) throws InterruptedException; public static void sleep(long paramLong, int paramInt) throws InterruptedException { if (paramLong < 0L) { throw new IllegalArgumentException("timeout value is negative"); } if ((paramInt < 0) || (paramInt > 999999)) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if ((paramInt >= 500000) || ((paramInt != 0) && (paramLong == 0L))) { paramLong += 1L; } sleep(paramLong); }

(2),join:合并线程

public class Test { public static void main(String[] args) { MyThread thread = new MyThread("abcde"); thread.start(); try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 3; i++) { System.out.println("I am main thread"); } } } class MyThread extends Thread { MyThread(String name) { super(name); } public void run() { for (int i = 0; i < 3; i++) { System.out.println("I am " + getName()); } } }

结果:

I am abcde
I am abcde
I am abcde
I am main thread
I am main thread
I am main thread

源码:

public final void join() throws InterruptedException { join(0L); } public final synchronized void join(long paramLong) throws InterruptedException { long l1 = System.currentTimeMillis(); long l2 = 0L; if (paramLong < 0L) { throw new IllegalArgumentException("timeout value is negative"); } if (paramLong == 0L) { while (isAlive()) { wait(0L); } } while (isAlive()) { long l3 = paramLong - l2; if (l3 <= 0L) { break; } wait(l3); l2 = System.currentTimeMillis() - l1; } }

(3),yield:让出CPU,给其余线程执行的机会

例子:

public class Test { public static void main(String[] args) { MyThread t1 = new MyThread("t1"); MyThread t2 = new MyThread("t2"); t1.start(); t2.start(); } } class MyThread extends Thread { MyThread(String name) { super(name); } public void run() { for (int i = 1; i <= 100; i++) { System.out.println(getName() + ":" + i); if(i%10 == 0) { yield(); } } } }

结果:

每当被10整除的时候会yield ... t1:14 t2:70 t1:15 ... ... t2:72 t1:20 t2:73 ... t2:76 t1:30 t2:77 ...

源码:

public static native void yield();

4,线程同步

在Java语言中,引入了对象互斥锁的概念,保证共享数据错作的完整性,每一个对象对应于一个可称为”互斥锁“的标记,这个标记保证在任一时刻,只能有一个线程访问该对象。
关键字synchronized来与对象的互斥锁联系。当某个对象synchronized修饰时,代表该对象在任一时刻只能由一个线程访问。
例一:

public class Test implements Runnable { Timer timer = new Timer(); public static void main(String[] args) { Test test = new Test(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); t1.setName("t1"); t2.setName("t2"); t1.start(); t2.start(); } @Override public void run() { timer.add(Thread.currentThread().getName()); } } class Timer { private static int num = 0; public synchronized void add(String name) { // synchronized (this) {
        num++; try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(name + ", 你是第" + num + "个使用timer的线程"); // }
 } }

结果一(可能):

t1, 你是第2个使用timer的线程
t2, 你是第2个使用timer的线程

加上synchronized控制

结果二:

t1, 你是第1个使用timer的线程
t2, 你是第2个使用timer的线程

例二:

public class Test implements Runnable { public int flag = 1; static Object o1 = new Object(); static Object o2 = new Object(); @Override public void run() { System.out.println("flag = " + flag); if(flag == 1) { synchronized (o1) {//得到对象o1锁 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2) {//得到对象o2锁 System.out.println(1); } } } if(flag == 0) { synchronized (o2) {//得到对象o2锁 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1) {//得到对象o1锁 System.out.println(0); } } } } public static void main(String[] args) { Test test1 = new Test(); Test test2 = new Test(); test1.flag = 1; test2.flag = 0; Thread t1 = new Thread(test1); Thread t2 = new Thread(test2); t1.start(); t2.start(); } }

结果:

flag = 0 flag = 1 分别在等待得到o1,o2对象锁的,程序死锁,没法结束

例三:

哲学家吃饭问题

5,生产者消费者问题

public class Test { public static void main(String[] args) { SyncStack ss = new SyncStack(); Producer p = new Producer(ss); Consumer c = new Consumer(ss); new Thread(c).start(); new Thread(p).start(); new Thread(p).start();//多加个生产者线程
 } } class WoTou { int id; WoTou(int id) { this.id = id; } public String toString() { return "WoTou:" + id; } } class SyncStack { int index = 0; WoTou[] arrWT = new WoTou[5]; //push
    public synchronized void push(WoTou wt) { while(index == arrWT.length) { try { this.wait();//阻塞
            } catch (InterruptedException e) { e.printStackTrace(); } } this.notifyAll();//唤醒其余全部线程
        arrWT[index] = wt; index++; } //pop
    public synchronized WoTou pop() { while (index == 0) { try { this.wait();//阻塞
            } catch (InterruptedException e) { e.printStackTrace(); } } this.notifyAll();//唤醒其余全部线程
        index--; return arrWT[index]; } } //生产者
class Producer implements Runnable { SyncStack ss = null; Producer(SyncStack ss) { this.ss = ss; } @Override public void run() { for(int i=1; i<= 10; i++) { WoTou wt = new WoTou(i);//produce
 ss.push(wt); System.out.println("生产了:" + wt); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } //消费者
class Consumer implements Runnable { SyncStack ss = null; Consumer(SyncStack ss) { this.ss = ss; } @Override public void run() { for (int i = 1; i <= 10; i++) { WoTou wt = ss.pop(); System.out.println("消费了:" + wt); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }

结果:

生产了:WoTou:1 消费了:WoTou:1 生产了:WoTou:1 生产了:WoTou:2 消费了:WoTou:2 生产了:WoTou:2 消费了:WoTou:2 生产了:WoTou:3 生产了:WoTou:3 生产了:WoTou:4 生产了:WoTou:4 消费了:WoTou:4 生产了:WoTou:5 消费了:WoTou:5 生产了:WoTou:5 生产了:WoTou:6 消费了:WoTou:5 消费了:WoTou:6 生产了:WoTou:6 生产了:WoTou:7 消费了:WoTou:6 消费了:WoTou:7 生产了:WoTou:8 生产了:WoTou:9 消费了:WoTou:8
相关文章
相关标签/搜索