线程Thread类(创建线程、线程同步、死锁、线程通讯、线程的停止、守护线程、join 方法)——Java基础

1. 创建多线程

  进程: 正在执行的程序作为一个进程,进程负责内存空间的划分
  单核的CPU在一个时间只能执行一个应用程序,各个应用程序在抢CPU资源
 *  线程 Thread :  任何一个java程序,jvm在运行的时候都会创建一个main线程执行main方法中所有的代码
 *   一个java 应用程序至少有 2 个线程   jvm 创建的 一个 主线程  是负责 main 方法代码的执行,一个是垃圾回收器线程
 
 *   创建新执行线程方法一:
 1. 将类声明为 Thread 的子类。
 2.   该子类应重写 Thread 类的 run 方法。?   目的: 自定义线程的任务代码就写在run方法中,自定义线程负责了 run 方法
 3. 接下来可以分配并启动该子类的实例,创建Tread子类对象,并且调用 start 方法开启线程
 *       一个线程一旦开启,那么线程会执行 run 方法中的代码,  run 方法千万不能直接调用,直接调用run方法 就相当于调用一个普通方法而已,并没有开启新线程

public class test_1 extends Thread {   // 继承 Thread 类
	@Override
	public void run() {
		for(int i=0; i<5; i++) {			
			System.out.println("自定义线程: " + i);			
		}
	}	
	public static void main(String args[])   { 			   
		   test_1 test = new test_1();
		 //  test.run();   // 直接调用run方法 就相当于调用一个普通方法而已,并没有开启新线程
		   test.start();
		   for(int i=0; i<3; i++) {				
				System.out.println("main线程: " + i);				
			} 
	  }      
}
********************   output  *****************************
main线程: 0
自定义线程: 0
main线程: 1
自定义线程: 1
main线程: 2
自定义线程: 2

 *  创建新执行线程方法二:

 *    1.  自定义一个类实现runnable接口
 *    2.  实现runnable接口的run方法,把自定义线程的任务定义在run方法上
 *    3.  创建runnable 实现类对象
 *    4.  创建 Thread 类的对象,并且把Runnable实现类的对象作为实参传递。
 *    5.  调用Thread 对象的start 方法开启一个线程。
    
 *    问题一:  Runnable实现类的对象是线程对象吗?
 *            不是, 只是实现了Runnable 接口的对象而已。 只有Thread 或者   Thread 子类才是线程对象。    
 *    问题二:   为什么 把Runnable实现类的对象作为实参传递给 Thread 类的对象
 *             作用就是把Runnable实现类的对象的run方法
 *  源码:   

    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    } 

        推荐使用第二种:  实现 runnable 接口的,  因为 java单继承,多实现。

public class test_1 implements Runnable{	// 实现runnable接口
	public static void main(String[] args)  {			
		test_1  pTest_1 = new  test_1();	// 创建runnable 实现类对象		
	//创建 Thread 类的对象,并且把Runnable实现类的对象作为实参传递
                Thread thread =  new Thread(pTest_1, "付祖贤女侠"); 
		thread.start();	  // 调用Thread 对象的start 方法开启一个线程		 
			 /*for(int i =0; i<5; i++) {		
					System.out.println(Thread.currentThread().getName() + ": " + i);					
				} */			 
	}
	@Override
	public void run() {
			 /*for(int i =0; i<5; i++) {		
			       System.out.println(Thread.currentThread().getName() + ": " + i);					
		        } */
			System.out.println("this: " + this);
			System.out.println("当前线程: " + Thread.currentThread());	// 自定义线程的任务定义在run方法上	
		}		 	 
}
2.  线程常用的方法
  线程常用的方法:
 *     Thread(String name)      初始化进程的名字
 *     getName()                返回线程的名字
 *     setName(String name)     设置线程对象名
 *     sleep()                  线程睡眠指定的毫秒数, !!!静态方法!!!,哪个线程执行了这个代码,那么就是哪个线程睡眠。
 *     getPriority()            返回当前线程对象的优先级,默认线程的优先级是5
 *     setPriority(int newPriority)  设置线程的优先级 1~10,10为最大的优先级
 *     currentThread()          返回CPU正在执行的线程的对象,!!!静态方法!!!!
