黑马程序员-Java基础:多线程

本文详细介绍了Java多线程的基础概念与核心技术,包括进程与线程的区别、JVM启动线程机制、实现多线程的两种方式、线程调度模型、线程控制方法如休眠、加入、礼让等,以及同步机制防止数据不一致问题。

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


多线程总结

一、多线程概述

进程:进程是指正在运行中的程序,当打开系统的资源管理器时,会发现很多进程,这些都是在计算机中正在运行中的程序。进程是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。 

线程:线程是指进程中的一条执行路径 
a.单线程:如果一个进程只有一条执行路径,那么这个程序就会沿着这条执行路径,顺序执行,知道结束,这就是单线程程序。 
b.多线程:如果一个进程有多条执行路径,那么程序会在这多条执行路径之间并行执行。 

1、多进程和多线程的意义

a:多进程的意义:提高CPU的使用率.

b:多线程的存在是:提供了应用程序的使用率.

2、JVM运行原理以及JVM启动的线程探讨

JVM的运行原理::

我们在使用java命令在运行一个程序的时候,其实是启动了JVM,而JVM的启动相当于一个进程.而同时这个进程会启动一个线程,通过这

个线程来调用main方法,而这个线程就是我们的主线程.

JVM的启动是多线程的吗?

是多线程的.因为JVM至少启动了两个线程,一个是主线程 , 一个是垃圾回收线程.

3、实现多线程的两种方式

第一种方式的步骤:
a: 创建一个类,然后让这个类去继承Thread类
b: 复写run方法(run方法中封装的都是要被线程执行的代码, run方法中代码的特点: 比较耗时的代码)
c: 创建a中定义的类的对象
d: 启动线程(启动线程使用的是start()方法) 

public class MyThread extends Thread {

	@Override
	public void run() {
		
		for(int x = 0 ; x < 100 ; x++){
			System.out.println(x);
		}
		
	}
	
}
public class SellTicketsDemo {
	
	public static void main(String[] args) {
		
		// 创建SellTickets对象
		SellTickets st = new SellTickets() ;
		
		// 创建Thread对象
		Thread t1 = new Thread(st , "窗口1") ;
		Thread t2 = new Thread(st , "窗口2") ;
		Thread t3 = new Thread(st , "窗口3") ;
		
		// 启动线程
		t1.start() ;
		t2.start() ;
		t3.start() ;
		
	}

}

第二种方式的步骤:
a: 创建一个类,然后让这个类去实现Runnable接口
b: 复写run方法
c: 创建a中定义的类的对象
d: 创建Thread类的对象,然后把c中的对象作为参数传递
e: 启动线程
public class MyThread implements Runnable {

	@Override
	public void run() {
	
		for(int x = 0 ; x < 100 ; x++){
			System.out.println(Thread.currentThread().getName() + "---" + x);
		}
		
	}

}
public class ThreadDemo {
	
	public static void main(String[] args) {
		
		// 创建MyThread类的对象
		MyThread my = new MyThread() ;
		
		// 创建Thread类的对象,把my中的对象作为参数传递
//		Thread t1 = new Thread(my) ;
//		Thread t2 = new Thread(my) ;
		
		/**
		 * public Thread(Runnable target, String name)分配新的 Thread 对象。可以给线程设置名称
		 */
		Thread t1 = new Thread(my , "张三") ;
		Thread t2 = new Thread(my , "李四") ;
		
		// 设置名称
//		t1.setName("张三") ;
//		t2.setName("李四") ;
		
		// 启动线程
		t1.start() ;
		t2.start() ;
	}

}

4、线程调度

调度模型:
a: 分时调度:就是给每一个线程分配指定的时间进行执行
b: 抢占式调度:优先的执行优先级高的线程,如果线程的优先级相同,那么就随机执行一个.每一个线程应该都存在一个默认的优先级.
而java语言采用的就是抢占式调度
如何获取线程的优先级?
public final int getPriority(): 获取线程的优先级(线程的默认优先级是: 5)
public final void setPriority(int newPriority): 给线程设置优先级(存在一个范围这个范围是1-10)

public class ThreadDemo {
	
