【Java作业】Week10

写在前面的部分

本周主要学习了多线程的相关知识

多线程

1.基本概念

a.  进程:
观察任务管理器,发现正在运行的程序(桌面的应用程序,客户端程序..)就是一个进程
进程来说:进程是系统分配资源调用的一个独立单位.

b. 多线程的意义:

1)现在目前计算机是一种多进程计算机,在做一件事情的同时还可以做另一件事情.
2)多进程是为了提高计算机CPU的使用率

c. 线程:

线程是依赖于进程存在,一个线程相当于进程中的某个任务,

d. 多线程的意义:

一个进程中开启了多个任务,每个一任务(线程),他们在互相抢占CPU的执行权,(提高程序的执行效率)
多线程在抢占CPU的执行权:特点:具有随机性!.

e. jvm虚拟机是不是一个多线程程序:

是,至少开启了两条线程,一条为主线程,另一条为垃圾回收器开启的垃圾回收线程

f. 多线程实现方式:

(1)自定一个类:MyThread 继承自Thread类

(2)在MyThread类中重写Thread类中的run()  :为什么重写run()

(3)在主线程中,创建该类的实例对象,启动线程

2.实现方式一——继承Thread类

a. 概念:
1)自定义一个类继承Thread类,然后重写run()方法,run方法中写的是耗时的操作
public class MyThread extends Thread {
	
	
	/*
	 * 重写Thread类中的run()方法
	 * */
	@Override
	public void run() {
//		System.out.println("helloworld");
		//耗时的操作,线程睡眠,线程等待,循环语句
		for(int x = 0 ; x <100 ; x ++){
			System.out.println(x);
		}
	}
}
2)在主线程中创建该类的实例,启动线程
//run()方法调用相当于调用了一个普通方法,并不会出现线程随机性;而start()方法调用,
		MyThread my1 = new MyThread() ;
		MyThread my2 = new MyThread() ;
		
		//分别启动线程
		my1.start() ;
		my2.start() ;


b. 构造方法:

(1)无参构造    Thread()

(2)有参构造    Thread(String name)

c. 线程名称:

(1)public final String getName()返回该线程的名称

(2)public final void setName(String name)改变线程名称,使之与参数 name 相同。

package org.westos_03;

public class MyThread extends Thread {
		
	

	//无参构造
	public MyThread(){
		
	}
	
	//有参构造
	public MyThread(String name){
		super(name) ;
	}
	@Override
	
	//my1和my2子线程都会执行这段代码,两个子线程在互相抢占CPU的执行权
	public void run() {
		for(int x = 0 ; x <200; x ++){
//			System.out.println(x);
			System.out.println(getName() +":"+x);
			
		}
	}
}

d. 线程优先级:

线程的优先级在1-10中取值,默认值为5.....

(1)Thread关于优先级的字段

1)public static final int MAX_PRIORITY 10  :最大优先级 优先级大的抢占到CPU的执行权大,并不代表就一定能抢到,因为线程的执行具有随机性!

2)public static final int MIN_PRIORITY 1   :最小优先级

3)public static final int NORM_PRIORITY 5  :默认优先级

(2)public final int getPriority():返回线程的优先级

(3)public final void setPriority(int):设置线程的优先级

e. join方法

(1)public final void join() throws InterruptedException等待该线程终止。

如果等待过程中被中断,则抛出异常InterruptedException

(2)线程1执行join方法后,会先执行线程1,等该线程执行完毕之后,再执行其他线程


		jt1.start() ;
			
		//设置线程等待该线程终止该方法必须要启动线程
		try {
			jt1.join() ;
		} catch (InterruptedException e) {
			//InterruptedException:中断异常
			e.printStackTrace();
		}
		
		jt2.start() ;
		jt3.start() ;


f. yield方法:

(1)public static void yield()暂停当前正在执行的线程对象,并执行其他线程。

(2)暂停当前线程执行其他线程,并不保证另一个线程就一定能抢占到CPU的执行权

		@Override
		public void run() {
			//yt1,yt2
			for(int x =0 ; x <100 ; x ++){
				System.out.println(getName()+":"+x);
				Thread.yield() ;
			}
		}


g. 其他方法:

(1)Thread.currentThread():获得当前运行的进程

(2)public final void setDaemon(boolean on)  on指定true,就是设置守护线程...(在线程启动前调用)

将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。 

主线程数据输出完毕时,守护线程等会就会退出,无论run方法是否执行完。

h. 线程的生命周期

(1)public static void sleep(long millis) throws InterruptedException在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)   如果睡眠过程中被打断则抛出异常

(2)public final void stop():强迫线程停止执行

(3)public void interrupt()中断线程。 表示中断线程一种状态

(4)线程的生命周期:


2.实现方式二——实现Runnable接口

a. 开发步骤:
(1)自定义一个类,并实现Runnable接口
(2)重写run方法
(3)在主线程创建实例对象,并将其作为Thread类的参数传入
(4)启动线程
package org.westos_06;

public class MyRunnable implements Runnable {

	@Override
	//run()中的方法也是需要一些耗时的操作
	public void run() {
		for(int x = 0 ; x < 100 ; x ++){
			//在控制台,打印每一个子线程的线程名称
//			System.out.println(getName()+":"+x);
			//getName()是Thread类中的方法
			//间接的使用Thread类的静态功能得到线程名称
			System.out.println(Thread.currentThread().getName()+":"+x);
		}
	}

}

public class ThreadDemo {
	
