JAVA 多线程的简单使用

一个线程的生命周期

线程是一个动态执行的过程,它也有一个从产生到死亡的过程。

下图显示了一个线程完整的生命周期。

  • 新建状态:

    使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。

  • 就绪状态:

    当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。

  • 运行状态:

    如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

  • 阻塞状态:

    如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:

    • 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

    • 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。

    • 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

  • 死亡状态:

    一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。


线程例子:

1.实现Runnable接口:

public class MyRunnable implements Runnable{
    private String msg;
	@Override
	public void run() {
		for(int i=0;i<10;i++){
			System.out.println(Thread.currentThread().getName()+"\t"+this.msg);
		}
		
	}
	public MyRunnable(){
		super();
	}
	public MyRunnable(String msg){
		super();
		this.msg=msg;
	}
}


/**
 * 使用Runnable目的就是有共享内容 几个线程共同完成任务
 * @author WQY
 *匿名内部类在Runnable上的使用 
 *不能加参数 必须无参!如果线程只在一处使用,可以选择这种方式,但是可读性较差
 */
//
public class Test3 {


	public static void main(String[] args) {
        for(int i=0;i<10;i++){
        	System.out.println(Thread.currentThread().getName()+"但可寻所爱");
        }
		MyRunnable m1=new MyRunnable("山海不可平");
		//1.使用匿名内部类(很少使用)
		Thread t1=new Thread(new Runnable() {
			
			@Override
			public void run() {
				for(int i=0;i<10;i++){
		        	System.out.println(Thread.currentThread().getName()+"山海不可平");
		        }
				
			}
		},"所思隔云端");
		//2.没有使用匿名内部类,实现Runnable接口解决了单一继承限制
		Thread t2=new Thread(m1,"奈何凡肉身");
		t1.start();
		t2.start();
	}
}
2.继承Thread
public class MyThread extends Thread {
private String msg;

public MyThread() {
	super();
}

public MyThread(String name,String msg) {
	super(name);
	this.msg=msg;
}

@Override
public void run() {
	for(int i=0;i<10;i++){
		System.out.println(this.getName()+"\t"+this.msg);
	}
}
}
//主线程 默认名字main
public class Test2 {
           public static void main(String[] args) {
		//MyThread mt1=new MyThread("mt1","一望可相见");
		MyThread mt2=new MyThread("mt2","一步如重城");
		//使用匿名内部类来启动线程
		new Thread("mt1"){
			@Override
			public void run() {
				for(int i=0;i<10;i++){
					System.out.println(Thread.currentThread().getName()+"\t"+"一望可相见");
				}
			}
		}.start();
		//继承Thread建立线程 
		mt2.start();
		for(int i=0;i<10;i++){
			System.out.println(Thread.currentThread().getName()+"\t"+"所爱隔山海");
		}
	}
}
3.窗口卖票例子:

class TicketRunnable implements Runnable {
	private Integer count;
	public TicketRunnable(Integer count) {
		super();
		this.count = count;
	}
	public TicketRunnable() {
		super();
	}
	@Override
	public void run() {
         while(true){
        	 //锁池状态
        	 //同步块  (同步锁)解决脏数据问题。保证一个线程在运行 运行结束别的线程才能进去
        	 synchronized(this){//this是表示当前对象 共享的count 。synchronized目的就是把共享的数据锁住
        	 if(count<=0){
        		 System.out.println(Thread.currentThread().getName()+"exit   窗口:\t"+this.count);
        		 break;
        	 }
        	 try {
        		 //sleep()控制运行时间
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
        	 int i=count--;
        	 //count-- 表达式的值后改变 先给表达式赋值在改count的值
        	 System.out.println(Thread.currentThread().getName()+"窗口:\t"+i);
            }
         }
	}
	


}
public class Ticket {

