Java多线程

1. 多线程:如果一个程序的执行路径有多条.
     单线程:程序的执行路径只有一条

2.面试题:

                Jvm(java虚拟机)是多线程程序吗?
  是多线程程序,由于java虚拟机中自带一个垃圾回收器,来确保程序不会轻易的造成内存溢出!
  至少开启两条子线程:a、当前程序在执行代码的时候,会开启main:主线程
                                              b、 垃圾回收器会开启一个垃圾回收线程,来确保程序不会内存异常,将不用的变量或者没有更多引用的对象来回收掉!

3.多线程的三种实现方式:

方式1:
  1)自定一个类:MyThread 继承自Thread类
  2)在MyThread类中重写Thread类中的run()  :为什么重写run()
  3)在主线程中,创建该类的实例对象

  4)启动线程

注意:

1)启动线程使用start(),调用run()相当于调用普通方法,start()的调用其实质是通过jvm调用的底层run()方法

2)一个线程不能启动多次,否则非法线程异常


/**
 * 
 */
package org.zst.thread;

/**
 * @author:zsyht
 * @date:2017-12-8上午6:59:45
 */
public class demo extends Thread {
	@Override
	public void run() {
//		System.out.println("helloworld");
		//耗时的操作,线程睡眠,线程等待,循环语句
		for(int x = 0 ; x <100 ; x ++){
			System.out.println(x);
		}
	}
}
/**
 * 
 */
package org.zst.thread;

/**
 * @author:zsyht
 * @date:2017-12-8上午7:02:40
 */
public class Test {

	public static void main(String[] args) {
		
		//创建该线程的实例对象		
		//启动线程
		/**
		 * 启动线程不是调用run()方法,
		 * strat()是线程开始执行的方法
	     run()方法调用相当于调用了一个普通方法,并不会出现线程随机性;而start()方法调用,
		 其实是通过Jvm调用线程中的run()来进行多个线程抢占CPU执行权
		 */
//		my.start() ;
		//0java.lang.IllegalThreadStateException
		/**
		 * 第二次启动线程,会出现:非法线程状态异常
		 * 当前线程已经启动了,不能重新启动
		 */
		demo d1 = new demo() ;
		demo d2 = new demo() ;
		
		//分别启动线程
		d1.start() ;
		d2.start() ;
	}
}


* 如何获取线程的名称?
 public final String getName()返回该线程的名称。 
 设置线程名称
 public final void setName(String name)改变线程名称,使之与参数 name 相同。 

/**
 * 
 */
package org.zst.thread2;

/**
 * @author:zsyht
 * @date:2017-12-8上午7:07:42
 */
public class demo extends Thread{
	//无参构造
	public demo(){
		
	}
	//有参构造
	public demo(String name){
		super(name) ;
	}
	@Override
	//d1和d2子线程都会执行这段代码,两个子线程在互相抢占CPU的执行权
	public void run() {
		for(int x = 0 ; x <200; x ++){
			System.out.println(getName() +":"+x);
		}
	}
}
/**
 * 
 */
package org.zst.thread2;

/**
 * @author:zsyht
 * @date:2017-12-8上午7:21:32
 */

import java.security.AccessControlContext;
import java.security.AccessController;
public class Test {		
		public static void main(String[] args) {
			//创建线程类的实例
			/*
			 * 设置线程名称方式:无参+setName(String name)方法
			 * */
			demo d1 = new demo() ;
			demo d2 = new demo() ;
			
			// public final void setName(String name)改变线程名称,使之与参数 name 相同。
			d1.setName("左舒婷") ;
			d2.setName("李赟琪") ;
			
			//public static Thread currentThread():表示正在运行的线程
			//默认所有的子线程的都在主线程中
			System.out.println(Thread.currentThread().getName());//main
			
			//启动线程
			d1.start() ;
			d2.start() ;
		}
	}


public final void join():
           抛出异常throws InterruptedException等待该线程终止。 

/**
 * 
 */
package org.zst.thread3;

/**
 * @author:zsyht
 * @date:2017-12-8上午8:03:53
 */
public class demo extends Thread {
		@Override
		public void run() {
			for(int x = 0 ; x <100 ; x ++){
				System.out.println(getName()+":"+x);
			}
		}
	}
/**
 * 
 */
package org.zst.thread3;

/**
 * @author:zsyht
 * @date:2017-12-8上午8:05:32
 */
