Java基础之《线程》

Java多线程机制
  线程的基本概念
    线程的简单理解:线程是一个程序里面不同的执行路径
    进程是一个静态的概念,main方法是进程的主线程

    线程和进程的区别
    1)每一个进程都有独立的代码和数据空间(进程上下文)。进程间的切换会有较大的开销
    2)线程可以看成是轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立
    的运行栈和程序计数器(PC),线程切换的开销小
    3)多进程:在操作系统中能同时运行多个任务(程序)
    4)多线程:在同一应用程序中有多个顺序流同时执行

    Java的线程是通过java.lang.Thread类来实现的,每一个Thread对象代表一个新的线程
    启动线程需要有两步:第一步创建一个自己的线程对象,然后new一个Thread对象,然后
    让它启动

    Java虚拟机启动时会有一个由主方法(public static void main() {})所定义的线程,主线程

    可以通过创建Thread的实例来创建新的线程,只要new一个Thread对象,一个新的线程就
    出现了

    每个线程都是通过某个特定的Thread对象所对应的方法run()来完成其操作的,方法run()
    称为线程体,在run()方法里面写什么语句,它就执行什么语句

    通过调用Thead类的start()方法来启动一个线程

  线程的创建和启动
    有两种方法创建新的线程
      第一种,定义线程类实现Runnable接口
      这个类的对象可以当作一个线程执行?
      当定义的类实现了Runnable接口,它就是一个线程类,用Thread对象来启动线程
      1)Thread myThread = new Thread(target) //target为Runnable接口类型
      2)Runnable中只有一个方法:
      public void run(); //用以定义线程运行体
      3)使用Runnable接口可以为多个线程提供共享的数据
      4)在实现Runnable接口的类的run方法定义中可以使用Thread的静态方法:
      public static Thread currentThread(); //获取当前线程的引用

      第二种,继承Thread类并重写其run()方法
      class MyThread extends Thread {
        public void run() {...}
      }
      然后生成该类的对象:
      MyThread myThread = new MyThread(...)

      线程启动必须调用Thread类的start()方法

      推荐只要能实现Runnable接口就不要从Thread类继承,实现接口更灵活,还能继承其它类和接口

/*
* TestThread1.java
*/
public class TestThread1 {
	public static void main(String args[]) {
		Runner1 r = new Runner1();
		Thread t = new Thread(r);
		t.start();  //启动一个新的线程
	
		for (int i=0; i<100; i++) {
			System.out.println("Main Thread:-----" + i);
		}
	}
}

class Runner1 implements Runnable {
	public void run() {
		for (int i=0; i<100; i++) {
			System.out.println("Runner1 :" + i);
		}
	}
}

线程的状态

  线程控制基本方法
    //判断线程是否还"活"着,即线程是否还未终止
    isAlive()

    //获得线程的优先级数值
    getPriority()

    //设置线程的优先级数值,优先级越高的线程获得CPU的时间越多
    setPriority()

    //将当前线程睡眠指定毫秒数
    Thread.sleep()
    sleep方法是Thread的静态方法,会抛InterruptedException异常
    在哪个线程里调用sleep方法,哪个线程就睡眠

    //调用某线程的该方法,将当前线程与该线程"合并",即等待该线程结束,再
    恢复当前线程的运行
    join()

    //让出CPU,当前线程进入就绪队列等待调度
    yield()

    //当前线程进入对象的wait pool
    wait()

    //唤醒对象的wait pool中的一个/所有等待线程
    notify() / notifyAll()

    sleep / join / yield 方法
    sleep方法
    可以调用Thread的静态方法:
      public static void sleep(long millis) throws InterruptedException
      使得当前线程休眠(暂时停止执行millis毫秒)
    由于是静态方法,sleep可以由类名直接调用:
      Thread.sleep(...)
    当sleep执行时,该线程被中断会抛出InterruptedException异常

/*
* TestInterrupt.java
*/

import java.util.*;

public class TestInterrupt {
	public static void main(String args[]) {
		MyThread thread = new MyThread();
		thread.start();
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {}
		thread.interrupt();
	}
}

