多线程

 程序,进程,线程,多进程,多线程 

1、程序:可以实现多个功能的代码体,也叫软件

2、进程:是一个实体,有自己的地址空间站,如:文本区域,数据区域,堆栈

        是一个运行中的程序:cpu赋予生命值时,就是进程

3、线程:是进程中的一个任务,是一个顺序执行流,有自己独立的堆栈,与其它线程共享进程中的地址空间

4、多进程:也既是可以打开运行多个软件,就像同时可以运行QQ与网易云音乐,这两个软件就是两个进程

5、多线程:一个线程中的多个任务,可以同时进行多个顺序执行流,它的好处是:

        <1>提高cpu的资源利用率

        <2>可以共享同一个资源(静态资源,同一个对象实例)

线程的状态图解

(1)新建状态:新建一个线程对象,设置好需要执行的任务

(2)就绪状态:调用线程的start方法,进入准备状态,等待cpu分配时间片段

(3)运行状态:当cpu将时间片段给了线程对象后,线程开始执行任务

(4)阻塞状态:正在运行中的线程由于某种原因,放弃了cpu的使用权,即线程放弃了时间片段,进入阻塞状态

    阻塞分为3种:

        <1>等待阻塞:运行中的线程调用wait()方法,jvm将此线程放入等待池中

        <2>同步阻塞:运行中的线程想要获取同步的锁对象时,如果锁对象被其他的线程占用,jvm将此线程放入锁池中

        <3>其他阻塞:当线程中执行到阻塞方法或者是thread.slepp()或者是其他线程join时,该线程进入阻塞状态

(5)当线程执行完任务后,表示结束,线程停止

     

并发原理:

         cpu在一个时间片段里只能做一件事。微观上,cpu是将时间片段分成很多个小的时间片段,尽可能的将时间片段分配给多个线程,所以针对于某一个线程来说是走走停停,断断续续的,但是在宏观上,我们人类是感觉不出来,看似这些线程是同时发生的,这就是并发原理。

线程创建的三种方式:

(1)继承Thread类,重写run方法,调用start方法,启动线程,进入就绪状态

(2)实现Runnable类,重写run方法,传给Thread对象

(3)创建Callable子类对象,重写call方法,传给FutureTask对象,FutureTask对象传给Thread对象,调用start方法,启动线程。

方法1:MyTask类继承Thread类,重写run()方法

public class ThreadDemo01 {
	public static void main(String[] args) {
		//创建线程对象,启动线程
		Thread t1 = new MyTask();
		Thread t2 = new MyTask();
		t1.start();
		t2.start();	
		System.out.println("main方法over");
	}
}
/*方法1:通过继承Thread,完成线程的定义*/
class MyTask extends Thread{

	//重写run方法
	@Override
	public void run() {
		for (int i = 0; i < 100000; i++) {
			System.out.println(this.getName()+":1");
		}
	}
	
}

方法2:通过实现Runnable接口,重写run()方法,创建一个指定线程任务的对象,传给Thread对象

public class TheadDemo02 {
	public static void main(String[] args) {
		Runnable r1 = new MyTask1();
		Thread t1 = new Thread(r1);
		
		Runnable r2 = new MyTask1();
		Thread t2 = new Thread(r2);
		
		t1.start();
		t2.start();
		System.out.println("main方法over");
	}
}
/*方法2:实现接口,完成任务的定义*/
class MyTask1 implements Runnable{
	//重写run方法,定义任务体
	@Override
	public void run() {
			//随机一个整数输出500次
			int num = (int)(Math.random()*500);
			for (int i = 0; i < 500; i++) {
				System.out.println(num);
			}
		}
		
}

方法3:通过匿名内部类获取线程对象

public class ThreadDeno03 {
	public static void main(String[] args) {
		Thread t1 = new Thread(){
			public void run(){
				for (int i = 0; i < 100; i++) {
					System.out.println("helloworld");
				}
				System.out.println(this.getName()+"结束");
			}
		};
		Thread t2 = new Thread(new Runnable(){
			public void run(){
				for (int i = 0; i < 100; i++) {
					System.out.println("no zuo no die");
				}
				System.out.println("t2结束");
			}
		});
		t1.start();
		t2.start();
		System.out.println("main线程结束");
	}
}

 方法4:使用FutureTask类创建对象,使用Callable创建子类对象,重写call方法(相当于run()方法),再将Callable对象传给FutureTask对象,再将FutureTask对象传给Thread对象,调用start方法,启动线程

public class ThreadDemo04 {
	public static void main(String[] args) {
		FutureTask<String> task = new FutureTask<String>(new Callable<String>(){

			@Override
			public String call() throws Exception {
				int count = 0;
				for (int i = 0; i < 100; i++) {
					if (i%2!=0) {
						count+=i;
					}
				}
				System.out.println("count:"+count);
				return count+"";
			}
			
		});
		Thread t1 = new Thread(task);
		t1.start();
	}
}

