【JavaSE笔记】多线程(二)多线程

本期知识点:
多线程
线程组
线程池
匿名内部类
定时器


1.多线程

a.JDK5以后的针对线程的锁定操作和释放操作

使用同步机制解决了线程的安全问题,但是我们并没有看到具体的锁对象是谁,JDK5以后java提供了接口
Lock里面又提供了一些方法:
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作
由于该Lock接口不能实例化,提供了子实现类:ReentrantLock
public void lock()   获取锁
public void unlock()   释放锁

b.死锁

i.虽然使用Lock锁定操作或者是同步锁synchronized来解决线程安全的问题,线程安全解决了,
线程安全的弊端:
1)效率低
2)如果出现了同步嵌套,就容易产生死锁问题
ii.死锁问题的产生:
两个或两个以上的线程,抢占CPU的执行权,然后出现了互相等待的情况:使用线程间的通信问题解决!
iii.死锁问题及其代码
是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象

c.线程间通信问题:

不同种类的线程间针对同一个资源的操作。
分析:当前的资源情况:
Student: 共同的资源
setThread:设置学生数据(生成者)
getThread:获取学生数据(消费者)
StudentDemo(测试类)
问题1:
按照生产消费模式:分别进行产生数据和消费数据,通过测试打印出来:

null---0


问题2:
为了数据的效果好,加入循环和判断,给出不同的值,这个时候产生新的问题。
a.同一个数据出现多次
原因: CPU的一点点时间片的执行权,就足够执行很多次。
b.年龄和姓名不匹配
原因:线程运行的随机性
解决方法:

加锁

注意:

i.不同种类的线程都要加锁

ii.不同种类的线程必须加同一把锁


问题3:线程安全问题解决了,但是会存在如下问题
i.如果消费者先抢到了CPU的执行权,就会去消费数据,但是现在的数据是默认值。没有意义应该等待数据有意义再去消费。
ii.如果生产者抢到CPU的执行权,就会去生产数据,但是它生产完数据后依然拥有执行权,那么它继续生产数据。这样就有问题,应该等消费者把数据消费完,再生产。
正常思路:
1)生产者:先看是否有数据,有就等待,没有就生产,生产完后通知消费者来消费数据。
2)消费者:先看是否有数据,有就消费,没有就等待,通知生产者生产数据。
为了处理这样的问题,Java提供了一种机制:等待唤醒机制
d.等待唤醒机制
i.Object类中提供了一些方法:
wait()   线程等待
public final void notify()   唤醒正在等待的单个线程
public final void notifyAll()   唤醒所有线程
ii.(面试题)这几个方法都是线程有关的方法,为什么把这个方法不定一在Thread类里面?

刚才这个案例,使用的锁对象进行调用,锁对象可以是任意对象.而Object本身就是代表所有的类根类:代表所有对象.


e.(问题)wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?

i.这些方法存在与同步中。
ii.使用这些方法时必须要标识所属的同步的锁。

iii.锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。


f.线程的状态转移图:




死锁:

public class MyLock {
	public static final Object objectA = new Object();
	public static final Object objectB = new Object();
	
}
public class DieLock extends Thread {
	private boolean flag;
	public DieLock(boolean flag) {
		this.flag=flag;
	}
	@Override
	public void run() {
		if(flag){
			synchronized (MyLock.objectA) {
				System.out.println("if ObjectA");
				synchronized (MyLock.objectB){
					System.out.println("if ObjectB");
				}
			}
		}
		else{
			synchronized (MyLock.objectB){
				System.out.println("else ObjectB");
				synchronized (MyLock.objectA){
					System.out.println("else Object");
				}
			}
		}
	}
}
public class DieLockDemo {
	public static void main(String[] args) {
		//创建线程对象
		DieLock d1 = new DieLock(true);
		DieLock d2 = new DieLock(false);
		//启动线程
		d1.start();
		d2.start();
	}
}


等待唤醒机制:

//生产者
public class SetThread implements Runnable {
	private Student s ;
	private int x = 0;
	