class MyThread extends Thread {
	public void run() {
		while (true) {
			System.out.println("===" + new Date() + "===");
			try {
				sleep(1000);
			} catch (InterruptedException e) {
				return;
				}
		}
	}
}
    join方法
      合并某个线程
/*
* TestJoin.java
*/

public class TestJoin {
	public static void main(String args[]) {
		MyThread2 t1 = new MyThread2("abc");
		t1.start();
		try {
			t1.join();
		} catch (InterruptedException e) {}
		for(int i=0; i<=10; i++) {
			System.out.println("i am main thread");
		}
	}
}

class MyThread2 extends Thread {
	MyThread2(String s) {
		super(s);
	}
	
	public void run() {
		for(int i=0; i<=10; i++) {
			System.out.println("i am " + getName());
			try {
				sleep(1000);
			} catch (InterruptedException e) {
				return;
				}
		}
	}
}
    yield方法

      让出CPU,给其他线程执行的机会

/*
* TestYield.java
*/

public class TestYield {
	public static void main(String args[]) {
		MyThread3 t1 = new MyThread3("t1");
		MyThread3 t2 = new MyThread3("t2");
		t1.start();
		t2.start();
	}
}

class MyThread3 extends Thread {
	MyThread3(String s) {
		super(s);
	}
	
	public void run() {
		for(int i=1; i<=100; i++) {
			System.out.println(getName() + ": " + i);
			if (i%10==0)
			yield();
		}
	}
}
    如何让线程在死循环退出?
    1)stop()  //太粗暴,不推荐
    2)在线程内部使用sleep,捕捉InterruptedException异常,然后退出
    3)如下
    boolean flag = true;
    public void run() {
      while(flag) {
      ...
      }
    }
    只要调用thread.flag = false,run方法就结束了,线程退出

  线程的调度和优先级
    1)Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程
    线程调度器按照线程的优先级决定应调度哪个线程来执行
    2)线程的优先级用数字表示,范围从1到10,一个线程的缺省优先级是5
      Thread.MIN_PRIORITY = 1
      Thread.MAX_PRIORITY = 10
      Thread.NORM_PRIORITY = 5
    3)使用下述方法获得或设置线程对象的优先级
      int getPriority();
      void setPriority(int newPriority);

    Thread类定义了静态常量,表示优先级MAX_PRIORITY、MIN_PRIORITY、NORM_PRIORITY
    run方法一结束,线程就结束

  线程的状态控制
    Thread.currentThread().isAlive();  //拿到当前线程,判断是否活着

  线程同步
    对访问同一份资源的多个线程之间来进行协调,叫做线程同步

    解决办法之一:
      加锁(独占)
    两个线程不同步,问题出在一个线程在执行过程中,被另一个线程打断了
    (本来应该是原子性的,但是现在中间被打断了)

    在Java语言中,引入了对象互斥锁的概念,保证共享数据操作的完整性。
    每个对象都对应于一个可称为“互斥锁”的标记,这个标记保证在任一时刻,
    只能有一个线程访问该对象

    synchronized关键字
      关键字synchronized用来与对象的互斥锁联系。当某个对象用synchronized
      修饰时,表明该对象在任一时刻只能由一个线程访问
      这个关键字会锁定某一段代码,它的内部含义是当执行这段代码的过程之中,
      锁定当前对象。如果另一个人也想访问这个对象,只能等着锁释放

      1)锁定某一个东西
      synchronized (this) {  //锁定当前对象,互斥锁
      ...
      }
      大括号里,在一个线程执行语句过程当中,不会被另外一个线程打断
      2)更简便写法,在执行这个方法的过程中,锁定当前对象
      public synchronized void XXX() {  //在执行方法的过程中,锁定当前对象
      ...
      }

      注意:这里是锁定当前对象,即这个方法的所在对象,同一个对象里面其它加锁的

      方法就不能执行了