public class test_1 extends Thread { 
	public test_1 (String name) {
		super(name);
	}	
	@Override
	public void run() {	
		for(int i=0;i<5;i++) {			
			System.out.println(this.getName()+ ":  " + i);	
        // 为什么在这里不能抛出异常,只能捕获?? 	子类抛出异常必须小于或等于父类异常
/*		 try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}   */		
	}	
}
	public static void main(String args[]) throws Exception  {
	//	test_1 hh = new test_1();
	//	System.out.println("线程名称:  " + hh.getName());
		test_1 hhhh = new test_1("付祖贤");
	//	System.out.println("线程名称2:  " + hhhh.getName());
	//    hhhh.sleep(1000);  // 静态方法,是主线程在睡眠,因为这句代码是主线程执行
		hhhh.setName(" 女侠 ");		
		 hhhh.setPriority(10); // 优先级的数字越大,优先级越高,优先级的范围为: 1~10
		hhhh.start();
		
               for(int i=0;i<5;i++) {			
			System.out.println(Thread.currentThread().getPriority()+ ":  " + i);	
                }				
		Thread mainthread = Thread.currentThread();
		System.out.println("main:  " + mainthread.getName());		
		// 线程的优先级默认为5
		System.out.println("自定义的线程优先级: " + hhhh.getPriority());
		System.out.println("主线程优先级: " + Thread.currentThread().getPriority());		
		}		
	}
*************************    output     *************************************
5:  0
5:  1
 女侠 :  0
5:  2
 女侠 :  1
5:  3
 女侠 :  2
5:  4
 女侠 :  3
main:  main
 女侠 :  4
自定义的线程优先级: 10
主线程优先级: 5
3.  线程安全问题
什么情况下才可能出现线程安全问题???
 *        1. 存在两个或者两个以上的线程对象,而且线程之间共享一个资源
 *        2. 有多个语句操作共享资源
 *     
 解决方法: sun提供了线程同步机制,    java线程同步机制方式:
      方式一:  同步代码块,    格式:
                    synchronzied(锁对象){   需要被同步的代码......   }
            
             同步代码块要注意的事项:
 *                   1.   任意的一个对象都可以作为锁对象。
 *                   2.   在同步代码块中sleep方法,并不会释放锁对象。  
 *                   3.   只有真正存在线程安全问题的时候才使用同步代码块,否则会降低效率的
 *                   4.   多线程操作的锁对象必须是唯一共享的,否则无效  
例子: 模拟3个窗口同时在售100张票
class Saleticket extends Thread{
	static int num = 1000;   //  静态的成员变量,该数据在每个对象都会维护一份数据	
	static Object object = new Object();	//  用static修饰!	
	public Saleticket(String num) {
		super(num);
	}	
	@Override
	public void run() {
	    while(true) {
	    	synchronized (object) {     //   这个对象也得用static修饰!!!!
	    //  synchronized ("锁对象") {  	  // 这样也可以,字符串一旦创建,就不会再次创建
	    //  synchronized (new String ("锁对象")) {  		 // 这样也不行!!!
	    		if(num>0) {
		    		System.out.println(Thread.currentThread().getName() + "售出了第 " + num + " 号票");
		    		num--;
		    	}else {
		    		System.out.println("售罄了......");
		    		break;
		    	}	   
			}	    	    	
	    }
	}	
}
public class test_1 extends Thread{	
		 public static void main(String[] args)  {	
		 Saleticket  sale1  = new Saleticket("第1窗口");
		 Saleticket  sale2  = new Saleticket("第2窗口");
		 Saleticket  sale3  = new Saleticket("第3窗口");		
		 sale1.start();
		 sale2.start();
		 sale3.start();	 
	}		 	 
}
**********************   output    ************************************
......
第1窗口售出了第 979 号票
第1窗口售出了第 978 号票
第1窗口售出了第 977 号票
第3窗口售出了第 976 号票
第3窗口售出了第 975 号票
第3窗口售出了第 974 号票
.....
第3窗口售出了第 1 号票
售罄了......
售罄了......
售罄了......

方式二: 同步函数 使用synchronized修饰一个函数

注意事项:
 *    1.  如果是一个非静态的同步函数的锁对象是this对象,如果是静态的同步函数的锁对象是当前函数所属类的字节码文件(  class 对象)
 *    2.  同步函数的锁对象   

