线程,多线程,线程池

一,Thread类
1.1,并发于并行
并发:指两个或者多个事件在同一时刻发生(一起发射一个弓箭)
并行:指两个或多个事件在同一个时间段内发生。(这一个小时我一边打游戏,一边看电视)

多核处理器:每个CPU并发执行一个任务,多任务并发执行,并行处理程序(了解)

1.2,线程与进程
进程:是指一个内存中运行的应用程序。(运行中的软件)
线程:进程内部的一个独立执行单元。(软件中的小模块)一个进程并发运行多个线程

1.3,线程和进程的区别
进程:有独立的内存空间,进程中的数据存放空间是独立的,至少有一个线程。
线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。

1.4线程的调度
JVM采用的是抢占式调度,造成多线程执行结果的随机性

2.1,创建线程类
第一种方式:继承Thread类来创建并启动多线程。
1.定义Thread子类,并重写该类的run()方法,run方法也叫线程执行体。
2.创建Thread子类的实例,即创建了线程的对象
3.调用线程对象的start()方法来启动该线程

Thread类中的具体方法(课外辅导时间来啦!)
嵌套类
static class Thread.State() 线程状态
static interface Thread.UncaughtExceptionHandler 未捕获异常时候调用的接口

构造方法
Thread() 分配新的 Thread 对象。
Thread(Runnable target, String name) 分配新的 Thread 对象。

方法摘要
static int activeCount() 返回当前线程的线程组中活动线程的数目。
static Thread currentThread() 返回对当前正在执行的线程对象的引用。
String getName() 返回该线程的名称。
Thread.State getState() 返回该线程的状态。
void interrupt() 中断线程。
void join() 等待该线程终止。
void run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响
static void yield() 暂停当前正在执行的线程对象,并执行其他线程。

二,线程
1.1多线程原理
每一个线程执行的时候都有一个属于自己的栈内空间,来执行自己的线程。
**start()**方法开启一个新的栈内空间,开启一个新的线程

1.2创建线程方式二
采用java.lang.Runnable也是非常常见的一种,我们只需要重写run方法即可。

步骤如下:
1.定义Runnable接口的实现类,并重写接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
2.创建
Runnable实现类的实例
,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
3.调用该线程对象**start()**方法来启动线程。

多线程实现类测试方法

public class Demo {
    public static void main(String[] args) {
        MyRunable mr = new MyRunable();
        Thread t = new Thread(mr,"小强");
        t.start();
        for(int i = 0;i < 100;i++){
            System.out.println("旺财" + i);
        }
    }
}

MyRunnbale多线程实现类接口