public class Test {
public static void main(String[] args) {
		
		/*
		 * 创建该线程的实例对象
		 * */
		demo d1 = new demo() ;
		demo d2 = new demo() ;
		demo d3 = new demo() ;
		
		//设置线程名称
		d1.setName("成都") ;
		d2.setName("重庆") ;
		d3.setName("上海") ;
		
		//启动线程
		d1.start() ;
			
		//设置线程等待该线程终止该方法必须要启动线程
		try {
			d1.join() ;
		} catch (InterruptedException e) {
			//InterruptedException:中断异常
			e.printStackTrace();
		}
		
		d2.start() ;
		d3.start() ;
	}
}


public static void yield()暂停当前正在执行的线程对象,并执行其他线程。
   暂停当前线程执行其他线程,并不保证另一个线程就一定能抢占到CPU的执行权
/**
 * 
 */
package org.zst.thread4;

/**
 * @author:zsyht
 * @date:2017-12-8上午8:13:04
 */
public class demo extends Thread{
	@Override
	public void run() {
		//yt1,yt2
		for(int x =0 ; x <100 ; x ++){
			System.out.println(getName()+":"+x);
			Thread.yield() ;
		}
	}
}
/**
 * 
 */
package org.zst.thread4;

/**
 * @author:zsyht
 * @date:2017-12-8上午8:13:28
 */
public class Test {
public static void main(String[] args) {
		
		//创建线程类对象
		demo d1 = new demo() ;
		demo d2 = new demo() ;
		
		//设置线程名称
		d1.setName("左舒婷") ;
		d2.setName("李赟琪") ;
		
		//启动线程
		d1.start() ;
		d2.start() ;
	}

}
 public final void setDaemon(boolean on)  on指定true,就是设置守护线程...
   将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。 
该方法必须在启动线程前调用。

jvm自动退出,对于主线程的数据如果直接输出完毕,对于两个守护线程来说不会立即消失,Jvm等会就自动退出.

/**
 * 
 */
package org.zst.thread5;

/**
 * @author:zsyht
 * @date:2017-12-8上午8:16:49
 */
public class demo extends Thread {
	@Override
	public void run() {
		for(int x = 0 ; x <100 ; x ++){
			System.out.println(getName()+":"+x);
		}
	}
}
/**
 * 
 */
package org.zst.thread5;

/**
 * @author:zsyht
 * @date:2017-12-8上午8:25:06
 */
public class Test {
public static void main(String[] args) {
		
		//创建线程类对象
		demo d1 = new demo() ;
		demo d2 = new demo() ;
		
		//设置线程名称
		d1.setName("张飞") ;
		d2.setName("关羽") ;
		
		//setDaemon(boolean on)
		d1.setDaemon(true) ;
		d2.setDaemon(true) ;
		
		
		//启动线程
		d1.start() ;
		d2.start() ;
		
		Thread.currentThread().setName("刘备") ;
		for(int x = 0 ; x <5 ; x++){
			System.out.println(Thread.currentThread().getName()+":"+x); //设置主线程
		}
	}

}

 线程睡眠:public static void sleep(long millis)
                  throws InterruptedException在指定的毫秒数内让当前正在执行的线程休眠(暂停执行

 线程停止:
   public final void stop():强迫线程停止执行
   public void interrupt()中断线程。 表示中断线程一种状态
  
  join()
  yield()
  stop()
  setDeamon(boolean  on):(用的多)
  sleep():线程睡眠 (用的多)

  wait():线程等待

多线程实现方式二(实际开发中第二种方式比第一种应用更加广泛,单继承具有局限性,该方法更符合Java的设计原则:数据分离原则):

 a、 自定义一个类,该类实现Runnable接口

 b、实现该接口中的run()方法

 c、 在主线程中创建该类的实例对象

 d、创建Thread对象,将3)创建的对象作为参数传递

 e、分别启动线程

/**
 * 
 */
package org.zst_06;

/**
 * @author:zst
 * @date:2017-12-24下午1:21:56
 */
public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for(int i =0;i<100;i++){
			//此时获取线程名称不能用getName方法,getName只能在Thread中使用,此时使用会报错
			//getName()方法在Runnable接口中没有定义
			System.out.println(Thread.currentThread().getName()+":"+i);
			//间接使用Thread类的静态功能获取线程名称
			
		}

	}

}
/**
 * 
 */
package org.zst_06;

/**
 * @author:zst
 * @date:2017-12-24下午1:33:43
 */
public class ThreadDemo {
	public static void main(String [] args){
		MyRunnable my = new MyRunnable();
		//设置线程名称public Thread(Runnable target,String name)
		Thread t1 = new Thread(my,"线程1");
		Thread t2 = new Thread(my,"线程2");
		//启动线程
		t1.start();
		t2.start();
		
	}

}



面试题:多线程的实现方式有几种?

面试过程中答三种,口头回答两种


