多线程

来源:互联网 发布:淘宝很出名的钢笔店铺 编辑:程序博客网 时间:2024/05/17 03:17


0. 线程概述:

  • 进程: 是一个正在执行中的程序. 每一个进程执行都有一个执行顺序. 该顺序是一个执行路径, 或者叫一个控制单元. 
  • 线程: 就是进程中的一个独立的控制单元. 线程在控制着进程的执行. 
  • 一个进程中至少有一个线程. 


1. 创建线程的方法:

  • 继承Thread类: 子类覆盖父类中的run方法, 将线程运行的代码存放在run中. 建立子类对象的同时线程也被创建. 通过调用start方法开启线程(start的两个作用: 启动线程, 调用run方法). 
  • 实现Runnable接口: 子类覆盖接口中的run方法. 通过Thread类创建线程, 并实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数. Thread类对象调用start方法开启线程. 
  • * 继承方式和实现方式有什么区别?
    实现方式好处: 避免了单继承的局限性. 在定义线程时, 建议使用实现方式. (还可以让多个线程处理同一个问题). 


2. 用Runnable接口例子:

/* * 需求:简单的卖票程序.  * 多个窗口同时卖票.  */class Ticket implements Runnable{private int tick = 100;public void run(){while(true){if(tick>0)System.out.println(Thread.currentThread().getName() + " sale : " + tick--);}}}public class TicketDemo {public static void main(String[] args) {Ticket t = new Ticket();Thread t1 = new Thread(t);Thread t2 = new Thread(t);Thread t3 = new Thread(t);Thread t4 = new Thread(t);t1.start();t2.start();t3.start();t4.start();}}

3. 线程的四种状态:

 新建(New)就绪(Runnable)→运行(Running)→阻塞(Blocked)→死亡(Dead)



4. 同步代码块

// 格式synchronized(对象){需要被同步的代码}

  • 对象如同所锁. 持有锁的线程可以再同步中执行, 没有持有锁的线程即使获取cpu的执行权, 也进不去, 因为没有获取锁.
  • 好处: 解决了多线程的安全问题. 弊端: 多线程需要判断锁, 较消耗资源. 


5. 同步函数

格式: 在函数上加上synchronized修饰符即可. 

* 同步函数用的锁是this. 

* 静态同步函数用的锁是该方法所在类的"类名.class"字节码文件对象(是Class类的实例). 


6. 死锁

定义: 死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁. 

例程:

class Test implements Runnable{private boolean flag;Test(boolean flag){this.flag = flag;}public void run(){if(flag){(true){synchronized(MyLock.locka){System.out.println("if locka");synchronized(MyLock.lockb){System.out.println("if lockb");}}}}else{while(true){synchronized(MyLock.lockb){System.out.println("else lockb");synchronized(MyLock.locka){System.out.println("else locka");}}}}}}// 提供锁class MyLock{static Object locka = new Object();static Object lockb = new Object();}// 测试死锁public class DeadLockTest {public static void main(String[] args) {Thread t1 = new Thread(new Test(true));Thread t2 = new Thread(new Test(false));t1.start();t2.start();}}


7.  * 线程间通信

wait(): 等待. 
notify(): 唤醒. 唤醒随机的一个等待线程. 
notifyAll(): 唤醒所有. 

  • 这三个方法都是用在同步中, 因为要对持有监视器(锁)的线程操作. 所以要使用再同步中, 因为只有同步才具有锁. 
  • 为什么这三个方法不是定义在Thread类中, 而是在Object类中? 
    答: 因为这些方法在操作同步中线程时, 都必须要表示它们所操作线程持有的锁, 一个锁上的被等待线程, 只可以被同一个锁上的notify唤醒, 不可以被不同锁中的线程唤醒. 
    也就是说, 等待和唤醒必须是同一个锁. 
    而锁可以是任意对象, 所以可以被任意对象调用, 定义在Object类中. 
