第一章:java多线程技能

1:进程和线程的概念
进程是受操作系统管理的基本运行单元,可以把进程列表中的.exe程序理解成一个进程。
线程是进程中独立运行的子任务,比如,QQ.exe在运行的时候会有很多的子任务在运行,比如,下载文件线程,传输数据线程,发送表情线程,这些都可以看成是QQ.exe进程的子任务。
线程是异步的,不是同步的,不用按照排队顺序执行,而是根据CPU安排。

2:使用多线程
2.1:继承Thread类
Thread类中的start方法是通知线程规划器,表示线程已经准备好了,等待调用线程对象的run()方法,让系统安排时间来调用Thread中的run()方法,具有异步的效果。如果使用thread.run()方法,那么就不是异步执行了,直接调用run方法是由main主线程来调用run()方法,这样就必须前面的代码执行完才能执行后面的代码。

2.2:实现Runnable接口
因为java不支持多继承,所以可以采用实现Runnable接口的方法得到一个线程。在Thread类的构造方法中Thread(Runnable target) 和Thread(Runnable target,String name)可以传递Runnable接口。

public class MyRunnable implements Runnable{
public void run(){
system.out.println("创建");
    }
 }
 public class Run(){
    public static void main(String[] args){
       Runnable runnable = new MyRunnable();
       Thread thread = new Thread(runnable);//传递一个Runnable接口对象,得到一个Thread对象
       thread.start();
       System.out.println("success");
       }
     }

另外,Thread类本身自己也实现了Runnable接口,这样说明Thread类可以传入一个Thread类的对象。这样就可以把一个Thread对象中的run方法交给其他线程调用。

2.3:实例变量和线程安全
共享数据的情况下,当多个线程访问同一个变量的时候,这时候可能会出现“非线程安全”。例如

在这里插入图片描述
这里面都是对同一个Thread对象进行调用,调用的是同一个run方法。产生的结果是
在这里插入图片描述
说明A和B同时对count进行了处理,产生了“非线程安全”,这种时候如果是对同一个变量进行访问的话,我们就要使多个线程之间进行同步。
使多个线程同步的方法:在run方法前加入synchronized关键字,使多个线程在执行run方法的时候,以排队的方式进行处理。synchronized可以在任意对象即方法上加锁,当一个线程要执行同步方法的代码时,先看这个方法有没有上锁,如果上锁了,说明其他线程正在调用这个方法,那么他只能等这个线程调用完它才能去拿到这个锁,去调用run方法。
特别注意:System.out.println()中的println方法中使用了synchronized关键字,是同步执行的。 要留意i–和println方法的联合使用时也会出现“非线程安全”,比如:

System.out.println("i =" + (i--);

其中i–是在println方法前执行的。

3:currentThread()方法
此方法可以返回代码段正在被哪个线程调用的信息。
下面注意一个特别重要的地方,this和Thread.currentThread的差异,看下面具体的代码。

public class Mythread extends Thread {

	public Mythread() {
		 System.out.println(Thread.currentThread().getName());
		 System.out.println(this.getName()); 
	}
	public void run() {
		 System.out.println(Thread.currentThread().getName());
		 System.out.println(this.getName());
	}
	public static void main(String[] args) {
		Mythread mythread = new Mythread();
		Thread t1  = new Thread(mythread);
		t1.setName("A");
		t1.start();
	}
}

得到的运行结果为:
在这里插入图片描述
因此,MyThread的构造函数是由main线程执行的,run方法是由A线程执行的。
为什么运行结果两个都是Thread-0呢?
因为代码 Thread t1 = new Thread(mythread);这里执行的是把mythread给了Thread的构造函数,得到一个Thread对象。引用了mythread,执行的是mythread里面的run方法。所以run方法中的this还是mythread对象,它并没有复写getName方法,所以调用的是父类的getName方法,返回的依旧是Thread-0。
我们来验证一下,如果它不是引用mythread对象,this.getName会不会是它自己线程的名字?

public static void main(String[] args) {
		Mythread t1 = new Mythread();
		t1.setName("A");
		t1.start();

得到的结果是:
在这里插入图片描述
很明显,返回的是它自己线程的名字。所以验证了上面的一点,如果将线程对象以构造参数的方式传递给Thread对象,那么this.getName返回的是引用的那个线程的名字。

4:isAlive(),sleep()方法,getid()方法
isAlive()方法的作用是测试线程是否处于活动状态,即线程已经启动尚未终止。
在使用isAlive方法时,如果线程对象是以参数的方式传递给Thread对象,跟上面的this和Thread.currentThread一样,存在差异。this指的是引用的那个线程。

sleep()方法:是在指定的毫秒数内让当前“正在执行的线程”(暂停执行),这个“正在执行的线程是this.currentThread()”返回的线程。这里就不会是引用的那个线程了,而是执行这个方法的线程。

getid():得到取得线程的唯一标识。

5:停止线程
5.1:停止不了的线程
使用interrupt()方法来停止线程,但是interrupt方法并不是立即停止运行,而仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程。使用了interrupt方法后面的代码依旧会执行。
5.2:判断线程是否是停止状态
1)this.interruptd()方法:测试当前线程是否已经中断,执行后具有将状态标志清除,置为false的功能。
2)this.isInterruptd()方法:测试线程是否已经中断,测试的是线程Thread对象是否已经中断状态,不清除状态。
这两个方法的区别:this.interruptd()方法:测试当前线程,当前线程指的是运行this.interrupted()方法的线程。看代码:

public class MyThread2 extends Thread {
         public void run() {
        	 super.run();
        	 for(int i = 0; i < 10; i++) {
        		 System.out.println("i = " + i);
        	 }
         }
         public static void main(String[] args) throws InterruptedException {
        	 MyThread2 mythread2 = new MyThread2();
        	 mythread2.start();
        	 Thread.sleep(1000);
        	 mythread2.interrupt();
        	 System.out.println(mythread2.interrupted());
        	 System.out.println(mythread2.interrupted());
         }
}

结果:
在这里插入图片描述
这里返回的都是false,很明显证明了上面的结论,这里使用的是interruptd()方法,判断的是当前线程,即执行这个方法的线程,这个线程是main线程,没有中断,并且第二次调用的时候,清除了之前的状态,即把其置为未中断状态,仍然返回了false。
this.isInterrupted()方法:测试的是Thread对象,不清除状态。

public class MyThread2 extends Thread {
         public void run() {
        	 super.run();
        	 for(int i = 0; i < 5; i++) {
        		 System.out.println("i = " + i);
        	 }
         }
         public static void main(String[] args) throws InterruptedException {
        	 MyThread2 mythread2 = new MyThread2();
        	 mythread2.start();
        	// Thread.sleep(1000);
        	 mythread2.interrupt();
        	 System.out.println(mythread2.isInterrupted());
        	 System.out.println(mythread2.isInterrupted());
         }
}

运行结果:
在这里插入图片描述
5.3:能停止的线程;
1)异常法:前面我们学了如何判断当前线程是否中断,如果停止状态,那么我们就不去执行后面的代码。的确理论上我们可以这样做,但是事实中结果并不是这样,看代码:

public class Run {
     public static void main(String[] args) {
    	 
	        ThreadTestThird thread = new ThreadTestThird();
	        thread.start();
			thread.interrupt();
     }
}