	public static void main(String[] args) {

		MyThreadDemo m=new MyThreadDemo();
		Thread t1=new Thread(m,"窗口1");
		Thread t2=new Thread(m,"窗口2");
		Thread t3=new Thread(m,"窗口3");
		Thread t4=new Thread(m,"窗口4");
		t1.start();
		t2.start();
		t3.start();	
		t4.start();
		}

}

4.经典的生产者消费者模型:

要保证 仓库是一个实例

public class Warehouse {
	private List<String> goods=new ArrayList<String>();
	public Warehouse() {
		super();
	}
	public Warehouse(List<String> goods) {
		super();
		this.goods = goods;
	}
	public List<String> getGoods() {
		return goods;
	}
	public void setGoods(List<String> goods) {
		this.goods = goods;
	}
	//同步方法   使用wait() 和notifyAll() 必须在同步里
	public synchronized void customer(){
		if(this.goods.isEmpty()){
			System.out.println("仓库空了");
			try {
				this.wait();//进入等待状态 退出CPU 方法结束,不向下运行
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		/*try {
		Thread.sleep(100);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}*/
		System.out.println("消费者消费了"+this.goods.remove(0));

		this.notifyAll();
	}
	public synchronized void produce(String name){
		if(!this.goods.isEmpty()){
			System.out.println("仓库满了");
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		/*try {
		Thread.sleep(100);
	} catch (InterruptedException e) {
		e.printStackTrace();
	}*/
		this.goods.add(name);
		System.out.println("生产者生产了" + name);
		this.notifyAll();
	}
}


//生产者
public class Pro implements Runnable {
	private Warehouse w; 
	public Pro() {
		super();
	}
	public Pro(Warehouse w) {
		super();
		this.w = w;
	}
	@Override
	public void run() {
		for(int i=0;i<1000;i++){
			this.w.produce("产品"+(i+1));
		}
	}
}

//消费者

public class Cus implements Runnable {
	private Warehouse w; 
	public Cus() {
		super();
	}
	public Cus(Warehouse w) {
		super();
		this.w = w;
	}
	@Override
	public void run() {
		for(int i=0;i<1000;i++){
			this.w.customer();
		}
	}
}


//测试
public class TestCP {
	public static void main(String[] args) {
		Warehouse w=new Warehouse();
		Pro p=new Pro(w);
		Thread tp=new Thread(p);
      
		Cus c=new Cus(w);
		Thread tc=new Thread(c);
		
		tp.start();
		tc.start();
	}
}
线程的一些问题:
线程的sleep()方法和yield()方法有什么区别? 
答: 
① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会; 
② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态; 
③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常; 
④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。

当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B? 
答:不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。

请说出与线程同步以及线程调度相关的方法。 
答: 
- wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁; 
- sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常; 
- notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关; 
- notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;
synchronized关键字的用法? 
答:synchronized关键字可以将对象或者方法标记为同步,以实现对对象和方法的互斥访问,可以用synchronized(对象) { … }定义同步代码块,或者在声明方法时将synchronized作为方法的修饰符。在第60题的例子中已经展示了synchronized关键字的用法

线程的优先级

每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。

Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。

默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。

具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。


创建一个线程

Java 提供了三种创建线程的方法:

  • 通过实现 Runnable 接口;
  • 通过继承 Thread 类本身;
  • 通过 Callable 和 Future 创建线程。

通过实现 Runnable 接口来创建线程

创建一个线程,最简单的方法是创建一个实现 Runnable 接口的类。

为了实现 Runnable,一个类只需要执行一个方法调用 run(),声明如下:

public void run ( )

你可以重写该方法,重要的是理解的 run() 可以调用其他方法,使用其他类,并声明变量,就像主线程一样。

在创建一个实现 Runnable 接口的类之后,你可以在类中实例化一个线程对象。

Thread 定义了几个构造方法,下面的这个是我们经常使用的:

Thread ( Runnable threadOb , String threadName ) ;

这里,threadOb 是一个实现 Runnable 接口的类的实例,并且 threadName 指定新线程的名字。

新线程创建之后,你调用它的 start() 方法它才会运行。

void start ( ) ;


停止线程的几种方法区别
sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态,请参考第66题中的线程状态转换图)。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值