	public static void main(String[] args) {
		
		// 创建线程对象
		MyThread t1 = new MyThread() ;
		MyThread t2 = new MyThread() ;
		
		// 给线程设置名称
		t1.setName("小菲菲") ;
		t2.setName("公孙瓒") ;
		
		// 获取线程的优先级
		/**
		 * public final int getPriority()返回线程的优先级。 
		 */
		
		/**
		 * 给线程设置优先级:	public final void setPriority(int newPriority)更改线程的优先级。 
		 */
		
		// java.lang.IllegalArgumentException: 非法的参数异常
		// t1.setPriority(10000) ;
		
		/**
		 * 线程的优先级是有一个范围的这个范围在[1,10]
		 */
		t1.setPriority(10) ;
		
//		System.out.println(t1.getPriority()) ;			// 线程的默认优先级是5
//		System.out.println(t2.getPriority()) ;
		
		// 启动线程
		t1.start() ;
		t2.start() ;
	}

}
5、线程获取名称

如何获取线程的名称? public final String getName()
如何给线程设置名称? public final void setName(String name), 也可以通过构造方法进行设置
如何获取主线程的名称? 获取到当前正在执行的线程,然后在获取线程名称.如何获取到当前正在执行的线程.
在Thread类中存在一个静态的方法: 
public static Thread currentThread():获取当前正在执行的线程

public class ThreadDemo {
	
	public static void main(String[] args) throws InterruptedException  {
		
		// 创建线程对象
		MyThread t1 = new MyThread() ;
		MyThread t2 = new MyThread() ;
		MyThread t3 = new MyThread() ;
		
		// 给线程设置名称
		t1.setName("李渊") ;
		t2.setName("李元吉") ;
		t3.setName("李元霸") ;
		
		
		// 启动线程
		t1.start() ;
		
		/**
		 * 加入李源线程
		 */
		t1.join() ;
		
		t2.start() ;
		t3.start() ;
		
	}

}
public class MyThread extends Thread {

	@Override
	public void run() {
		
		for(int x = 0 ; x < 100 ; x++) {
			System.out.println(getName() + "----" + x);
		}
		
	}
	
}
6、线程控制

(1)线程休眠

public static void sleep(long time) ;让线程休眠一段时间

public class MyThread extends Thread {

	@Override
	public void run() {
		
		for(int x = 0 ; x < 100 ; x++) {
			
			// 线程休眠
			try {
				Thread.sleep(2000) ;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			System.out.println(getName() + "----" + x);
		}
		
	}
	
}
/**
 * 线程控制之线程休眠:
 * 		public static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),
 */
public class ThreadDemo {
	
	public static void main(String[] args) {
		
		// 创建线程对象
		MyThread t1 = new MyThread() ;
		
		// 给线程设置名称
		t1.setName("张三") ;
		
		// 启动线程
		t1.start() ;
		
	}

}

(2)线程加入

public final void join();等待该线程终止. 等待该线程执行完毕以后在执行其他的线程

/**
 * 线程加入:	public final void join() 等待该线程终止。 
 * 			等待该线程执行完毕以后在执行其他的线程
 * 线程加入: 在启动线程以后在进行加入,如果在线程启动以前在进行加入,则无效.
 * 
 */
public class ThreadDemo {
	
	public static void main(String[] args) throws InterruptedException  {
		
		// 创建线程对象
		MyThread t1 = new MyThread() ;
		MyThread t2 = new MyThread() ;
		MyThread t3 = new MyThread() ;
		
		// 给线程设置名称
		t1.setName("李渊") ;
		t2.setName("李元吉") ;
		t3.setName("李元霸") ;
		
		
		// 启动线程
		t1.start() ;
		
		/**
		 * 加入李源线程
		 */
		t1.join() ;
		
		t2.start() ;
		t3.start() ;
		
	}

}
public class MyThread extends Thread {

	@Override
	public void run() {
		
		for(int x = 0 ; x < 100 ; x++) {
			System.out.println(getName() + "----" + x);
		}
		
	}
	
}

(3)线程礼让

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

/**
 * 线程礼让:
 * 		public static void yield()暂停当前正在执行的线程对象,并执行其他线程。 
 * 		这线程礼让是暂停当前正在执行的线程,而这个线程的暂停时间很短很短,那么如果在该线程暂停完毕以后其他的线程还没有在
 * 		抢到CPU的执行权,那么该线程就会继续和其他的线程抢占CPU的执行权.
 */
public class ThreadDemo {
	
	public static void main(String[] args) {
		
		// 创建线程对象
		MyThread t1 = new MyThread() ;
		MyThread t2 = new MyThread() ;
		
		// 给线程设置名称
		t1.setName("刘亦菲") ;
		t2.setName("惠雷雨") ;
		
		// 启动线程
		t1.start() ;
		t2.start() ;
		
	}

}
public class MyThread extends Thread {