public class ThreadTestThird extends Thread {
	public void run() {
		super.run();
		for(int i = 0; i < 10; i++) {
			if(this.interrupted()) {
			System.out.println("end");
			break;
			}
			 System.out.println("i=" + i);
		}
		System.out.println("还是运行了!");

运行结果:
在这里插入图片描述
本应该在停止了线程后,不会再执行后面的代码,但是还是执行了后面的代码。这个时候我们可以使用抛出异常的办法来停止后面代码的执行。

public class ThreadTestThird extends Thread {
   public void run() {
	super.run();
	try {
		for(int i = 0; i < 10; i++) {
			if(this.interrupted()) {
			System.out.println("end");
			throw new InterruptedException();
			    }
			      System.out.println("i=" + i);
		       }
		          System.out.println("还是运行了!");
			} catch (InterruptedException e) {
				System.out.println("进catch了,不会执行下面的代码了");
				e.printStackTrace();
			}

运行结果:
在这里插入图片描述
2):在沉睡中停止
如果线程在sleep()状态下停止线程,会产生什么样的效果;

public class Run3 {
         public static void main(String[] args) throws InterruptedException {
        	 ThreadSleep  ts = new ThreadSleep();
        	 ts.start();
        	 Thread.sleep(200);
        	 ts.interrupt();
             System.out.println("end");
         }
}

public class ThreadSleep extends Thread {
       public void  run() {
    	   super.run();
    	   try {
    		System.out.println("run begin");
			Thread.sleep(20000);
			System.out.println("run end");
		} catch (InterruptedException e) {
			System.out.println("在沉睡中被停止!进入catch" + this.isInterrupted());
			e.printStackTrace();
		}
       }
}

运行结果:
在这里插入图片描述
根据结果可以得到,如果在sleep状态下停止一个线程,会进入catch语句,并且清除停止状态值,使其变为false。
反之,如果先interrupt停止线程再进入sleep状态依然会进入catch语句,不执行sleep后面的语句。

3) 使用stop()方法暴力停止(但是此方法已经过期作废);
如果强制让线程停止则有可能使一些清理性的工作得不到完成;另外就是对锁定的对象进行了“解锁 ”,会导致数据不同步。不建议使用stop()方法。
4)使用return停止线程
使用return+interrupt()方法可以实现停止线程的效果

public class ThreadReturn extends Thread {
	public void run() {
		while(true) {
			if(this.isInterrupted()) {
				System.out.println("end!");
				return ;
			}
			System.out.println("运行ing");
		}
	}
    public static void main(String[] args) throws InterruptedException {
    	ThreadReturn tr = new ThreadReturn();
    	tr.start();
    	Thread.sleep(2000);
    	tr.interrupt();
    }
}

运行结果:
在这里插入图片描述
还是建议使用“抛异常”的方法来实现线程的停止,异常流能更好的控制程序的运行流程,不至于使用很多个的return,造成污染。

6:暂停线程
suspend方法:暂停线程;resume:恢复线程(这两个方法也已经作废了,原因如下)
suspend与resume方法的缺点-----独占;即如果使用不恰当,极易造成公共的同步对象的独占,使其他线程无法访问公共同步对象。
例如:如果这个方法是上了锁的,两个线程都要调用这个公共方法,必须要前一个执行完后面的才能执行,比如println方法中就上了锁。看一个代码例子。

public class ThreadSuspend extends Thread {
	int i = 0;
	public void run() {
		while(true) {
			i++;
		  System.out.println(i);
		}
	}
   public static void main(String[] args) {
	   try {
		   ThreadSuspend ts = new ThreadSuspend();
		   ts.start();
		   Thread.sleep(1000);
		   ts.suspend();
		   System.out.println("end");
	} catch (InterruptedException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
   }
}

运行结果:
在这里插入图片描述
这里是不会输出main主线程中的那个“end”,因为println方法就有一个锁,当你把其中的一个线程暂停了的时候,main主线程就无法去访问println方法,也就不能输出“end”了。

另外一个缺点-----数据不同步
看例子:

public class MyObject {
        private String username = "1";
        private String password = "11";
        public void SetValue(String u,String p) {
        	this.username = u;
        	if(Thread.currentThread().getName().equals("a")) {
        		Thread.currentThread().suspend();
        	}
        	this.password = p;
        }
        public void print() {
        	System.out.println(username + " " +password);
        }
}

public class Run {
     public static void main(String[] args) throws InterruptedException {
    	  final MyObject my = new MyObject();
    	  //final MyObject test = new MyObject();
    	  Thread thread1 = new Thread() {
    		  public void run() {
    			  my.SetValue("a", "aa");
    		  };
    	  };
    	  thread1.setName("a");
    	  thread1.start();
    	  Thread.sleep(200);
    	  Thread thread2 = new Thread() {
    		  public void run() {
    			  my.print();
    		  };
    	  };
    	  thread2.start();
     }
}

运行结果:
在这里插入图片描述
数据不同步,原因是两个线程都是对同一个对象进行访问,而之前的那个线程赋予了对象的username属性后就暂停了,而password又没有改变,线程2进去后读取了线程1赋予的值,使数据产生了不同步。本应该得到的是a,aa。
7:线程的优先级
1)线程的优先级具有继承性,例如A线程启动B线程,则B线程的优先级与A的是一样的。
2)优先级具有规则性,可以使用setPriority()方法去设置线程的优先级,一般来说,高优先级的线程总是先执行完,谁先执行完与代码的调用顺序是无关的。可以看个例子:

public class MyThread2 extends Thread {
       public void run() {
			for(int i = 0; i < 5; i++) {
	        	   System.out.println("Thread2走了哈"); 
	    	   }
       }
}

public class MyThread extends Thread {
        public void run() {
        	for(int i = 0; i < 5; i++) {
            	System.out.println("Thread1走了哈");
        	}

        }
}

public class Run {
        public static void main(String[] args) throws InterruptedException {
        	for(int i = 0; i < 5; i++) {
        		 MyThread2 thread2 = new MyThread2();
	           	 thread2.setPriority(1);
	           	 thread2.start();
	           	 //Thread.sleep(5000);
	        	 MyThread thread1 = new MyThread();
	           	 thread1.setPriority(10);
	           	 thread1.start();
	           	
        	}
        	 
        }
}

运行结果:
在这里插入图片描述
先执行完的是thread2。说明谁先执行完和代码的调用顺序没有关系。

3)优先级具有随机性,不一定每次都是优先级高的先执行完。

8:守护线程
在java中有两种线程,一种是用户线程,一种是守护(Daemon )线程。守护线程是相当于幼儿园的保姆,当幼儿园的小朋友都回家了,也就不需要保姆了。当线程中没有了非守护线程,守护线程也就不自动销毁了。最经典的就是GC(垃圾回收器)的应用。
看个例子:

public class Run2 {
      public static void main(String[] args) {
    	  
    	  try {
    		  TestThread3 thread3 =  new TestThread3();
    		  //MyThread2  thread2 = new MyThread2();
    		  //thread2.setDaemon(true);
    		  thread3.setDaemon(true);//把它定义为守护线程
    	      thread3.start();
			  Thread.sleep(5000);
			  System.out.println("end");
			  //thread2.start();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	  
      }
}

public class TestThread3 extends Thread {
	int i = 0;
      public void run() {
    	  
               try {
            	   while(true) {
                       i++;
                       System.out.println(i);
				       Thread.sleep(1000);
            	   }
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
      }
}

运行结果:
在这里插入图片描述
当主线程全部都执行完了以后,守护线程thread3也就强制退出,不会再执行了。只要线程中还有非守护线程在执行,守护线程也会执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值