JAVA多线程基础

JAVA多线程基础

1.什么是并发原理?

多线程:多线程允许我们“同时”执行多段代码。

线程是并发运行的,线程调度会统一规划CPU时间,将CPU的时间划分为若干片段,然后尽可能的均匀分配给所要并发运行的线程,每个线程在获取CPU时间片段后CPU就会运行它的任务,当时间片段用完后,Cpu会离开并执行获取到CPu时间片的线程,所以,所有线程并非真正的“同时”运行着代码,而是走走停停,这种微观上走走停停,在宏观上感觉是同步进行的现象称为“并发运行”。

2.创建线程Thread

1. 方式一:继承Thread并重写run方法来定义线程任务;
public class ThreadDemo {
	public static void main(String[] args) {
		Thread t1=new MyThread1();
		Thread t2=new MyThread2();
	/*
	* 启动线程不是调用Run方法,而是要调用start()方法,
	* 线程开始并发运行时会自动调用run()方法。
	*/
		t1.start();
		t2.start();
	}
}

class MyThread1 extends Thread{
	public void run(){
		for(int i=0;i<100;i++){
			System.out.println("你好,兄弟");
		}
	}
}

class MyThread2 extends Thread{
	public void run(){
		for(int i=0;i<100;i++){
			System.out.println("我不好!!!!!");
		}
	}
}

​ 第一种创建线程的方式有两个不足的地方
​ 1.由于java是单继承,这就倒置若继承了Thread就不能在继承其他的类,在实际开发中并不方便,因为无法重用其他类的某些方法。
​ 2.由于继承Thread后重写run()方法定义线程要执行的任务,这就导致了 线程与线程执行任务有一个必然的耦合关系,不利于线程的重用。

2.第二种创建线程的方法:实现Runnable接口单独定义线程任务
public class ThreadDemo2 {
	public static void main(String[] args) {
		//创建线程任务
		MyThread3 m1=new MyThread3();
		MyThread4 m2=new MyThread4();
		//创建线程并执行任务
		Thread t1=new Thread(m1);
		Thread t2=new Thread(m2);
		
		//执行任务
		t1.start();
		t2.start();
	 }
}
//线程任务
class MyThread3 implements Runnable {
	public void run() {
		for(int i=0;i<100;i++){
			System.out.println("淦淦淦!");
		}
	}
}

class MyThread4 implements Runnable{
	public void run() {
		for(int i=0;i<100;i++){
			System.out.println("草草草!");
		}
	}
}

​ 使用Runnable创建并使用线程实现Runnable接口并重写run方法来定义线程体,然后再创建线程的时候将Runnable的实例传入并启动线程,这样做的好处在于可以线程和线程要执行任务分离,降低耦合度。

3.使用匿名内部类实现创建线程的两种方式
public class ThreadDemo3 {
	public static void main(String[] args) {
		//创建线程
		Thread t1=new Thread(){
			
			public void run() {
				for(int i=0;i<100;i++){
					System.out.println("淦淦淦!");
				}
			}
		};
		
		//创建线程任务
		Runnable r=new Runnable() {
		
			public void run() {
				for(int i=0;i<100;i++){
					System.out.println("草草草!");
				}
			}
		};
		Thread t2=new Thread(r);
		
		//启动线程
		t1.start();
		t2.start();
	}
}

3.currentThread();

线程提供了static Thread currentThread(),该方法可以获取运行这个方法的线程,java中所有的代码都是线程运行的,main方法也不例外,jvm启动后会启动一个线程来执行main方法,通常我们成执行main方法的线程为“主线程”.

public class CurrentThreadDemo {
    
	public static void dosome(){
		Thread t=Thread.currentThread();
		//Thread[main,5,main]
		//Thread[线程名称,优先级,线程组]
		System.out.println("运行dosome的线程:"+t);
	}
    
	public static void main(String[] args) {
		dosome();
		Thread t2=Thread.currentThread();
		System.out.println(t2);
		Thread t3=new Thread(){
			public void run(){
				Thread r=Thread.currentThread();
				System.out.println("t3线程"+r);
			}
		};
		
		//启动
		t3.start();
	}
}

代码运行结果:

运行dosome的线程:Thread[main,5,main]
Thread[main,5,main]
t3线程Thread[Thread-0,5,main]

4.线程的优先级