使用Runnable接口比较继承Thread类的好处:
(1)将线程对象和任务对象分开,降低了耦合度,便于维护
(2)避免了java中单继承的限制
(3)适合相同的任务代码块处理同一个资源
使用FutureTask类:call方法带有返回值

线程API:

    常用的构造器

        Thread(Runnable r);

        创建一个指定任务的线程对象

        Thread(Runnable r,String name)

        创建一个指定任务,并且指定名称的线程对象

        Thread(String name)

        创建一个指定名称的线程对象

线程的优先级:

1-10,10为最高级别,1为最低级别,5为默认级别

Thread.MIN_PRIORITY--最小优先级

Thread.MAX_PRIORITY--最大优先级

Thread.NORM_PRIORITY--默认优先级

PS:先用线程优先级只能提高cpu分配给优先级高的时间片段的概率,但是不是绝对的

守护线程:

    线程分为两类,一类是普通线程(前台线程),一类是守护线程(后台线程),当线程只剩下守护线程后,所有的线程都结束

设置守护线程的方法:线程对象调用

方法:
    void setDaemon(boolean flag);
    设置为true时是守护线程。
static void sleep(long time);
    使当前进程放弃时间片段,进入阻塞状态,超时后会进入就绪状态,如果没有超时而是被打断会出现检查性异常,
    InterruptException
interrupt()
    打断阻塞状态下的线程对象
void join()
    将当前线程A加入到线程B中,线程B进入阻塞状态,直到线程A结束,线程B进入就绪状态,等待被分配时间片段

static void yield()
    线程对象让步的功能:让出时间片段,此线程进入就绪状态。


同步锁:
    当多个线程操作临界资源时,可能会出现线程安全隐患问题。
    临界资源可能是:
    (1)某一个静态变量
    (2)某一个实例变量
    如果想解决这样的问题,需要使用同步操作。
    异步操作:多线程的并发操作,相当于各干各的
    同步操作:相当于一个做完,另一个再做
线程在内部提供了一个内置的锁机制保证原子性,用于线程进行同步操作。

锁需要两点:
    (1) 锁是一个对象
    (2)如果想进行同步,多个线程操作的必须是同一个锁
    synchronized(锁对象的引用){
    需要同步的代码块
    }

锁机制:

    当一个线程进入同步的代码块后,就会获得锁对象的使用权,其他线程如果执行到此处,会发现锁对象被占用,只能处于等待状态(锁池);当线程执行完同步代码块后或者出现异常,都会自动释放锁。

合适的锁对象:
    必须是一个引用类型,而且必须使多个线程都可以看到这个锁对象,因此this对象比较适合

同步代码块的范围:
    (1)可以是方法内的一部分代码,可以是全部代码(相当于给方法上了锁)
    (2)给方法上添加修饰词synchronized,锁对象为this
    如果一个类的所有成员方法都用了同步关键字,当某一个线程操作了其中某一个方法,另外的线程即时操作的不是这个方法,也会
    进入锁池状态
    (3)静态方法上也可以添加synchronized关键字,锁对象为类对象   类名.class
    调用方法:类名.class,每一种类都有唯一的类对象   

wait()/notify()/notifyAll():
上述方法都是Object类型提供的,用来调控线程状态的。
使用位置必须是同步块中,如果不是同步块中会报异常

wait():
    当前获取锁对象的线程准备释放锁,给其他线程获取锁的机会。
wait(long time):
    当前获取锁对象的线程如果没有被通知,在延迟time毫秒后释放锁对象,自动释放
wait(long time,int naons):
    功能一样,只不过比上一个方法延迟的时间更加精确
notify()/nitifyAll();
    当前获取锁对象的线程准备释放锁,使用此方法通知处于使用wait()等待的线程,让它们开始准备竞争锁对象。
    notify只会随机通知等待线程中的其中一个
notifyAll():
    通知所有等待的线程来竞争锁。

同步代码块内:
    (1)尽可能的不要使用sleep方法和yield方法,因为比较占cpu资源
    (2)同步块越小越好,省cpu的资源

线程池:
    (1)如果每个任务都需要创建线程对象,内存开销大
    (2)方便管理线程对象(任务是Runnable,线程是线程)

线程池的原理:
        就是一些线程的集合,线程的状态不是死亡状态,可以从外面接收任务,当线程池接收到外面的任务时,线程池的任务
    管理器会查看是否有空闲线程,如果有,就将任务分配给它,如果没有,任务就处在等待队列中

线程池的类型:ExecutorService
    
    另外一个类Executors里提供了多个静态方法来获取线程池里的对象
    方法1:
        newSingleThreadExecutor():
    获取单个线程的线程池对象,内部维护了一个无界队列用于存储任务 
    方法2:
        newFixedThreadPool(int nThreads) 
    创建一个固定数量线程的线程池,维护一个无界队列(存任务)
    方法3:
        newCachedThreadPool()
    可以根据需求来创建新线程的线程池对象,如果有可重用的,会优先使用。
    方法4:
        newScheduledThreadPool(int corePoolSize)
    创建一个线程池,可以进行设置延迟

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值