黑马程序员----多线程

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


进程与线程

进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。

线程:就是进程中一个独立的控制单元。线程在控制着进程的执行。

一个进程中至少有一个线程。

多线程的运行可以看成是相互争夺cpu的执行权,这就是多线程的一个特征:随机性。谁抢到谁执行,至于执行多长,cpu说的算。

Java VM启动的时候会有一个进程java.exe。

该进程中至少一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中,该线程称为主线程。

扩展:其实更细节说明java jvm启动不止一个线程,还有负责垃圾回收机制的线程。

如何在自定义的代码中,自定义一个线程呢?

创建线程的两种方式:

继承Thread类

->步骤:

    |--定义类继承Thread。

    |--复写Thread类中的run方法。目的:将自定义代码存储在run方法,让线程运行。

    |--调用线程的start方法。 该方法两个作用:创建线程,调用run方法。

class Demo extends Thread{
	public void run(){
		for(int i = 0; i < 60; i++){
			System.out.println("demo run----" + i);
		}
	}
}
public class ThreadDemo {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Demo d = new Demo();
		
		d.start();	//开启线程并执行该线程的run方法
		//d.run();	//仅仅是对象调用方法,而线程创建了,并没有运行
		
		for(int i = 0; i < 60; i++){
			System.out.println("hello world----" + i);
		}
	}

}

实现Runnable接口

->步骤

    |--定义类实现接口Runnable接口。

    |--覆盖Runnable接口中的run方法。 将线程要运行的代码存放在该run方法中。

    |--通过Thread类建立线程对象

    |--将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。

    |--调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

class RunnableDemo implements Runnable{

	private String name;
	public RunnableDemo(String name){
		this.name = name;
	}
	public void run() {
		// TODO Auto-generated method stub
		for(int i = 0; i < 5; i++){
			System.out.println(name + ":" + i);
		}
	}
}

public class TestRunnable {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		RunnableDemo r1 = new RunnableDemo("tom");
		RunnableDemo r2 = new RunnableDemo("jim");
		
		Thread t1 = new Thread(r1);
		Thread t2 = new Thread(r2);
		
		t1.start();
		t2.start();
	}
}

为什么要将Runnable接口的子类对象传递给Thread的构造函数?

自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去执行指定对象的run方法,就必须明确该run方法所属对象。


为什么要覆盖run方法?

Thread类用于描述线程。该类定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法。也就是说Thread类中的run方法,用于存储线程要运行的代码。

线程都有自己默认的名字,通过getName获取线程名称。


实现方式和继承方式有什么区别?

实现方式好处:避免了单继承的局限性。在定义线程时,建立使用实现方式。

两种方式区别

继承Thread:线程代码存放在Thread子类run方法中。

实现Runnable:线程代码存放在接口的子类的run方法中。

多线程的运行方式导致其容易出现安全问题。

问题产生原因

         当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。

解决方法

        对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不能参与执行。

 //java中通过:同步代码块
 synchronized(对象)
 {
 	需要同步的代码
 }
 
同步的前提:

1. 必须要有两个或者两个以上的线程。

2. 必须是多个线程使用同一个锁。

3. 必须保证同步中只能有一次线程执行。

优缺点

解决了多线程的安全问题;多个线程需要判断锁,较为消耗资源。


JDK1.5中提供了多线程升级解决方案。

将同步synchronized替换成现实lock操作,将object中的wait, notifyAll替换成立condition对象,该对象可以lock锁进行获取。

如何停止线程?

只有一种方式,run方法结束。开启多线程运行,运行代码通常都是循环结构,只要控制循环,就可以让run方法结束,也就是线程结束。

特殊情况:

当线程处于冻结状态,就不会读取到标记,那么线程就不会结束。

当没有指定的方式让冻结的线程恢复到运行状态,这时需要对冻结进行清除,强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。

Thread类提供该方法interrupt();

join:

当A线程执行到了B线程的join()方法时,A就会等待,等B线程执行完,A才会执行。join可以用来临时假死线程执行。


生产者消费者模型(同步代码块实现线程同步):

/*
 * 此例只适合:一个生产方和一个消费方的情况,多生产及多消费方,此例仍然不完善;
 * 
 * 注意:
 * 
 * 1.在BaoZiPu类中的两个方法中使用的wait()和notify和notifyAll()这些方法都是Object的方法;
 * 	 因为任何对象都有可能被多线程访问,也就都有权利让当前正在访问的线程等待。也有权唤起正在等待的其它线程。
 *   所以这些方法被定义在Object中。
 * 2.调用wait()和notify()及notifyAll()时,一定要在"同步代码块"或"同步方法"中;
 *   
 */
import java.util.ArrayList;
import java.util.ArrayList;
public class Demo {
	public static void main(String[] args) {
		BaoZiPu bzp = new BaoZiPu();
		//生产方线程
		SetBaoZiThread setThread = new SetBaoZiThread(bzp);
		//消费方线程
		GetBaoZiThread getThread = new GetBaoZiThread(bzp);
		
		setThread.start();
		getThread.start();
	}
}


class BaoZiPu {
	private ArrayList<String> bzList = new ArrayList<>();
	
	//增加包子的方法-->由生产方调用
	//此方法可能会被多线程调用,可能会产生并发问题,所以,使用synchronized
	public synchronized  void setBaoZi(String s){
		bzList.add(s);
		//设置完包子后,唤醒其他等待的线程
		this.notify();
	}
	
	//获取包子-->由消费方调用
	public synchronized  String getBaoZi(){
		if(bzList.size() == 0){
			//让当前访问的线程等待
			try {
				System.out.println("请消费者等待......");
				this.wait();
				System.out.println("来包子了,请消费者继续取包子......");
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//取第一个包子
		String s = bzList.get(0);
		//删除第一个包子
		bzList.remove(0);
		//将包子返回
		return s;
		
	}
	
}
class GetBaoZiThread extends Thread{
	private BaoZiPu bzp = null;

	public GetBaoZiThread(BaoZiPu bzp) {
		super();
		this.bzp = bzp;
	}
	
	@Override
	public void run() {
		while(true){
			String s= this.bzp.getBaoZi();
			System.out.println("我获取到:" + s);
			
		}
	}
}
class SetBaoZiThread extends Thread {
	private BaoZiPu bzp = null;

	public SetBaoZiThread(BaoZiPu bzp) {
		super();
		this.bzp = bzp;
	}
	
	@Override
	public void run() {
		while(true){
			//每隔2毫秒生产一个包子
			//为了消费方能够获取到null,使测试效果更明显;
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			//生产一个包子
			this.bzp.setBaoZi("包子");
		}
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值