模拟电影院窗口售票

方式一:继承自Thread类

/**
 * 
 */
package org.zst_07;

/**
 * @author:zst
 * @date:2017-12-24下午2:23:30
 */
public class SellTicekts extends Thread {
	//定义100张票
	//这100张票应该被三个线程共用,所以用static修饰
	private static int tickets = 100 ;
	@Override
	public void run() {
		//有100张票
		//模拟该电影院售票:电影院一直有票
		
		while(true){
			
			if(tickets>0){
				System.out.println(getName() + "正在出售第" + (tickets--) + "张票");
			}
		}
	}
}
/**
 * 
 */
package org.zst_07;

/**某电影院目前正在上映贺岁大片(红高粱,少林寺传奇藏经阁),共有100张票,而它有3个售票窗口售票,请设计一个程序
      模拟该电影院售票。
      方式一:继承自Thread类
 * @author:zst
 * @date:2017-12-24下午2:20:01
 */
public class SellTicketsDemo {
	
	public static void main(String[] args) {
		
		//创建线程类对象
		SellTicekts st1 = new SellTicekts() ;
		SellTicekts st2 = new SellTicekts() ;
		SellTicekts st3 = new SellTicekts() ;
		
		//分别命名三个窗口
		st1.setName("窗口1") ;
		st2.setName("窗口2") ;
		st3.setName("窗口3") ;
		
		//启动线程
		st1.start() ;
		st2.start() ;
		st3.start() ;
	}
}

方式二:实现Runnable接口

/**
 * 
 */
package org.zst_09;

/**
 * @author:zst
 * @date:2018-1-15下午5:45:40
 */
public class SellTicket implements Runnable {

	//100张票被共用,不让外界修改数据
	private  static int tickets = 100 ;
//	@Override
//	public void run() {
//		
//		// 模拟电影院一直有票
//		while (true) {
//			
//			//设置线程睡眠0.1m
//			
//			if (tickets > 0) {
//				//ts1,ts2,ts3
//				try {
//					Thread.sleep(100) ;
//				} catch (InterruptedException e) {
//					e.printStackTrace();
//				}
//				System.out.println(Thread.currentThread().getName() + "正在出售第"
//						+ (tickets--) + "张票");
//			}
//		}
//	}
	
	//一张票可能会被卖多次,(加入延迟操作之后)
//	@Override
//	public void run() {
//		
//		// 模拟电影院一直有票
//		while (true) {
//			
//			//设置线程睡眠0.1m
//			
//			if (tickets > 0) {
//				//ts1,ts2,ts3
//				try {
//					Thread.sleep(100) ;
//				} catch (InterruptedException e) {
//					e.printStackTrace();
//				}
//				System.out.println(Thread.currentThread().getName() + "正在出售第"
//						+ (tickets--) + "张票");
//			}
			/**
			 * 理想状态:
			 * 	窗口1正在出售第100张票
			 *  窗口2正在出售第99张票
			 *  ...
			 *  
			 *  多个线程在抢占CPU的执行权,CPU的一点点时间片具有特性:原子性操作(最简单最基本):比如变量++或者--
			 *  窗口1正在出售100张票
			 *  ticketss--,之前的动作:记录之前的值100(窗口2)
			 *  窗口2在记录值的时候抢占cPU的执行权,打印-->窗口也正在出售第100张票
			 *  -->99
			 */
	//	}
	@Override
	public void run() {
		
		// 模拟电影院一直有票
		while (true) {
			
			//设置线程睡眠0.1m
			//ts1,ts2,ts3
			try {
				Thread.sleep(100) ;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			if (tickets > 0) {
				System.out.println(Thread.currentThread().getName() + "正在出售第"
						+ (tickets--) + "张票");
			}
			/**
			 * 当前代码出现了负票
			 * 理想状态:
			 * 		窗口1正在出售第3张票,tickets--
			 * 		窗口3正在出售第2张票,tickets--
			 * 		窗口2正在出售第1张票....
			 * 	
			 * 加入延迟操作后,线程执行具有随机性(延迟操作和随机性)
			 * 		窗口2正在出售第0张票..tickets--
			 * 		窗口3正在出售第-1张票...
			 */
		}
	}

}
/**
 * 
 */
package org.zst_09;

/**
 * @author:zst
 * @date:2018-1-15下午5:46:29
 */
public class SellTicketDemo {
public static void main(String[] args) {
		
		//创建SellTicket对象
		SellTicket st = new SellTicket() ;
		
		//创建线程类的对象
		Thread t1 = new Thread(st, "窗口1") ;
		Thread t2 = new Thread(st, "窗口2") ;
		Thread t3 = new Thread(st, "窗口3") ;
		
		//启动线程
		t1.start() ;
		t2.start() ;
		t3.start() ;
	}
}




为了模拟电影院卖票更真实的场景,每一个窗口卖票应该延迟操作

在接口自实现类中,在run()方法中让每一个线程执行睡眠0.1秒 
 加入延迟操作:
  1)一张票可能被卖多次
  100张票会出现多次
  CPU的执行具有原子性操作
  2)可能出现负票
  1,0,-1
  延迟操作和线程随机性导致
 
 检验多线程安全问题的标准(以后在判断一个多线程序是否有安全问题的标准)
  1)当前是否是一个多线程环境
  2)多线程环境中是否有共享数据
  3)是否有多条语句对共享数据进行操作
 
 就目前这个电影院卖票这个案例,
  1)是多线程环境,因为要实现3个窗口同时卖票
  2)有共享数据,比如:tickets
  3)有多条语句对当前这个共享数据进行操作
 
 优化改进:
  1)多线程环境和共享数据改变不了,突破口就是3)条件:将多条语句对共享数据的操作进行更改
 
  将多条语句对共享数据进行操作的代码用代码块包起来
  Java的同步机制:
  使用同步代码块:synchronized(同步锁对象){
  多条语句对共享数据的操作;
  }
 
 同步锁对象:应该每一个线程都要使用这个锁对象(同步锁):理解为门的开和关
 使用同步代码块可以解决线程安全问题
 