class Saleticket extends Thread{
	static int num = 6;   //  静态的成员变量,该数据在每个对象都会维护一份数据	
	static Object object = new Object();		
	public Saleticket(String num) {
		super(num);
	}	
	@Override
	public synchronized void run() {    // 同步函数 ,使用synchronized修饰一个函数
		  while(true) {
			    //	synchronized (object) {
			    		if(num>0) {
				    		System.out.println(Thread.currentThread().getName() + "售出了第 " + num + " 号票, 还剩下: " + (num-1) + "张");
				    		num--;
				    	}else {
				    		System.out.println("售罄了......");
				    		break;
				    	}	   
				//	}	    		
				}	    	 
	}		
  //  静态的函数-----》 函数所属类的字节码文件 ------》 Saleticket.class	
	public static synchronized void getnum() {   // 一个线程全做完			 
	}
}
public class test_1 extends Thread{	
		 public static void main(String[] args)  {	
		 Saleticket  sale1  = new Saleticket("第1窗口");
		 Saleticket  sale2  = new Saleticket("第2窗口");		 
		 sale1.start();
		 sale2.start();
	}		 	 
}
**************************    output    ******************************
第1窗口售出了第 6 号票, 还剩下: 5张
第2窗口售出了第 6 号票, 还剩下: 5张
第1窗口售出了第 5 号票, 还剩下: 4张
第2窗口售出了第 4 号票, 还剩下: 3张
第1窗口售出了第 3 号票, 还剩下: 2张
第2窗口售出了第 2 号票, 还剩下: 1张
第1窗口售出了第 1 号票, 还剩下: 0张
售罄了......
售罄了......

推荐使用: 同步代码
 *    1.  同步代码的锁对象由我们随意指定,同步函数的锁对象是固定的,不能由我们指定

 *    2.  同步代码可以很方便控制被同步代码的范围,同步函数必须是整个函数的所有代码都同步了!

4. 死锁现象

死锁现象原因: 存在两个或两个以上的线程;   存在两个或者两个以上的共享资源。  没有方案解决,避免发生!!!

class DeadLock extends Thread{
	public DeadLock(String name) {
		super(name);
	}	
	@Override
	public void run() {
		if("女侠".equals(Thread.currentThread().getName())) {
			synchronized ("遥控器") { //字符串常量池(String Pool)这部分也在方法区中,String Pool是JVM实例全局共享的,全局只有一个
				System.out.println(" 女侠--遥控器,---> 电池 ");
			
			synchronized ("电池") {
				System.out.println(" 女侠全都搞定 ");
			}
		  }	
		}else if ("仙女".equals(Thread.currentThread().getName())){			
			synchronized ("电池") {
				System.out.println(" 仙女--电池,---> 遥控器 ");			
			synchronized ("遥控器") {
				System.out.println(" 仙女全都搞定 ");
			}
		}
	  }
	}	
}
public class test_1 extends Thread{	
	public static void main(String[] args)  {	
		DeadLock person1 = new DeadLock("女侠");
		DeadLock person2 = new DeadLock("仙女");		
		person1.start();
		person2.start();
	}		 	 
}
5. 线程通讯

 线程通讯: 一个线程完成了自己的任务,要通知另外的一个线程去完成另外一个任务 

wait():   // 等待, 如果线程执行了 wait 方法,那么该线程就会进入等待的状态,必须要被其他的线程调用notify方法才能唤醒                //  调用wait方法,就会释放锁对象
notify():   //  唤醒,   唤醒以锁对象为标识符的线程,唤醒等待的线程 ,先等待的线程一般会先唤醒。
notifyAll():    唤醒线程池中所有的线程
注意事项:
     1.  wait方法 和 notify方法是属于object对象的; 锁对象是object对象     
     2.  两个方法必须要在同步块或者同步函数中才能使用; 因为需要锁对象调用,同步代码中有锁对象  
     3.  这两种方法需要由锁对象调用,同一个锁对象! 一个锁对象建立一个线程池 

例子: 生产者和消费者

class Product{            // 产品
	String name;
	double price;	
	boolean flag = false;   // 是否开始的标志
}
class Producter extends Thread{       // 生产者
	Product p;	
	public Producter(Product p) {
		this.p = p;
	}	
	@Override
	public void run() {
		int i = 0;
		for(int t= 0; t<100; t++) {
			synchronized (p) {      //   出现价格错乱????  需要同步
				if(p.flag ==false) {    //  消费者线程已经结束,生产线程开始进行
					if(i%2==0) {					
						p.name = "柠檬";		
						p.price = 66.6;
					}			
				   else {
					p.name = "百香果";
					p.price = 33.3;					
				  }
				 System.out.println("生产者生产出了: " + p.name + "  价格:  " + p.price);
				 p.flag = true;	
				 p.notify();    //  生产完,唤醒正在睡眠的消费者线程
				 i++;	
				}else {
					try {
						p.wait();   //  消费者线程正在进行,生产线程等待
					} catch (InterruptedException e) {						
  					   e.printStackTrace();
					}
				}			
			}				
	   }
	}	 
}
class Customer extends Thread{      // 消费者
	Product p;
	 public Customer(Product p) {
		this.p = p;
	}		
	@Override
	public void run() {
		for(int t= 0; t<100; t++) {
			synchronized (p) {         //  出现价格错乱????  需要同步
				if(p.flag ==true) {
				System.out.println(" 付祖贤减肥成功  "+ p.name + "  " + p.price);
				p.flag = false;
				p.notify();     
			  }else {
				try {
					p.wait();
				} catch (InterruptedException e) {					
					e.printStackTrace();
				}
			}			
		}
	}	
 }
}
public class test_1 {	
	 public static void main(String[] args)  {			
		Product p = new Product();    // 
		Producter producter = new Producter(p);
		Customer customer = new Customer(p);	 
		producter.start();
		customer.start();
	}			 
}
6. 线程的停止

     1.  停止一个线程,一般通过一个变量去控制。
     2.  停止一个等待状态的线程,需要用一个变量配合 notify() 或者   interrupt()