public class MyRunable implements Runnable {
    @Override
    public void run() {
        for(int i = 0; i < 100; i++){
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
}

通过实现Runnable接口,使得该类有多线程的特征,run()方法是多线程的一个执行目标,所有的多线程都在run方法里面,Thread类实际上也是Runnable接口的类

接口 Runnable中只有一个方法
那就是void run() 使用实现接口 Runnable 的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的 run 方法。

实际上所有的多线程方法都是通过Threadstart()方法来运行的,不管吧最终实现Runnable还是继承了Thread都是通过Thread对象的API来控制的。

Runnbale实现类里包含的run()方法仅作为线程执行体,而实际的对象依然是Thread实例,只是Thread线程负责执行target的**run()**方法

1.2Thread和Runnbale的区别
如果一个类继承Thread,则不适合资源的共享,但如果实现了Runnbale接口,则很容易实现资源共享。

如果使用Runnbale接口来实现多线程,不同的线程之间可以进行资源的共享。比如下面是实现了Runnbale的多线程

Thread t1 = new Thread(MyRunnable);t1.setName("售票窗口1");
Thread t2 = new Thread(MyRunnable);t2.setName("售票窗口2");
Thread t2 = new Thread(MyRunnable);t2.setName("售票窗口3");
t1.start();
t2.start();
t3.start();

原本线程如果是继承了Thread 类的话
代码执行效果如下:
继承了Thread的多线程
执行了多线程之间的资源共享:
执行效果如下:
实现了Runnable接口的多线程
我们根本目的其实就是为了卖出5张票通过三个窗口来进行卖票。

实现Runnable接口比继承Thread类所具有的优势4点:
1.适合多个相同线程的程序代码的线程去共享同一个资源(我上面已经描述,三个售票窗口卖出5张票)
2.可以避免java单继承的局限性(继承了Thread类以后就无法继承其他类,但是实现了Runnbale接口以后还能实现其他接口,继承其他类)
3.增加程序健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立
4.线程池只能放入实现Runnbale或Callable类线程,不能直接放入继承Thread的类
(PS:联想记忆法)
(我健身比我继承遗产多了哪些好处,我的身体不会只是我的身体,可以被多个女人分享,我也不会只是局限于继承遗产的富二代,我身体更加健壮,我所知道的知识含量也会被共享,钱和知识分开独立,健身房里面只欢迎跑步的人,不欢迎继承遗产的富二代)

1.3匿名内部类方式实现线程的创建

Runnbale r = new Runnable(){
	public void run(){
		for(int i = 0; i < 20; i++){
			   System.out.println("张宇:"+i);
		}
	};
	new Thread(r).start();
	for(int i = 0; i < 20; i++){
		System.out.println("费玉清:"+i);
	}
 }
}

二,线程安全
2.1线程安全
如果多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果一样的,而且其他的变量值也和预期的一样,就是线程安全的。(我挖id我快扫查出来的QQ号,和我手工单次查出来的QQ号是一样,那么我就说这个是安全的QQ号)

注意:
1.不代表所有的多线程都是线程不安全的
2.资源共享并不代表线程安全。
3.线程安全问题都是由全局变量及静态变量引起的,若每个线程中对全局变量在,静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般需要考虑线程同步,否则的话就有可能影响线程安全。

ps:(这是由全局的QQ和死了的静态QQ引起的,快扫如果只用来扫QQ,不写在界面上,那么快扫永远不会奔溃,这个快扫就是线程安全的,但是快扫一但在界面上显示了扫描出来的QQ,那么快扫很有可能就会奔溃,一般要加入同步按钮,导致快扫的线程不安全。)

2.2同步代码块
同步代码块:synchronized可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥的访问。

(小葵花妈妈课堂中的互斥的访问)互斥的访问:进程互斥:
两个或两个以上的进程,不能同时进入关于同一组共享变量的临界区域,否则可能发生与时间有关的错误,这种现象被称作进程互斥· 也就是说,一个进程正在访问临界资源,另一个要访问该资源的进程必须等待。

2.3同步方法
同步方法:使用synchronized修饰的方法就叫做同步方法,保证A线程在访问临时资源的时候B线程绝不会访问,B线程只能等待。

同步锁:
对于非static方法,同步锁就是this。
对于static方法,同步锁就是当前方法所在类的字节码文件。(类名.class)

synchronized 定义在方法内的:

public class Ticket implements Runnable{
	private int ticket = 100;
	