	@Override
	public void run() {
		
		for(int x = 0 ; x < 100 ; x++) {
			
			System.out.println(getName() + "----" + x);
			
			// 线程礼让
			Thread.yield() ;
		}
		
	}
	
}

(4)线程守护

public final void setDaemon(boolean on):把指定的线程设置成一个守护线程.当执行的线程都是守护线程的时候,JVM停止运行

**
 * 线程控制之线程守护:
 * 			public final void setDaemon(boolean on): 把指定的线程设置成守护线程
 */
public class ThreadDemo {
	
	public static void main(String[] args) {
		
		// 创建线程
		MyThread t1 = new MyThread() ;
		MyThread t2 = new MyThread() ;
		
		// 给线程设置名称
		t1.setName("关羽") ;
		t2.setName("张飞") ;
		
		// 设置关羽和张飞线程为守护线程
		t1.setDaemon(true) ;
		t2.setDaemon(true) ;
		
		// 启动线程
		t1.start() ;
		t2.start() ;
		
		// 获取主线程,给主线程设置名称
		Thread.currentThread().setName("刘备") ;
		for(int x = 0 ; x < 10 ; x++){
			System.out.println(Thread.currentThread().getName() + "---" + x);
		}
		
	}

}


(5)线程中断

public final void stop():停止线程的运行

public void interrupt():打断阻塞状态. 继续执行下面的代码,但是该方法会抛出异常.

/**
 * 线程控制之线程中断:
 * 		public final void stop(): 中断线程
 * 		public void interrupt():  中断线程。
 * 			如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,
 * 			或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,
 * 			则其中断状态将被清除,它还将收到一个 InterruptedException。 
 */
public class ThreadDemo {
	
	public static void main(String[] args) {
		
		// 创建线程对象
		MyThread t1 = new MyThread() ;
		
		// 设置线程名称
		t1.setName("宋承宪") ;
		
		// 启动线程
		t1.start() ;
		
		try {
			Thread.sleep(3000) ;
			
			// 中断线程t1
//			t1.stop() ;
			t1.interrupt() ;
			
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
	}

}
6、线程获安全

可以使用同步代码块:
格式:
synchronized(对象){
要被同步的代码 ;
}
同步代码块在保证数据的安全性主要依赖这个对象,要求使用的对象必须是同一个.这个对象可以看做成一把锁.
同步代码块的好处: 可以保证数据的安全性
同步代码块的弊端: 效率降低了

同步代码块以及同步方法和静态同步方法的锁对象的问题:
同步代码块的锁对象: 是任意的对象
同步方法的锁对象: 是this
静态同步方法的锁对象:是该类对应的字节码文件对象.

例1:同步代码块

public class SellTickets implements Runnable {
	
	// 定义票的数量
	private static int num = 100 ;
	
	private static final Object obj = new Object() ;

	@Override
	public void run() {
		
		while(true) {
			
			synchronized(obj) {
				
				if(num > 0) {
					
					try {
						Thread.sleep(100) ;
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
					System.out.println(Thread.currentThread().getName() + "正在出售第" + (num--) + "张票");
				}
				
			}
			
		}
	}

}
public class SellTicketsDemo {
	
	public static void main(String[] args) {
		
		// 创建SellTickets对象
		SellTickets st = new SellTickets() ;
		
		// 创建Thread对象
		Thread t1 = new Thread(st , "窗口1") ;
		Thread t2 = new Thread(st , "窗口2") ;
		Thread t3 = new Thread(st , "窗口3") ;
		
		// 启动线程
		t1.start() ;
		t2.start() ;
		t3.start() ;
		
	}

}
例2:同步方法和静态代码块

public class SellTickets implements Runnable {
	
	// 定义票的数量
	private static int num = 100 ;
	private static final Object obj = new Object() ;
	private int n = 0 ;
	