	public static void main(String[] args) {
		
		//1)创建MyRunnable实例对象
		MyRunnable my = new MyRunnable() ;
		
		//2)创建线程类对象
		//Thread线程类中的构造方法
		//public Thread(Runnable target)
//		Thread t1 = new Thread(my) ;
//		t1.setName("线程1") ;
		//public Thread(Runnable target,String name)
		Thread t1 = new Thread(my, "线程1") ;
		Thread t2 = new Thread(my, "线程2") ;
		
		//启动线程
		t1.start() ;
		t2.start() ;
	}
}

b. 线程同步

(1)模拟电影院售票

某电影院目前正在上映贺岁大片(红高粱,少林寺传奇藏经阁),共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。(要保证三个窗口共享100张票,应把票定义在Runnable接口的子实现类中;)

(2)可能出现的问题(线程不安全)

1)一张票可能被卖多次(CPU的原子性)

2)可能出现负票(延时操作和线程随机性)

(3)检验多线程安全问题的标准

1)当前是否是一个多线程环境

2)多线程环境中是否有共享数据

3)是否有多条语句对共享数据进行操作

(4)解决方案:——synchronized

synchronized(同步锁对象){

多条语句对共享数据的操作;

}

2)

			synchronized(d){//每一个线程ts1,ts2,ts3都使用的一个锁
				//ts1进来,其他线程进不来
				if (tickets > 0) {
					try {
						Thread.sleep(100) ;
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "正在出售第"
							+ (tickets--) + "张票");
				}
			}

(5)同步锁

1)同步锁对象:应该每一个线程都要使用这个锁对象(同步锁):理解为门的开和关

2)可以是Object类型,任意的Java类

(6)同步方法:

1)public synchronized void method(){}   ->非静 态的同步方法的同步锁对象是this

2)静态的同步方法的同步锁对象时类名.class对象

(7)使用第二种方法实现多线程满足数据分离原则(面向接口的编程)

3.同步锁对象Lock

a. 概念:Lock是一个接口,所以它在使用的是 ReentrantLock子实现类

b. 常用方法:

(1)public void lock()获取锁。

(2)public void unlock()试图释放此锁

c. 实现代码 (同步代码块不需要synchronized块)

try{
//获取锁
lock.lock() ;
if(tickets>0){
//加入延迟操作
try {
Thread.sleep(100) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}
}finally{
//试图释放锁对象
lock.unlock() ;

}

4.死锁

a. 概念:
使用同步机制可以解决多线程的安全问题,但是自身也会有弊端:
(1)同步---->执行效率低(每一个线程在抢占到CPU的执行权,会去将(门)关闭,别的线程进不来)

(2)容易出现死锁现象

(3)死锁:两个或者两个以上的线程出现了互相等待的情况,就会出现死锁!

(4)应用:生产和消费者模式

b. 常见代码:

		if(flag){
			synchronized(MyLock.objA){
				System.out.println("if objA");
				synchronized (MyLock.objB) {
					System.out.println("if objB");
				}
			}//代码执行完毕,objA锁相当于才能被释放掉
		}else {
			//dl2
			synchronized (MyLock.objB) {
				System.out.println("else objB");
				synchronized(MyLock.objA){
					System.out.println("else objA");
				}
			}
		}
c. 生产者与消费者模式:

(1)概念: 

生产者:生产数据(输入数据)

消费者:输出数据

5.等待唤醒机制

a. 常用方法:(定义在Object类中)
(1)wait():等待直到调用notify()唤醒
调用之后立即释放锁

(2)notify():唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。


6.线程组——ThreadGroup

a. 构造方法:
(1)public ThreadGroup(String name)构造一个新线程组
(2)public Thread(ThreadGroup group,Runnable target ,String name)(使用线程组构造Thread线程)

b. 方法:

(1)setDaemon:将所有线程均设为守护线程

(2)public final ThreadGroup getThreadGroup()返回该线程所属的线程组(默认线程组为main)

7.线程池

a. 概念:使用线程池节约成本(很多子线程调用完毕不会立即被回收掉,而是会回到线程池中被多次利用!)
使用工厂类来生产线程池

b. 工厂类Executors中常用方法:

(1)public static ExecutorService newFixedThreadPool(int nThreads)

参数指定线程池中有多少个线程

返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。

c. ExecutorService接口常用方法:

(1)Future<?> submit(Runnable task) ——>开启线程

(2)<T> Future<T> submit(Callable<T> task)

		ExecutorService pool = Executors.newFixedThreadPool(2) ;
		
		//下来使用ExecutorService(跟踪多个异步任务)一些方法
		
		//使用submit(Runnable target):提交多个任务
		pool.submit(new MyRunnable()) ;
		pool.submit(new MyRunnable()) ;
		
		//结束线程池
//		void shutdown()
		pool.shutdown() ;
(3)shutdown():关闭线程

d. 多线程实现方式3

(1)submit方法返回Future接口表示异步运算的结果

(2)callable接口:(与Runnable接口类似)

实现该接口需要重写call方法(返回值是Object)

1)public Object call() throws Exception{}

该方法返回的类型和Callable接口中的泛型一致

(3)实现代码:public class MyCallable implements Callable<Object>{}

e. 利用多线程求异步计算的结果:

(1)案例:分别计算1-100,1-200之间的和

(2)创建线程池:

ExecutorService ThreadPool = Executors.newFixedThreadPool(2) ;

(3)提交异步任务:

Future<Integer> f1 = ThreadPool.submit(new MyCallable(100)) ;

Future<Integer> f2 = ThreadPool.submit(new MyCallable(200)) ;

(4)写Callable子实现类:

	@Override
	public Integer call() throws Exception {
		//定义最终结果变量
		int sum = 0 ;
		for(int x = 1 ; x <=number;  x ++ ){
			sum += x ;
		}
		return sum;
	}

(5)调用Future接口中get()方法,返回计算结果

Integer v1 = f1.get() ;
Integer v2 = f2.get() ;














  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值