/*
* TestSync.java
*/
public class TestSync implements Runnable {
	Timer timer = new Timer();
	public static void main(String args[]) {
		TestSync test = new TestSync();
		Thread t1 = new Thread(test);
		Thread t2 = new Thread(test);
		t1.setName("t1");
		t2.setName("t2");
		t1.start();
		t2.start();
	}
	public void run() {
		timer.add(Thread.currentThread().getName());
	}
}

class Timer {
	private static int num = 0;
	//注意区分加synchronized和不加synchronized的区别
	public /*synchronized*/ void add(String name) {
		//synchronized (this) {
		num++;
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {}
		System.out.println(name+", 你是第"+num+"个使用timer的线程");
		//}
	}
}

    死锁
      原理:都需要等待对方释放手中的锁
      一个解决死锁:把锁的粒度加粗,锁定当前整个对象

/*
* TestDeadLock.java
*/
public class TestDeadLock implements Runnable {
	public int flag = 1;
	static Object o1 = new Object();
	static Object o2 = new Object();
	public void run() {
		System.out.println("flag" + flag);
		if (flag == 1) {
			synchronized(o1) {
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized(o2) {
					System.out.println("1");
				}
			}
		}
		if (flag == 0) {
			synchronized(o2) {
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					e.printStackTrace();
				}
				synchronized(o1) {
				System.out.println("0");
				}
			}
		}
	}

	public static void main(String args[]) {
		TestDeadLock td1 = new TestDeadLock();
		TestDeadLock td2 = new TestDeadLock();
		td1.flag = 1;
		td2.flag = 0;
		Thread t1 = new Thread(td1);
		Thread t2 = new Thread(td2);
		t1.start();
		t2.start();
	}
}
      一个类里加锁的方法,不影响其他线程访问其他方法
      一个资源能不能够正确的访问一致,必须把访问这个资源的所有方法都要考虑到
      只给一个方法加锁是不行的,必须把能访问这个资源的所有方法都加锁

    互斥:在某一个时间段,保证只有一个线程进入到这个方法体里面

    生产者消费者问题

    wait()  //当前线程阻塞,有人调用notify方法后就醒了,然后抢锁,谁抢到谁运行
    wait的时候不再拥有锁
   
    this.wait() 指当前正在访问这个对象方法的线程wait(线程先拿到锁,访问对象的方法)

    notify()  //叫醒一个现在wait在我这个对象上的线程
    notifyAll()  //叫醒在这个对象上wait的所有线程

    wait sleep区别
    wait时,别的线程可以访问锁定对象,锁不归我所有了
      调用wait方法的时候必须锁定该对象

    sleep时,睡着了也抱着那把锁,别的线程也不可以访问锁定对象

/*
* ProducerConsumer.java
*/
public class ProducerConsumer {
	public static void main(String args[]) {
		SyncStack s = new SyncStack();
		Producer p = new Producer(s);
		Consumer c = new Consumer(s);
		Thread t1 = new Thread(p);
		Thread t2 = new Thread(c);
		t1.start();
		t2.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[6];
	
	public synchronized void push(WoTou wt) {
		while (index == arrWT.length) {
			try {
				this.wait(); //这里的wait方法是Object类的
				} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.notify();
		arrWT[index] = wt;
		index++;
	}
	
	public synchronized WoTou pop() {
		while (index == 0) {
			try {
			this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.notify();
		index--;
		return arrWT[index];
	}
}
//生产者
class Producer implements Runnable {
	SyncStack ss = null;
	Producer(SyncStack ss) {
		this.ss = ss;
	}
	
	public void run() {
		for(int i=0; i<20; i++) {
			WoTou wt = new WoTou(i);
			ss.push(wt);
			System.out.println("生产了: " + wt);
			try {
				Thread.sleep((int)(Math.random() * 200)); //生产比消费快
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
//消费者
class Consumer implements Runnable {
	SyncStack ss = null;
	Consumer(SyncStack ss) {
		this.ss = ss;
	}
	
	public void run() {
		for(int i=0; i<20; i++) {
			WoTou wt = ss.pop();
			System.out.println("消费了: " + wt);
			try {
				Thread.sleep((int)(Math.random() * 1000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值