	public SetThread(Student s ){
		this.s=s;
	}
	@Override
	public void run() {
		while(true){
			synchronized (s){
				if(s.flag){
					try {
						s.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				if(x%2==0){
					s.name="Ash"; 
					s.age=24;
				}else{
					s.name="Ying"; 
					s.age=22;
				}
				x++;
				//修改标记
				s.flag=true;
				//唤醒线程
				s.notify();
			}
		}
	}
}
//消费者
public class GetThread implements Runnable {

	private Student s ;
	public GetThread(Student s ){
		this.s=s;
	}
	@Override
	public void run() {
		while(true){
			synchronized(s){
				if(!s.flag){
					try {
						s.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println(s.name+"---"+s.age);
				//修改标记
				s.flag=false;
				//唤醒线程
				s.notify();
			}
		}
	}

}
public class Student {
	String name;
	int age;
	boolean flag;	
}
public class StudentDemo {
	public static void main(String[] args) {
		//创建共同资源对象
		Student s = new Student();
		//创建每个线程对象,针对同一个资源进行操作
		SetThread st = new SetThread(s);
		GetThread gt = new GetThread(s);
		//创建Thread类对象
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(gt);
		//分别启动线程
		t1.start();
		t2.start();	
	}
}


2.线程组

a.概述

Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。

b.常用方法:

public ThreadGroup(String name)   构造一个新线程组。
public Thread(ThreadGroup group,Runnable target, String name)
public final void setDaemon(boolean daemon)   设置线程组是否是一个守护线程
public final ThreadGroup getThreadGroup()   返回该线程所在的线程组。默认情况下,所有的线程都属于主线程组。

public final String getName()   返回此线程组的名称。

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 10 ; i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
}
public class ThreadGroupDemo {
	public static void main(String[] args) {
		//fun1();
		fun2();
	}
	private static void fun2() {
		//不知道t1,t2线程是属于哪一个线程组的?
		//如何该线程所在线程组的名称
		MyRunnable my = new MyRunnable() ;
		
		Thread t1 = new Thread(my, "张三") ;
		Thread t2 = new Thread(my, "李四") ;
		
		//Thread类中提供了另外一个方法:
//		public final ThreadGroup getThreadGroup():返回该线程所在的线程组
		//使用线程对象调用
		ThreadGroup tg1 = t1.getThreadGroup() ;
		ThreadGroup tg2 = t2.getThreadGroup() ;
		
		//public final String getName()返回此线程组的名称。
		String name1 = tg1.getName() ;
		String name2 = tg2.getName() ;
		
		//输出这两个线程所在的线程组名称
		System.out.println(name1);
		System.out.println(name2);
		
		//通过测试:发现线程默认情况线程组属于main线程:
		System.out.println(Thread.currentThread().getThreadGroup().getName());
	}
	private static void fun1() {
		//如何设置线程组名称?
//		public ThreadGroup(String name)构造一个新线程组。
		ThreadGroup tg = new ThreadGroup("这是一个新的线程组") ;
		
		MyRunnable my = new MyRunnable();
		Thread t1 = new Thread(my ,"Ash");
		Thread t2 = new Thread(my ,"Glaz");
		
		ThreadGroup tg1 = t1.getThreadGroup();
		ThreadGroup tg2 = t2.getThreadGroup();
		
		String name1 = tg1.getName() ;
		String name2 = tg1.getName() ;
		System.out.println(name1);
		System.out.println(name2);
		
		tg.setDaemon(true);
	}
}



3.线程池

a.概述:

程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。

b.使用线程池可以解决很多问题:

1)如何创建线程池对象:
Executors工厂:专门用来创建线程池的:提供了一个方法
public static ExecutorService newFixedThreadPool(int nThreads)
2)这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
Future<?> submit(Runnable task)   Runnable接口作为一个参数:要该类的子实现类对象
<T> Future<T> submit(Callable<T> task)
3)线程池可以结束吗?
void shutdown()

c.多线程程序实现方法三:

步骤:
1)自定义一个类,实现Callable接口
2)实现里面的call方法
3)主线程中创建线程对象,
4)用线程池对象提交任务(例如:MyCallable这个任务实现0--100之间的循环)

5)提交后结束线程池

public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 100 ; i++) {
			System.out.println(Thread.currentThread().getName()+":"+i);
		}
	}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorsDemo {
	public static void main(String[] args) {
		//创建线程池对象
		ExecutorService pool = Executors.newFixedThreadPool(3);
		//使用ExecutorsService接口中有要给方法:
		//Future<?> submit(Runnable task):Runnable接口作为一个参数:要该类的子实现类对象
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());
		pool.submit(new MyRunnable());
		//结束线程池
		pool.shutdown();
	}
}


4.匿名内部类

匿名内部类的方式实现多线程程序:

new 类名或者接口名{(

重写/实现方法;

}

本质:继承该类或者或者实现该接口的子类对象!


5.定时器

a.概述:

定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能

b.构造方法:a

public Timer()   创建一个新计时器。
public void schedule(TimerTask task, Date time)   安排在指定的时间执行指定的任务
参数1:task - 所要安排的任务
参数2:time - 执行该任务的时间毫秒值

c.其他方法

public boolean cancel()   取消此计时器任务
public void schedule(TimerTask task, Date firstTime,long period)   每隔多少毫秒进行重复性的 任务

import java.util.Timer;
import java.util.TimerTask;

public class Demo01_无参调用 {
	public static void main(String[] args) {
		Timer t = new Timer();
		t.schedule(new Fun(), 3000);
	}	
}
class Fun extends TimerTask{
	public Fun(){
		
	}
	@Override
	public void run() {
		System.out.println("boom!!!");
	}
}

import java.util.Timer;
import java.util.TimerTask;

public class Demo02_有参调用 {
	public static void main(String[] args) {
		//创建定时器对象
		Timer t = new Timer();
		t.schedule(new Hun(t), 3000);
	}
}

class Hun extends TimerTask{
	private Timer t;
	public Hun(){
		
	}
	public Hun(Timer t){
		this.t=t;
	}
	@Override
	public void run() {
		System.out.println("boom!!!");
		t.cancel();
	}
}

import java.io.File;
import java.util.Timer;
import java.util.TimerTask;

//在指定的时间删除我们的指定目录(你可以指定c盘,但是我不建议,我使用项目路径下的demo)

public class test {
	public static void main(String[] args) {
		File f = new File("C:\\Users\\Administrator\\Desktop\\Demo");
		Timer t = new Timer();
		t.schedule(new Fun3(t,f), 3000);
	}
}
class Fun3 extends TimerTask{
	
	private Timer t ;
	private File f;
	public Fun3(Timer t, File f) {
		this.t=t;
		this.f=f;
	}

	@Override
	public void run() {
		hun(f);
		t.cancel();
	}
	public void hun(File f){
		File[] fa = f.listFiles();
		//非空判断
		if(fa!=null){
			for (File file : fa) {
				if(file.isDirectory()){
					hun(file);
				}else{
					System.out.println(file.getName()+":"+file.delete());
				}
			}
			System.out.println(f.getName()+":"+f.delete());
		}
	}
}


                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值