​ 一.线程的优先级:线程调用start()方法后纳入线程调度统一管理,线程不能主动获取cpu时间片段,只能被动分配,调正先线程优先级可大程度的改善某个线程获取cpu时间片段的概率,理论上线程优先级越高的线程获取cpu时间片段的次数越多。

​ 二.线程优先级10个等级(1-10)

​ 1.表示优先级最低

​ 5.表示默认

​ 10.表示优先级最高

​ 三.设置优先级方法setPriority(int p);

​ 线程三个常量分别表示最低,最高,默认

public class priorityDemo {
	public static void main(String[] args) {
		Thread t1=new Thread(){
			public void run(){
				for(int i=0;i<1000;i++){
					System.out.println("max");
				}
			}
		};
		Thread t2=new Thread(){
			public void run(){
				for(int i=0;i<1000;i++){
					System.out.println("默认");
				}
			}
		};
		Thread t3=new Thread(){
			public void run(){
				for(int i=0;i<1000;i++){
					System.out.println("min");
				}
			}
		};
		
		//设置线程的优先级
		t1.setPriority(Thread.MAX_PRIORITY);//10
		t2.setPriority(Thread.NORM_PRIORITY);//5
		t3.setPriority(Thread.MIN_PRIORITY);//1
		
		//启动
		t1.start();
		t2.start();
		t3.start();
	}
}

结果分析:

max先输出完,下来是默认,再是min。

5.睡眠阻塞状态

1.static void sleep(long ms)当一个线程执行完sleep方法后就会进入阻塞状态指定的毫秒数,超时后线程会自动回到runnable就绪状态等待两次并发运行,该方法要求必须处理interruptedException。

2.睡眠阻塞状态时被其他线程调用interruput方法时,会抛出该中断异常并打断睡眠阻塞状态。

3.interrupt()方法用于中断线程,但是若线程处于阻塞状态时中断阻塞,若线程没有处于阻塞状态则线程直接被中断。