例子: 
// Res的对象作为被操作的资源和锁class Res{String name;String sex;boolean flag = false;}class Input implements Runnable{private Res r;Input(Res r){this.r = r;}public void run(){int x = 0;while(true){synchronized(r){if(r.flag)// 调用锁r的wait()方法try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}if(x==0){r.name = "mike";r.sex = "man";}else{r.name = "丽丽";r.sex = "女女女女";}x = (x+1)%2;r.flag = true;// 调用锁r的notigy()方法r.notify();}}}}class Output implements Runnable{private Res r;Output(Res r){this.r = r;}public void run(){while(true){synchronized(r){if(!r.flag)// 调用锁r的wait()方法try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(r.name + "...." + r.sex);r.flag = false;// 调用锁r的notigy()方法r.notify();}}}}// 测试public class InputOutputDemo {public static void main(String[] args) {Res r = new Res();Input in = new Input(r);Output out = new Output(r);Thread t1 = new Thread(in);Thread t2 = new Thread(out);t1.start();t2.start();}}
上述代码很乱, 优化后是酱紫:
// Res的对象作为被操作的资源class Res{private String name;private String sex;boolean flag = false;public synchronized void set(String name, String sex){if(flag)try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}this.name = name;this.sex = sex;flag = true;this.notify();}public synchronized void out(){if(!flag)try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name+"......."+sex);flag = false;this.notify();}}class Input implements Runnable{private Res r;Input(Res r){this.r = r;}public void run(){int x = 0;while(true){if(x==0)r.set("mike", "man");elser.set("丽丽", "女女女女");x = (x+1)%2;}}}class Output implements Runnable{private Res r;Output(Res r){this.r = r;}public void run(){while(true){r.out();}}}// 测试public class InputOutputDemo {public static void main(String[] args) {Res r = new Res();new Thread(new Input(r)).start();new Thread(new Output(r)).start();}}

8. 停止线程

因为开启多线程运行, 运行代码通常是循环结构, 只要控制住循环, 就可以让run方法结束, 也就是线程结束. 
就像这样: 
class StopThread implements Runnable{private boolean flag = true;public void run(){while(flag){System.out.println(Thread.currentThread().getName()+".....run");}}public void changeFlag(){flag = false;}}public class StopThreadDemo {public static void main(String[] args) {StopThread st = new StopThread();Thread t1 = new Thread(st);Thread t2 = new Thread(st);t1.start();t2.start();int num = 0;while(true){if(num++ == 60){st.changeFlag();break;}System.out.println(Thread.currentThread().getName()+"....."+ num);}}}
* Thread类中提供了interrupt()方法, 用于中断sleep()、wait()和join()等. 


9. join()方法

作用: 当A线程执行到了B线程的.join()方法时, A就会等待. 等B线程都执行完, A才会执行. join可以用来临时加入线程执行. 

例子: 

class Demo1 implements Runnable{public void run(){for(int x=0; x<70; x++){System.out.println(Thread.currentThread().getName()+"..."+x);}}}public class Test2 {public static void main(String[] args) throws InterruptedException { Demo1 d = new Demo1();Thread t1 = new Thread(d);Thread t2 = new Thread(d);t1.start();//t1.join();//t1要申请加入. 就是t1要加进来,抢走申请cpu执行权. 主线程运行到这句, 然后等待t1结束才能继续执行. t2.start();t1.join();//主线程释放执行权给t1. 执行这句后, t1和t2交替执行, 直到t1执行完后主线程又拿到执行权. for(int x=0; x<80; x++){System.out.println("main....." + x);}System.out.println("over");}}

10. yield()方法

作用: 暂停正在执行的线程对象, 并执行其他线程. 

  • yield将线程转为就绪状态. 只是让线程暂停一下, 如果程序中不存在优先级比该线程高或相同的线程, 则此让步的线程继续运行; 如果有, 另算 (给优先级更高的线程让步). 
  • sleep() 方法声明抛出InterruptedException异常, yield() 方法没抛出异常. 
例子: 
public class YieldTest extends Thread{public YieldTest(String name){super(name);}public void run(){for (int i = 0; i < 50; i++){System.out.println(getName() + " " + i);if (i == 20){Thread.yield();}}}public static void main(String[] args) throws Exception{YieldTest yt1 = new YieldTest("HIGH");//yt1.setPriority(Thread.MAX_PRIORITY);yt1.start();YieldTest yt2 = new YieldTest("LOW");//yt2.setPriority(Thread.MIN_PRIORITY);yt2.start();}}