	@Override
	public void run() {
		
		while(true) {
			
			if(n % 2 == 0){
				synchronized(SellTickets.class) {
					
					if(num > 0) {
						
						try {
							Thread.sleep(100) ;
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						
						System.out.println(Thread.currentThread().getName() + "正在出售第" + (num--) + "张票");
					}
					
				}
			}else {
				sellTickets() ;
			}
			
			n++ ;
		}
	}
	
	/**
	 * 现在的这个方法的方法体添加了同步代码块,那么我们可不可以把这个synchronized关键字定义在方法上呢?
	 */
//	public void sellTickets() {
//		
//		synchronized(obj) {
//			
//			if(num > 0) {
//				
//				try {
//					Thread.sleep(100) ;
//				} catch (InterruptedException e) {
//					e.printStackTrace();
//				}
//				
//				System.out.println(Thread.currentThread().getName() + "正在出售第" + (num--) + "张票");
//			}
//			
//		}
//	}
	
	// 同步方法
//	public synchronized void sellTickets() {
//		
//		if(num > 0) {
//			
//			try {
//				Thread.sleep(100) ;
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
//			
//			System.out.println(Thread.currentThread().getName() + "正在出售第" + (num--) + "张票");
//		}
//	}
	
	// 静态同步方法
	public static synchronized void sellTickets() {
			
		if(num > 0) {
			
			try {
				Thread.sleep(100) ;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			System.out.println(Thread.currentThread().getName() + "正在出售第" + (num--) + "张票");
		}
	}
}
/**
 * 同步代码块的锁对象:		可以是任意的对象
 * 同步方法的锁对象:		是this
 * 静态同步方法的锁对象:   是该类对应的字节码文件对象
 */
public class SellTicketsDemo {
	
	public static void main(String[] args) {
		
		// 创建SellTickets对象
		SellTickets st = new SellTickets() ;
		
		// 创建Thread对象
		Thread t1 = new Thread(st , "窗口1") ;
		Thread t2 = new Thread(st , "窗口2") ;
		Thread t3 = new Thread(st , "窗口3") ;
		
		// 启动线程
		t1.start() ;
		t2.start() ;
		t3.start() ;
		
	}

}
7、死锁
/*
写一个死锁程序
*/

//定义一个类来实现Runnable,并复写run方法
class LockTest implements Runnable
{
	private boolean flag;
	LockTest(boolean flag)
	{
		this.flag=flag;
	}
	public void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(LockClass.locka)//a锁
				{
					System.out.println(Thread.currentThread().getName()+"------if_locka");

					synchronized(LockClass.lockb)//b锁
					{
					System.out.println(Thread.currentThread().getName()+"------if_lockb");
					}
				}
			}
		}
		else
		{
			while(true)
			{
				synchronized(LockClass.lockb)//b锁
				{
				  System.out.println(Thread.currentThread().getName()+"------else_lockb");

					synchronized(LockClass.locka)//a锁
					{
				   System.out.println(Thread.currentThread().getName()+"------else_locka");
					}
				}
			}
		}
	}
}

//定义两个锁
class LockClass
{
	static Object locka = new Object();
	static Object lockb = new Object();
}

class DeadLock
{
	public static void main(String[] args)
	{
		//创建2个进程,并启动
		new Thread(new LockTest(true)).start();
		new Thread(new LockTest(false)).start();
	}
}
8、线程间通信
其实就是多个线程在操作同一个资源,但是操作的动作不同。

/*
有一个资源
一个线程往里存东西,如果里边没有的话
一个线程往里取东西,如果里面有得话
*/

//资源
class Resource
{
	private String name;
	private String sex;
	private boolean flag=false;
	
	public synchronized void setInput(String name,String sex)
	{
		if(flag)
		{
			try{wait();}catch(Exception e){}//如果有资源时,等待资源取出
		}
		this.name=name;
		this.sex=sex;

		flag=true;//表示有资源
		notify();//唤醒等待
	}

	public synchronized void getOutput()
	{		
		if(!flag)
		{
			try{wait();}catch(Exception e){}//如果木有资源,等待存入资源
		}
		System.out.println("name="+name+"---sex="+sex);//这里用打印表示取出
				
		flag=false;//资源已取出
		notify();//唤醒等待
	}
}


//存线程
class Input implements Runnable
{
	private Resource r;
	Input(Resource r)
	{
		this.r=r;
	}
	public void run()//复写run方法
	{
		int x=0;
		while(true)
		{
			if(x==0)//交替打印张三和王羲之
			{
				r.setInput("张三",".....man");
			}
			else
			{
				r.setInput("王羲之","..woman");
			}
			x=(x+1)%2;//控制交替打印
		}
	}
}

//取线程
class Output implements Runnable
{
	private Resource r;
	Output(Resource r)
	{
		this.r=r;
	}
	public void run()//复写run方法
	{
		while(true)
		{
			r.getOutput();
		}
	}
}



class ResourceDemo2 
{
	public static void main(String[] args) 
	{
		Resource r = new Resource();//表示操作的是同一个资源

		new Thread(new Input(r)).start();//开启存线程

		new Thread(new Output(r)).start();//开启取线程
	}
}










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值