多线程

多线程

1.程序、进程和线程
1.1程序

Java源程序和字节码文件被称为“程序” (Program),是一个静态的概念。

1.2进程

执行中的程序叫做进程(Process),是一个动态的概念。进程是程序的一次动态执行过程, 占用特定的地址空间.
进程是资源分配的最小单位 (程序),一个进程可以包含1~n个线程 ,每个进程都是独立的,都有自己的代码和运行空间,进程之间的切换会有较大的开销。

1.3线程

线程是进程中一个“单一的连续控制流程” (a single sequential flow of control)/执行路径。
线程是cpu调度的最小单位,每个线程都有自己的运行栈和程序计数器,不同的线程之间可以有某种程度上的资源共享,线程之间切换开销小,所以线程又被称为轻量级进程(lightweight process)。
线程之间可以快速切换,达到同一份程序产生好几个进程的效果。

2.多线程优缺点

优点: 资源利用率更好;程序设计在某些情况下更简单;程序响应更快。

缺点: 线程之间的交互往往非常复杂。不正确的线程同步产 生的错误非常难以被发现,并且重现以修复。

区别进程线程
根本区别资源分配的单位调度和执行的单位
开销每个都有自己的代码和空间,进程之间切换有较大的开销。同一类线程共享代码和空间,每个线程都有自己的运行栈和程序计数器,线程切换的开销小
所处环境在操作系统中能同时运行多个任务在同一应用程序中有多个顺序流同时执行
分配内存系统在运行时为每个进程分配不同的内存区域。除了CPU之外不会为线程分配内存,线程使用的资源时它所属进程的资源,线程组只能共享资源。
包含关系一个进程可以拥有多个线程,没有线程的进程可以被看作单线程。线程是进程的一部分。
3.创建线程
3.1继承Thread ,重写run()方法,在方法内部定义线程体

缺点:如果类已经从一个类继承(如小程序必须继承自 Applet 类),则无法再继承

Thread 类 ,也无法实现资源共享。

public class TheadPractice extends Thread{

	@Override
	public void run() {
		for(int i=0;i<20;i++) {
			System.out.println("敲代码...........");
		}
	}

	public static void main(String[] args) {
		Thread th = new TheadPractice();
		th.start();//开启线程,不可以用th.run(),此为th在主线程中对run方法的调用,
		for(int i=0;i<20;i++) {
			System.out.println("唱歌...........");
		}
		//th.start();此时主线程已执行完毕,开启多线程没有意义。
	}
}
3.2通过Runnable接口实现多线程

优点:可以同时实现继承,可以实现资源共享。

package com.tyl.homework07011;

public class ThreadPractice02 {
	
	public static void main(String[] args) {
		//接口多态,run()方法在Test内部,需要通过Test对象调用run方法
		Runnable t = new Test();
		Thread th = new Thread(t);
		th.start();
		
		for(int i=0;i<20;i++) {
			System.out.println("一边笑");
			try {
				th.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
			
	}

}
class Test implements Runnable{

	@Override
	public void run() {
		for(int i=0;i<20;i++) {
			System.out.println("一边吃饭");
			try {
				Thread.sleep(400);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}	
}
3.3Runnable接口实现多线程资源共享案例

简单模拟12306购票,3个人重复不停地买同100张票。

package com.tyl.homework07011;

public class Case12306 implements Runnable{
	//设定票数为100张,此为A、B、C的共享资源
	int ticketsNum = 100;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			//票数为0时,线程结束
			if(ticketsNum<=0) {
				break;
			}
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"正在购买第"+ticketsNum--+"票");
		}
	}
	public static void main(String[] args) {
		Case12306 c = new Case12306();
		Thread th1 = new Thread(c,"A");
		Thread th2 = new Thread(c,"B");
		Thread th3 = new Thread(c,"C");
	
		th1.start();
		th2.start();
		th3.start();
		
	}
}

由于线程不安全,运行结果会出现一张票多个人买的情况。可以通过synchronized关键字解决。

4.线程的五种状态

1)、新生状态: new

new()的时候,线程处于新生状态。

2)、就绪状态: runnable

进入就绪状态的几种情况:

  • start()
  • 阻塞解除
  • 线程切换
  • yield() 礼让线程

3)、运行状态: running

cpu将资源分配线程的时候,线程进入运行状态。

4)、阻塞状态: blocked

进入阻塞状态的几种情况

  • sleep()
  • wait()
  • join()

5)、执行完毕: dead

进入终止状态的几种情况

  • stop destroy() 不推荐使用
  • 通过标识判断–推荐
  • 线程正常执行完毕
6. 阻塞状态(sleep/yield/join方法)
6.1sleep方法