1.案例一:用睡眠阻塞模拟小品事件
public class SleepDemo {
	public static void main(String[] args) {
		
		Thread qiang=new Thread(){
			public void run(){
				System.out.println("强哥:刚贴上面膜,睡会。。。。。");
				try {
					Thread.sleep(10000);
					System.out.println("我醒来了!");
				} catch (InterruptedException e) {
					System.out.println("强哥:卧槽,咋回事!小老弟");
				}
			}
		};
		
		
		Thread chao=new Thread(){
			public void run(){
				System.out.println("刘永超:我开始砸墙了");
				for(int i=0;i<5;i++){
					System.out.println("刘永超:80");
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				System.out.println("刘永超:咣当,搞定!!");
				qiang.interrupt();
			}
		};
		
		//启动
		chao.start();
		qiang.start();
	}
}
运行结果:

强哥:刚贴上面膜,睡会。。。。。
刘永超:我开始砸墙了
刘永超:80
刘永超:80
刘永超:80
刘永超:80
刘永超:80
刘永超:咣当,搞定!!
强哥:卧槽,咋回事!小老弟

2.案例二:用睡眠阻塞状态实现图片切换
public class SleepDemo2 {
	public static void main(String[] args) {
		JFrame rame=new JFrame();//相框
		rame.setSize(350,350);//大小
		JPanel panel=new JPanel();//面板
		panel.setSize(350,350);
		rame.setContentPane(panel);//把面板放到相框中
		rame.setVisible(true);//设置相框可见
		//点击关闭按钮同时关闭程序
		rame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		Thread t=new Thread(){
			public void run(){
				int i=0;
				while(true){
					i=i==0?1:0;
					try {
						Thread.sleep(500);
						if(i==0){
							panel.setBackground(Color.BLACK);
						}else{
							panel.setBackground(Color.green);
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			}
		};
		//启动
		t.start();
	}
}
运行结果:

一个面板中会有两张分别为红色和绿色底色来回切换,切换间隔为500ms。

6.join()方法

线程提供了一个方法void join()该方法允许线程在另一个线程上等待,直到执行完毕后再继续运行,这样做可以协调线程间的“同步运行”。

同步运行:代码执行有先后顺序(单线程下运行时是同步的,多线程下也可以进行同步运行操作)

异步运行:代码各执行各的。(多线程下运行代码时是异步的)

案例:模拟图片下载
public class JoinDemo {
	public static void main(String[] args) {
		//线程(下载)
		Thread download=new Thread(){
			public void run(){
				System.out.println("开始下载图片。。");
				for(int i=10;i<=100;i=i+10){
					try {
						System.out.println("下载进度:"+i+"%");
						sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println("图片下载完毕!!");
			}
		};
		
		
		//线程(显示图片)
		Thread show=new Thread(){
			public void run(){
				try {
					download.join();
					System.out.println("图片显示完毕");
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		};
		
		
		//运行
		download.start();
		show.start();
	}
}
运行结果:

开始下载图片。。
下载进度:10%
下载进度:20%
下载进度:30%
下载进度:40%
下载进度:50%
下载进度:60%
下载进度:70%
下载进度:80%
下载进度:90%
下载进度:100%
图片下载完毕!!
图片显示完毕

7.守护线程setDaemon(boolean a)

守护线程(后台线程):

1.守护线程又称为后台线程,默认创建的线程都不是后台线程,守护线程需要调用线程提供的方法void setDaemon( boolean b),当参数为true时,线程变为守护线程。

2.守护线程和普通线程上在使用上没有什么区别,但是结束时有一个区别:

当进程结束时,所有正在运行的守护线程会强制结束,也就是说,也就是说,普通线程结束时,守护线程会强制结束。

案例:模拟泰坦尼克号情景剧
public class DaemonThread {
	public static void main(String[] args) {
		//线程1(rose)
		Thread rose=new Thread(){
			public void run(){
				for(int i=0;i<10;i++){
					System.out.println("rose: 让我去死。");
					try {
						sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				System.out.println("rose:扑通。。。");
			}
		};
		
		
		//线程2(jack)
		Thread jack=new Thread(){
			public void run(){
				while(true){
					System.out.println("jack:你死我也死!!!");
					try {
						sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		};
		
		//给jack设置成守护线程(后台线程)
		//设置守护线程在start方法之前方法前调用
		jack.setDaemon(true);
		rose.start();
		jack.start();
	}
}
运行结果:

jack:你die我也die!!!
rose: 让我去die。
rose: 让我去die。
jack:你die我也die!!!
rose: 让我去die。
jack:你die我也die!!!
rose: 让我去die。
jack:你die我也die!!!
rose: 让我去die。
jack:你die我也die!!!
jack:你die我也die!!!
rose:扑通。。。

从上面的运行可以看出当rose不再说话了,jeck也不说话了,说明rose线程结束,jack线程也跟着结束了。

8.查看线程的相关方法

public class InforDemo {
	public static void main(String[] args) {
		//获取该方法的线程信息
		Thread main=Thread.currentThread();
		System.out.println(main);
		//获取线程id
		long id=main.getId();
		System.out.println(id);
		//获取线程名字
		String name=main.getName();
		System.out.println(name);
		
		//获取线程的优先级
		int p=main.getPriority();
		System.out.println(p);
		
		//判断线程是否还活着
		boolean b=main.isAlive();
		System.out.println(b);
		
		//判断线程是否是后台线程
		boolean b1=main.isDaemon();
		System.out.println(b1);
		
		//判断线程是否被中断
		boolean b2=main.isInterrupted();
		System.out.println(b2);
	}
}

9.线程池

线程池主要完成工作:
1.管理多线程数量,由于多个每个线程会占用一定的内存,线程数量过多会导致内存占用过大,还有个问题就是cpu过度切换会导致程序(避免浪费资源)出现卡顿。
2.重用线程。

public class ThreadPool {
	public static void main(String[] args) {
		//创建线程池
		ExecutorService pool=Executors.newFixedThreadPool(2);
		for(int i=0;i<10;i++){
			//10个任务
			Runnable runn=new Runnable() {
				public void run() {
					String name=Thread.currentThread().getName();
					System.out.println(name+"正在执行任务。。。");
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			};
			//将线程任务指定给线程池
			pool.execute(runn);
		}
		/*
		 * shutdown()与shutdownNow()都是线程池停止状态
		 * shutdown()方法调用后,线程池不再接受新任务,会将已有的任务全部执行后再停止
		 * shutdownNow()调用后,线程池会调用所有线程的中断方法后立即停止
		 */
		pool.shutdown();
	}
}
运行结果:

会有两个线程来一起执行任务,大大降低的运行时间,提高了运行效率。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值