public class test_1 extends Thread{	
	 boolean flag= true;	 
	 public test_1(String name) {
		super(name);
	}	
	@Override
	public synchronized void run() {
		int i = 0;
		while(flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			 System.out.println(Thread.currentThread().getName()+ ": " + i);	
			 i++;
		 }
	}
	 public static void main(String[] args)  {				
		 test_1 tt = new test_1("付祖贤女侠");
		 tt.setPriority(10);
		 tt.start();		 
		 for(int i=0; i<6; i++) {
			 System.out.println(Thread.currentThread().getName()+ ": " + i);	
			 if(i==3) {
					//	 tt.stop();  // 
					//	 tt.interrupt();  // 无法停止线程			 
						 tt.flag=false; 
						 tt.interrupt();  // 把等待状态的线程清除,被清除的线程会收到 一个异常 InterruptedException
						/* synchronized (tt) {
							 tt.notify();  // 不能指定唤醒哪个线程
						}	*/	
			 }		 	 
		 }			 
	 } 		 
}			 
**************************    output  ********************************
main: 0
main: 1
main: 2
main: 3
main: 4
main: 5
java.lang.InterruptedException
付祖贤女侠: 0
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Unknown Source)
	at test.test_1.test_1.run(test_1.java:14)
7.  守护线程(后台线程)Daemon

 守护线程(后台线程): 在一个进程中如果只剩下了守护线程,那么守护线程也会死亡。

  一个线程都不会默认为守护线程。设置: setDaemon();

public class test_1 extends Thread {	
	public test_1(String name) {
		super(name);
	}	
	@Override
	public void run() {
		for(int i= 0; i<20;i++) {
			System.out.println("更新包目前下载了" + i + "%");
			if(i==20) {
				System.out.println("更新包下载完毕");	
			}
			try {
				this.sleep(100);
			} catch (InterruptedException e) {				
				e.printStackTrace();
			}
		}		
	}
	public static void main(String[] args) {	
		test_1 tt = new test_1("后台线程");  	
                tt.setDaemon(true);   //  怎样成为守护线程  ??  main 线程结束之后,守护线程也死亡!     
		System.out.println("是守护线程吗? " + tt.isDaemon());   // 怎样判别守护线程??		
		tt.start();		
		for(int i= 0; i<20;i++) {
			System.out.println(Thread.currentThread().getName() + i);	
		}			
	}	  
}	
8.  join 方法
join 方法  : 一个线程执行好了join()语句,就有新的线程执行,执行该语句的线程必须要让步给新加入的线程先完成任务,然后才能继续执行
class  Washing extends Thread{     //  washing类
	@Override
	public void run() {
		System.out.println("付祖贤刷牙");
		System.out.println("付祖贤洗脸");
		System.out.println("付祖贤补水,发现喷雾没有了");
		Spray spray = new Spray();
		spray.start();
		try {
			spray.join();    // 加入, 由 washing类线程 来执行
		} catch (InterruptedException e) {			
			e.printStackTrace();
		}
		System.out.println("付祖贤涂乳液");
		System.out.println("付祖贤涂隔离霜");
		System.out.println("付祖贤涂防晒霜");			
	}	
}
class Spray  extends Thread{	  //  喷雾类
        @Override
	public void run() {		
		System.out.println("那就找屈臣氏的那瓶水");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {			
	     	e.printStackTrace();
		}
		System.out.println("忘了那瓶水在哪?");
		System.out.println("那瓶水在搁架上");
		System.out.println("在搁架上找到了那瓶水");	
	}
}
public class test_1  {		
	public static void main(String[] args) {		
		Washing washing = new Washing();
		washing.start();
	}	 	
}	
**************************   output   **************************************
付祖贤刷牙
付祖贤洗脸
付祖贤补水,发现喷雾没有了
那就找屈臣氏的那瓶水
忘了那瓶水在哪?
那瓶水在搁架上
在搁架上找到了那瓶水
付祖贤涂乳液
付祖贤涂隔离霜
付祖贤涂防晒霜

//\\

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值