	Object lock = new Object();
/*
 * 执行卖票操作
 */
@Override
public void run() {
	//每个窗口卖票的操作 
	//窗口 永远开启 
	while(true){
		synchronized (lock) {
			if(ticket>0){//有票 可以卖
				//出票操作
				//使用sleep模拟一下出票时间 
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//获取当前线程对象的名字 
				String name = Thread.currentThread().getName();
				System.out.println(name+"正在卖:"+ticket--);
			}
		}
	}
}
}

synchronized 修饰在方法上的:

public class Ticket implements Runnable{
	private int ticket = 100;
	/*
	 * 执行卖票操作
	 */
	@Override
	public void run() {
		//每个窗口卖票的操作 
		//窗口 永远开启 
		while(true){
			sellTicket();
		}
	}
	
/*
 * 锁对象 是 谁调用这个方法 就是谁 
 *   隐含 锁对象 就是  this
 *    
 */
public synchronized void sellTicket(){
    if(ticket>0){//有票 可以卖	
        //出票操作
        //使用sleep模拟一下出票时间 
        try {
          	Thread.sleep(100);
        } catch (InterruptedException e) {
          	// TODO Auto-generated catch block
          	e.printStackTrace();
        }
        //获取当前线程对象的名字 
        String name = Thread.currentThread().getName();
        System.out.println(name+"正在卖:"+ticket--);
    }
	}
}

2.4Lock锁
lock提供了一个功能更加强大的锁机制,比synchronized更好用。
Lock锁也称同步锁,加锁与释放锁方法化了,如下:

- public void lock() `:加同步锁。
- public void unlock():释放同步锁。

但是我们更加倾向于使用synchronized ,因为synchronized 可以自动释放同步锁

public class Ticket implements Runnable{
	private int ticket = 100;


Lock lock = new ReentrantLock();
/*
 * 执行卖票操作
 */
@Override
public void run() {
	//每个窗口卖票的操作 
	//窗口 永远开启 
	while(true){
		lock.lock();
		if(ticket>0){//有票 可以卖
			//出票操作 
			//使用sleep模拟一下出票时间 
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//获取当前线程对象的名字 
			String name = Thread.currentThread().getName();
			System.out.println(name+"正在卖:"+ticket--);
		}
		lock.unlock();
	}
}
}

三,线程状态
3.1线程状态概述
在API中java.lang.Thread.State这个枚举中给出了六种线程状态:
NEW(新建) :线程刚创建,还未启动,还没调用start()方法。
Runnable (可运行):线程可以在java模拟机中运行的状态
Waiting(无限等待):线程等待另一个线程唤醒,进入这个状态后是不能自动唤醒的
Timed(计时等待):倒计时唤醒
Terminated(被终止):非正常退出导致线程死亡
Blocked(阻塞状态):如果一个对象试图获取对象锁,但是对象锁被其他线程持有,那么这个线程处于阻塞状态。

3.2Timed Waiting(计时等待):
1.进入TIMED_WAITING状态的一种常见情形是调用sleep方法,单独的线程也可以调用,不一定非要由协作关系。
2.为了让其他线程有机会执行,可以将Thread.sleep放入**run()**之内,保证线程执行过程中会睡眠
3.sleep与锁无关,线程睡眠到期自动苏醒,并返回到Runnable(可运行)状态。

新创建
new
|
可运行——>sleep带参(时间) 计时等待(timed waiting)
run <—— 参数时间到达
|
被终止
Terminated

3.3Blocked(锁阻塞)
Blocked状态在API中的介绍为:一个正在阻塞等待一个监视器锁(锁对象)的线程处于这一个状态。

3.4Waiting(无限等待)
Waiting状态在API中介绍为:一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一个状态。
那么我们之前遇到过这种状态吗?答案是并没有,

通过上述案例我们会发现,一个调用了某个对象的Object.wait方法的线程会等待另一个线程调用此对象的Object.notify()方法或Object.notifyAll()方法

其实waiting并不是一个线程的操作(同事们之间的竞争关系,但是大多数情况下他们还是合作关系)

当多个线程协作时,A,B,A进入等待,B获得同步锁,B调用notifyA唤醒进入Runnable(可运行)状态。

四,等待唤醒机制
4.1线程间的通信
概念:多个线程在处理同一个资源,但是处理的动作却不相同。
这就是协调调度,不同于抢占式调度,协调调度让线程间更加有规律的执行一件事情。

4.2等待唤醒机制
什么是等待唤醒机制
这是多线程之间的一种协作机制

调用wait和notify方法需要注意的细节:
1.wait方法和notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象wait方法调用后的线程。
2.wait方法与notify方法是属于Object类的方法。因为:锁对象可以是任意对象,而任意对象所属的类都继承了Object类。
**3.wait方法与notify方法必须要在同步代码块或者是同步函数中使用。**因为:必须要通过锁对象调用这两个方法。同步代码块(被synchronized 修饰的代码块是同步代码块,同步函数:就是同步方法代码块

五,线程池
5.1线程池思想概述
思想概述:执行完一个线程不进行销毁,等待继续执行其他任务。
概述:就是一个容纳多个线程的容器,其中线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。

工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务:
任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。
任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完成后的收尾工作,任务的执行状态等。
线程池管理器(ThreadPool):用于创建并管理线程池,包括创建线程池,销毁线程池,添加新任务。

(重点)多线程数据传递
9.1、通过构造方法传递数据

package mythread; 
public class MyThread1 extends Thread 
{ 
private String name; 
public MyThread1(String name) 
{ 
this.name = name; 
} 
public void run() 
{ 
System.out.println("hello " + name); 
} 
public static void main(String[] args) 
{ 
Thread thread = new MyThread1("world"); 
thread.start(); 
} 
} 

9.2、通过变量和方法传递数据

package mythread; 
public class MyThread2 implements Runnable 
{ 
private String name; 
public void setName(String name) 
{ 
this.name = name; 
} 
public void run() 
{ 
System.out.println("hello " + name); 
} 
public static void main(String[] args) 
{ 
MyThread2 myThread = new MyThread2(); 
myThread.setName("world"); 
Thread thread = new Thread(myThread); 
thread.start(); 
} 
} 

三、通过回调函数传递数据

package mythread; 
class Data 
{ 
	 	public int value = 0; 
} 
class Work 
{ 
		public void process(Data data, Integer numbers) 
{ 
for (int n : numbers) 
{ 
		data.value += n; 
} 
} 
} 
public class MyThread3 extends Thread 
{ 
		private Work work; 
public MyThread3(Work work) 
{ 
		this.work = work; 
} 
public void run() 
{ 
		java.util.Random random = new java.util.Random(); 
		Data data = new Data(); 
		int n1 = random.nextInt(1000); 
	    int n2 = random.nextInt(2000); 
		int n3 = random.nextInt(3000); 
		work.process(data, n1, n2, n3); // 使用回调函数 
		System.out.println(String.valueOf(n1) + "+" + String.valueOf(n2) + "+" 
		+ String.valueOf(n3) + "=" + data.value); 
} 
public static void main(String[] args) 
{ 
		Thread thread = new MyThread3(new Work()); 
		thread.start(); 
} 
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值