/**
 * 
 */
package org.zst_08;

/**
 * @author:zst
 * @date:2018-1-15下午5:29:55
 */
public class SellTicket implements Runnable {

	//100张票被共用,不让外界修改数据
	private  static int tickets = 100 ;
	
	//同步锁对象
	private Object obj = new Object() ;
	
	private Demo d = new Demo() ;
	@Override
	public void run() {
		
		// 模拟电影院一直有票
		while (true) {
			
			//设置线程睡眠0.1m
			//ts1,ts2,ts3
			//相当于ts1,ts2,ts3创建自己的锁定(不是同一把锁)
//			synchronized(new Object()){
			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--) + "张票");
				}
			}//ts1出来了
			
		}
	}

}
class Demo{
	
}
/**
 * 
 */
package org.zst_08;

/**
 * @author:zst
 * @date:2018-1-15下午5:30:47
 */
public class SellTicketDemo {
public static void main(String[] args) {
		
		//创建SellTicket对象
		SellTicket st = new SellTicket() ;
		
		//创建线程类的对象
		Thread t1 = new Thread(st, "窗口1") ;
		Thread t2 = new Thread(st, "窗口2") ;
		Thread t3 = new Thread(st, "窗口3") ;
		
		//启动线程
		t1.start() ;
		t2.start() ;
		t3.start() ;
	}
}

同步锁定对象:
  1)可以Object类型以及任意的Java类型对象
   2)如果一个方法进来之后是一个同步代码块,那么同步代码块可以演变成一个同步方法
   3)如果是一个静态的同步方法,锁对象是当前类名class属性:类名.class (反射机制:获取一些类的字节码文件对象Class类对象)
    
 面试题:
  wait()线程等待,notify(),唤醒单个线程,notifyAll():唤醒所有线程这三个方法为什么不定义到Thread类中而是定义在Object类中?
  线程中会存在安全问题,并且解决线程安全问题使用的同步代码块或者同步方法来解决,同步代码块来解决线程安全问题,就存在同步锁对象,谁能代表同步锁对象(Object以及任意的Java类),把它定义到Object类中;
  等待唤醒机制(生产者和消费者线程)

 线程安全的类有哪些?
   Vector
  StringBuffer

  Hasttable

/**
 * 
 */
package org.zst_09;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;

/**
 * @author:zst
 * @date:2018-1-15下午5:54:55
 */
public class ThreadDemo {

		public static void main(String[] args) {
			//1)集合里面的Vector集合
			Vector<String> v = new Vector<String>() ;
			
			//2)字符串缓冲区
			StringBuffer sb = new StringBuffer("StringHello") ;
			
			//3)Map集合中的子实现类:Hashtable
			Hashtable<String, String> hm = new Hashtable<String,String>() ;
			
			//从集合中来看,vector集合是一个安全的类,实际开发也不怎么用它
			//Collections:针对集合操作的工具类
			//public static <T> List<T> synchronizedList(List<T> list)返回指定列表支持的同步(线程安全的)列表
			ArrayList list = new ArrayList() ;
			List l = Collections.synchronizedList(list) ; //安全的集合列表
		}
	}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值