在sleep休眠时间过程当中,会让出cpu的资源, 当休眠解除,即恢复到就绪状态,供cpu选择

  • 抱着资源睡觉,—> 对象资源(对象的锁),不会释放锁,别的线程也不可以访问锁定对象。

  • 作用:

    1.模拟 网络延迟

    2.放大问题的可能性

    案例:倒计时1~10

    package com.tyl.homework07011;
    
    public class CountDown implements Runnable{
    	@Override
    	public void run() {
    		System.out.println("开始倒计时喽........");
    		for(int i=10;i>0;i--) {
    			System.out.println(i);
    			try {
    				Thread.sleep(1000L);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}	
    	}
    	public static void main(String[] args) {
    		CountDown cd = new CountDown();
    		Thread th = new Thread(cd);
    		th.start();
    	}
    }
    
    6.2yield方法

    让出CPU的使用权,从运行态直接进入就绪态。让CPU重新挑选哪一个线程进入运行状态。

package com.tyl.homework07011;

public class YieldPractice implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		System.out.println(Thread.currentThread().getName()+"开始啦");
		Thread.yield();
		System.out.println(Thread.currentThread().getName()+"结束啦");
		
	}
	
	public static void main(String[] args) {
		YieldPractice yp = new YieldPractice();
		new Thread(yp,"A").start();
		new Thread(yp,"B").start();
	
}
}
6.3join方法

使用join方法时,另一个线程需要等待调用该方法的线程执行完毕后再往下继续执行。

package com.tyl.homework07011;

public class JoinPractice implements Runnable{
	
	public static void main(String[] args) {
		Runnable r = new JoinPractice();
		Thread th = new Thread(r);
		for(int i=0;i<10;i++) {
			System.out.println("main");
			if(i==5) {
				//进入就绪状态后才能执行join方法
				th.start();
				try {
					th.join();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
		}
		
	}

	@Override
	public void run() {
	
		System.out.println("A线程开始------------------");
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("A线程结束------------------");
	}

}
7.使用内部类、匿名内部类、Lambda开启线程
package com.tyl.homework07011;

public class LambdaTest {
	//内部类
	static  class InnerClass implements Runnable{

		@Override
		public void run() {
			// TODO Auto-generated method stub
			for(int i=0;i<20;i++) {
				System.out.println("内部类"+i);
			}
		
		}
		
		public static void main(String[] args) {
			Thread th1 = new Thread (new InnerClass());
			th1.start();
			
			//匿名内部类
			Thread th2 = new Thread(new Runnable(){
				public void run() {
					// TODO Auto-generated method stub
					for(int i=0;i<20;i++) {
						System.out.println("匿名内部类"+i);
					}
				
				}
				
			});
			th2.start();
			//Lambda
			new Thread( ()->{
				for(int i=0;i<20;i++) {
					System.out.println("Lambda"+i);
				}
			
			}).start();
8. synchronized同步
8.1 synchronized 方法

通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。

synchronized 方法控制对类成员变量的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该
方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得 该锁,重新进入可执行状态。

synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,代码范围小会出现锁不住对象的问题。

8.2 synchronized 块
通过** synchronized****关键字来声明synchronized 块。语法如下:
synchronized (sync Object) 
{ 
//允许访问控制的代码
} 
8.3用synchronized解决懒汉式单例线程不安全的问题
8.3.1同步newInstance方法
package com.tyl.homework07011;

public class SynchronizedPractice {
	public static void main(String[] args) {
		new Thread(()->{
			System.out.println(Single03.newInstance());
			
		}).start();
		
		new Thread(()->{
			System.out.println(Single03.newInstance());
			
		}).start();
		
		new Thread(()->{
			System.out.println(Single03.newInstance());
			
		}).start();
		
	}	 
}
class Single03{
	
	private static Single03 single03;
	private Single03() {
		super();
		// TODO Auto-generated constructor stub
	}
	public static  synchronized Single03 newInstance() {
		if(single03 == null) {
			single03 = new Single03();
		}
		return single03;
	}
}
8.3.2使用双重检查(double check)
package com.tyl.homework07011;

public class SynchronizedPractice {
	public static void main(String[] args) {
		new Thread(()->{
			System.out.println(Single03.newInstance());
			
		}).start();
		
		new Thread(()->{
			System.out.println(Single03.newInstance());
			
		}).start();
		
		new Thread(()->{
			System.out.println(Single03.newInstance());
			
		}).start();
		
	}	 
}
class Single03{
	
	private static Single03 single03;
	private Single03() {
		super();
		// TODO Auto-generated constructor stub
	}
	public static Single03 newInstance() {
		if(single03 == null) {//外层判断:当线程1走完了内层判断,对象实例化了,线程3也调用了
            //getInstace函数,如果没有外层的判断,线程3要继续等待线程2的完成,加上外层判断,就不
            //需要等待了,直接返回了实例化的对象,提高效率。
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//双重检查
			synchronized (Single03.class){//同步类,线程1到达这里,线程2、3等待
				if(single03 == null){//线程1发现single03为空,执行if语句,执行完退出后线程2进入。
                    //如果不加一次判断,会造成single03再次实例化,增加判断后,线程2发现已经single3
                    //已经被实例化直接跳过if语句
				single03 = new Single03();	
                }
			}
			
		}
		return single03;
	}
}
8.4 以3.3中模拟12306抢票为案例,说明synchronized使用的几种方法
8.4.1通过同步成员方法实现线程安全
package com.tyl.homework07011;
public class Sync01Case12306 implements Runnable{
	//设定票数为100张,此为A、B、C的共享资源
	int ticketsNum = 100;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true){
			//票数为0时,线程结束
			if(Check()) {
				break;
			}	
		}
	}
	public synchronized boolean Check() {
		if(ticketsNum<=0) {
			return true;
		}
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"正在购买第"+ticketsNum--+"票");
		return false;
	}
	public static void main(String[] args) {
		Sync01Case12306 c = new Sync01Case12306();
		Thread th1 = new Thread(c,"A");
		Thread th2 = new Thread(c,"B");
		Thread th3 = new Thread(c,"C");
	
		th1.start();
		th2.start();
		th3.start();
		
	}

}

8.4.2用synchronized块同步类的class对象
package com.tyl.homework07011;
/**
 * 	同步块------类 的Class对象,  唯一的不变的
 * 	 如果锁类,这个类的所有对象都锁住,因为类的Class对象只有一个
 * @author 86137
 *
 */
public class Sync02Case12306 implements Runnable{
	//设定票数为100张,此为A、B、C的共享资源
		int ticketsNum = 100;
		@Override
		public void run() {
			// TODO Auto-generated method stub
			while(true){
				//票数为0时,线程结束
				if(Check()) {
					break;
				}
				
			}
		}
		public  boolean Check() {
			synchronized (Sync02Case12306.class){
			if(ticketsNum<=0) {
				return true;
			}
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"正在购买第"+ticketsNum--+"票");
			return false;
			}
		}	
		public static void main(String[] args) {
			Sync02Case12306 c = new Sync02Case12306();
			Thread th1 = new Thread(c,"A");
			Thread th2 = new Thread(c,"B");
			Thread th3 = new Thread(c,"C");
		
			th1.start();
			th2.start();
			th3.start();
			
		}
}
8.4.3用synchronized块同步成员方法中当前正在运行的对象
package com.tyl.homework07011;
/**
 * 
 * 	同步块---this-->在成员方法中指代当前调用方法的对象
 * 	如果当前对象有多个资源,都被锁住,
 * @author 86137
 *
 */
public class Sync03Case12306 implements Runnable{
	//设定票数为100张,此为A、B、C的共享资源
		int ticketsNum = 100;
		@Override
		public void run() {
			// TODO Auto-generated method stub
			while(true){
				synchronized (this) {
				//票数为0时,线程结束
				if(ticketsNum<=0) {
					break;
				}
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"正在购第"+ticketsNum--+"票");
			}
			}
		}
		public static void main(String[] args) {
			Sync03Case12306 c = new Sync03Case12306();
			Thread th1 = new Thread(c,"A");
			Thread th2 = new Thread(c,"B");
			Thread th3 = new Thread(c,"C");
			th1.start();
			th2.start();
			th3.start();	
		}
}
8.4.4用synchronized块同步成员变量
package com.tyl.homework07011;
/**
 * 同步块---成员变量-->一定要为自定义的引用数据类型的对象地址
 * @author 86137
 *
 */
public class Sync04Case12306 implements Runnable{
	Tickets tickets = new Tickets();
	//设定票数为100张,此为A、B、C的共享资源
			int ticketsNum = 100;
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true){
					synchronized (tickets) {
					//票数为0时,线程结束
					if(ticketsNum<=0) {
						break;
					}
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"正在购买第"+ticketsNum--+"票");
				}
				}
			}
			public static void main(String[] args) {
				Sync04Case12306 c = new Sync04Case12306();
				Thread th1 = new Thread(c,"A");
				Thread th2 = new Thread(c,"B");
				Thread th3 = new Thread(c,"C");
				th1.start();
				th2.start();
				th3.start();	
			}
}
class Tickets{
	int ticketsNum = 100;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值