Java中的多线程(一)关于线程的基本操作
来源:互联网 发布:51单片机怎么烧程序 编辑:程序博客网 时间:2024/05/29 04:29
Java中的多线程
进程:进程是操作系统的基础,是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的活动;是程序在一个数据集合上的运行过程,它是系统进行资源分配和调度的一个独立单位。 线程:线程可以理解为是在进程中独立运行的子任务。当一个类中需要用到多线程时,一方面可以直接继承Thread类,另一方面可以去实现Runnable接口,这两种方法的作用其实是一样的,因为Thread类本身实现了Runnable接口,不过鉴于Java是单继承的特性,所以在这里,建议大家通过第二种方法去使用多线程。
获取当前线程名称的方法:Thread.currentThread().getName()
取得线程的唯一标识:getId()方法
线程放弃当前CPU资源:yield()方法,将此机会让给其他的任务去占用CPU执行时间。
多线程的一些特点 <咱们看例子说话>
public class Thread1Test extends Thread{ @Override public void run() { // TODO Auto-generated method stub System.out.println("MyThread"); } public static void main(String[] args) { Thread1Test thread1Test=new Thread1Test(); //开启线程,实现异步操作,如果直接调用run(),则不会产生异步的效果 thread1Test.start(); System.out.println("fdjvdfdkljg"); }}
其运行结果为:
fdjvdfdkljg
MyThread
1.代码的运行结果与代码执行顺序或调用顺序是无关的,这种执行的随机性表现出CPU执行哪个线程具有不确定性。
public class Thread1Test extends Thread{ private int i; public Thread1Test(int i){ this.i=i; } @Override public void run() { // TODO Auto-generated method stub System.out.println(i); } public static void main(String[] args) { Thread1Test thread1Test1=new Thread1Test(1); Thread1Test thread1Test2=new Thread1Test(2); Thread1Test thread1Test3=new Thread1Test(3); Thread1Test thread1Test4=new Thread1Test(4); Thread1Test thread1Test5=new Thread1Test(5); Thread1Test thread1Test6=new Thread1Test(6); thread1Test1.start(); thread1Test2.start(); thread1Test3.start(); thread1Test4.start(); thread1Test5.start(); thread1Test6.start(); }}
其执行结果为:
1
4
5
6
2
3
2. 当一个程序中存在多个start()方法时,执行start()方法的顺序不代表线程启动的顺序。
3. 自定义线程类中的实例变量针对其他线程可以有共享与不共享之分,这个特点在多线程之间进行交互时是很重要的一个技术点。
1)不共享数据
public class Thread1Test extends Thread{ private int count=5; public Thread1Test(String name){ this.setName(name); } @Override public void run() { // TODO Auto-generated method stub while(count>0){ count--; System.out.println("由"+this.currentThread().getName()+ "计算,"+"count="+count); } } public static void main(String[] args) { Thread1Test thread1Test1=new Thread1Test("A"); Thread1Test thread1Test2=new Thread1Test("B"); Thread1Test thread1Test3=new Thread1Test("C"); thread1Test1.start(); thread1Test2.start(); thread1Test3.start(); }}
执行结果为:
由B计算,count=4
由C计算,count=4
由A计算,count=4
由C计算,count=3
由B计算,count=3
由C计算,count=2
由A计算,count=3
由C计算,count=1
由B计算,count=2
由C计算,count=0
由A计算,count=2
由A计算,count=1
由A计算,count=0
由B计算,count=1
由B计算,count=0
- 从结果中我们会发现,对于每一个线程,都会有独立的一个count,这种情况就是数据不共享的情况。
2)共享数据
public class Thread1Test extends Thread{ private int count=5; @Override public void run() { // TODO Auto-generated method stub while(count>0){ count--; System.out.println("由"+this.currentThread().getName()+ "计算,"+"count="+count); } } public static void main(String[] args) { Thread1Test thread1Test1=new Thread1Test(); Thread t1=new Thread(thread1Test1, "A"); Thread t2=new Thread(thread1Test1, "B"); Thread t3=new Thread(thread1Test1, "C"); t1.start(); t2.start(); t3.start(); }}
运行结果为:
由B计算,count=3
由A计算,count=3
由B计算,count=2
由A计算,count=1
由B计算,count=0
- 从结果中我们会发现,A与B同时对count进行了操作,所以count=3,一共出现了两次,这里也就产生了非线程安全的问题(
- 非线程安全主要是指多个线程对同一个对象的同一个变量进行操作,使得变量的值不能够同步的情况)这里其实我们会发现其实count数据已经被三个线程所共享了,但由于出现了非线程安全的问题,所以此时的结果离我们想要的最终结果(count 由5到0依次递减)是有一定的差距。 为此,我们对程序进行了修改,如下所示:
public class Thread1Test extends Thread{ private int count=5; @Override public synchronized void run() { // TODO Auto-generated method stub count--; System.out.println("由"+this.currentThread().getName()+ "计算,"+"count="+count); } public static void main(String[] args) { Thread1Test thread1Test1=new Thread1Test(); Thread t1=new Thread(thread1Test1, "A"); Thread t2=new Thread(thread1Test1, "B"); Thread t3=new Thread(thread1Test1, "C"); t1.start(); t2.start(); t3.start(); }}
通过在run方法前加synchronized关键字,使得多个线程在执行run方法时,以排队的方式进行处理。当一个线程调用r9un方法前,先判断run方法有没有被上锁,如果上锁,说明其它线程正在调用run方法,此线程必须等其它线程对run方法调用结束后才能执行run方法。这样就实现了排队调用run方法的目的,也就达到了按顺序对count进行减一的效果。synchronized关键字可以在任何对象和方法上加锁,而加锁的这段代码被称为“互斥区”或者“临界区”。
—————————————————————————
有一点需要注意的是,线程操作如果放在System.out.println()中执行的话,会产生非线程安全的问题
public class Thread1Test extends Thread{ private int count=5; @Override public void run() { // TODO Auto-generated method stub System.out.println("由"+this.currentThread().getName()+ "计算,"+"count="+(count--)); } public static void main(String[] args) { Thread1Test thread1Test1=new Thread1Test(); Thread t1=new Thread(thread1Test1); Thread t2=new Thread(thread1Test1); Thread t3=new Thread(thread1Test1); Thread t4=new Thread(thread1Test1); Thread t5=new Thread(thread1Test1); Thread t6=new Thread(thread1Test1); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start(); }}
运行结果为:
由Thread-1计算,count=5
由Thread-6计算,count=2
由Thread-4计算,count=3
由Thread-3计算,count=4
由Thread-2计算,count=5
由Thread-5计算,count=1
从结果上我们会发现,非线程安全的问题又一次发生了,为什么会有这种情况呢?看过源码的人都知道println方法中有synchronized关键字修饰,是线程安全的,那为什么还会产生这种错误呢?让我们再仔细看看println方法的源码,看完之后你就懂了。
public void println(String x) { synchronized (this) { print(x); newLine(); } }
原来println方法的synchronized关键字是在方法体的内部,而我们上边变量的改变是在println方法的参数部分。所以产生了这种非线程安全的情况。
—————————————————————————
线程的停止
停止线程是在多线程开发时很重要的技术点。停止线程意味着在线程处理完任务之前停止掉正在做的操作,也就是放弃当前的操作。停止一个线程需要用到Thread.stop()方法或者Thread.interrupt()方法,Thread.stop()方法确实可以停止一个正在运行的线程,但是这个方法是不安全的,而且是已被弃用的。而大多数停止一个线程的操作使用的是Thread.interrupt()方法,这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。单独调用interrupt()方法仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程。
在Java中一共有三种方法可以终止正在运行的线程:
* 使用退出标志,使线程正常退出,也就是当run方法完成之后线程终止。
* 使用stop方法强行终止线程,但是不推荐使用这个方法,因为stop和suspend及resume方法一样,都是作废过期的方法,使用它们可能产生不可预料的后果。
* 使用interrupt方法中断线程。
1.判断线程是否是停止状态
Thread.java类里提供了两种方法:
1)this.interrupted():测试当前线程(运行此方法的线程)是否已经停止。
2)this.isInterrupted():测试线程是否已经中断。
1.对于this.interrupted()方法,此方法是static的,如果当前的线程处于中断状态,那么调用此方法可以清除线程的中断状态,下面我给出一个例子:
public class Thread1Test extends Thread{ @Override public void run() { // TODO Auto-generated method stub } public static void main(String[] args) { Thread.currentThread().interrupt(); System.out.println("是否停止1?="+Thread.interrupted()); System.out.println("是否停止2?="+Thread.interrupted()); }}
运行结果为:
是否停止1?=true
是否停止2?=false2.对于isInterrupted()方法,此方法不是static的,此方法只能够测试Thread对象是否处于中断状态,但不清除状态标志。下面,我同样给出一个例子:
public class Thread1Test extends Thread { @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<50000;i++){ System.out.println("i="+(i+1)); } } public static void main(String[] args) { try { Thread1Test thread1Test=new Thread1Test(); thread1Test.start(); Thread.sleep(2000); Thread.currentThread().interrupt(); System.out.println("是否停止1?="+Thread.interrupted()); System.out.println("是否停止2?="+Thread.interrupted()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }}
运行结果为:
i=49995
i=49996
i=49997
i=49998
i=49999
i=50000
是否停止1?=true
是否停止2?=false
2.在沉睡中停止线程
1.在sleep状态下停止某一线程:
public class Thread1Test extends Thread { @Override public void 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(); } } public static void main(String[] args) { try { Thread1Test thread1Test=new Thread1Test(); thread1Test.start(); Thread.sleep(200); thread1Test.interrupt(); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println("main catch"); e.printStackTrace(); } }}
运行结果为:
run begin
在沉睡中被停止!进入catchfalse
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.company.Thread1Test.run(Thread1Test.java:11)
Process finished with exit code 0
在sleep状态下停止某一线程,会进入catch语句,并且清除停止状态值,使之变成false.
2.先用interrupt()停止,然后在sleep:
public class Thread1Test extends Thread { @Override public void run() { try { for(int i=0;i<10000;i++){ System.out.println("i="+(i+1)); } System.out.println("run begin"); Thread.sleep(20000); System.out.println("run end"); }catch (InterruptedException e){ System.out.println("先停止,再遇到了sleep!进入catch"); e.printStackTrace(); } } public static void main(String[] args) { try { Thread1Test thread1Test=new Thread1Test(); thread1Test.start(); Thread.sleep(200); thread1Test.interrupt(); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println("main catch"); e.printStackTrace(); } }}
运行结果为:
i=9996
i=9997
i=9998
i=9999
i=10000
run begin
先停止,再遇到了sleep!进入catch
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.company.Thread1Test.run(Thread1Test.java:15)
Process finished with exit code 0
根据运行结果我们可以看到线程先停止,再遇到了sleep!进入catch。
3.使用return停止线程
将方法interrupt()与return相结合也能达到停止线程的效果,测试代码:
public class Thread1Test extends Thread { @Override public void run() { while(true){ if(this.isInterrupted()){ System.out.println("停止了!"); return; } System.out.println("time="+System.currentTimeMillis()); } } public static void main(String[] args) { try { Thread1Test t1=new Thread1Test(); t1.start(); Thread.sleep(2000); t1.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("end!"); }}
运行结果如下所示:
time=1490408029279
time=1490408029279
time=1490408029279
time=1490408029279
time=1490408029279
time=1490408029279
time=1490408029279
time=1490408029279
time=1490408029279
end!
停止了!
线程在return和interrupt()方法的配合下实现了停止。 不过一般不建议使用这种方式来停止线程,因为还有一种更好的方式来停止线程,接下来我就给大家介绍这种方式。
4.使用异常停止线程
public class Thread1Test extends Thread { @Override public void run() { try{ for(int i=0;i<500000;i++){ if(this.interrupted()) { System.out.println("已经是停止状态了,我要退出"); throw new InterruptedException(); } System.out.println("i="+(i+1)); } System.out.println("我在for下面"); }catch(InterruptedException e){ System.out.println("进入到run方法的catch语句块了"); e.printStackTrace(); } } public static void main(String[] args) { try { Thread1Test t1=new Thread1Test(); t1.start(); Thread.sleep(2000); t1.interrupt(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("end!"); }}
运行结果为:
i=384646
i=384647
i=384648
i=384649
end!
已经是停止状态了,我要退出
进入到run方法的catch语句块了
java.lang.InterruptedException
at com.company.Thread1Test.run(Thread1Test.java:15)
此方法可以有效的停止线程,并在catch语句块中将异常向上抛,使得线程停止的事件1得以传播。
暂停线程
暂停线程意味着此线程还可以恢复运行,在java多线程中,可以通过suspend()方法暂停线程,使用resume()方法恢复线程。
1.suspend()与resume()方法的使用
public class MyThread extends Thread { private long i=0; public long getI() { return i; } public void setI(long i) { this.i = i; } @Override public void run() { while(true){ i++; } } public static void main(String []args){ try { MyThread thread = new MyThread(); thread.start(); Thread.sleep(5000); //A段 thread.suspend(); System.out.println("A="+System.currentTimeMillis()+"i="+thread.getI()); Thread.sleep(5000); System.out.println("A="+System.currentTimeMillis()+"i="+thread.getI()); //B段 thread.resume(); Thread.sleep(5000); System.out.println("B="+System.currentTimeMillis()+"i="+thread.getI()); Thread.sleep(5000); System.out.println("B="+System.currentTimeMillis()+"i="+thread.getI()); } catch (InterruptedException e) { e.printStackTrace(); } }}
运行结果为:
A=1490414043916i=2697026143
A=1490414048917i=2697026143
B=1490414053917i=5513982481
B=1490414058917i=8322025282从运行结果可以看到线程确实被暂停了,而且还可以恢复到运行状态
2.suspend()与resume()方法的缺点——独占
在使用suspend()与resume()方法,如果使用不当,极易造成公共的同步对象的独占,使得其他线程无法访问公共同步对象。
public class SynchronizeObject { synchronized public void printString(){ System.out.println("begin"); if (Thread.currentThread().getName().equals("a")){ System.out.println("a线程永远suspend"); Thread.currentThread().suspend(); } System.out.println("end"); } public static void main(String []args){ try { final SynchronizeObject object=new SynchronizeObject(); Thread t1=new Thread(){ @Override public void run() { object.printString(); } }; t1.setName("a"); t1.start(); Thread.sleep(1000); Thread t2=new Thread(){ @Override public void run() { super.run(); System.out.println("t2启动了,但进入不了printString()方法!只打印了一个begin" ); System.out.println("因为printString()方法被a线程锁定并且永远suspend暂停了!"); object.printString(); } }; t2.start(); } catch (InterruptedException e) { e.printStackTrace(); } }}
运行结果为:
begin
a线程永远suspend
t2启动了,但进入不了printString()方法!只打印了一个begin
因为printString()方法被a线程锁定并且永远suspend暂停了!
从运行结果中我们可以看出,当线程t1执行printString()方法时遇到suspend()被暂停,然后t2就不能再执行printString()方法了。也就造成了公共同步对象的独占。
3.suspend()与resume()方法的缺点——不同步
在使用suspend()与resume()方法时也容易出现因为线程的暂停而导致数据不同步的情况。
public class MyObject { private String username="l"; private String password="ll"; public void setValue(String u,String p){ this.username=u; if(Thread.currentThread().getName().equals("a")){ System.out.println("停止a线程"); Thread.currentThread().suspend(); } this.password=p; } public void printUsernamePassword(){ System.out.println(username+" "+password); } public static void main(String []args){ try { MyObject object=new MyObject(); Thread t1=new Thread(){ @Override public void run() { super.run(); object.setValue("a","aa"); } }; t1.setName("a"); t1.start(); Thread.sleep(500); Thread t2=new Thread(){ @Override public void run() { super.run(); object.printUsernamePassword(); } }; t2.start(); } catch (InterruptedException e) { e.printStackTrace(); } }}
运行结果为:
停止a线程
a ll
从运行结果来看,当在线程执行setValue()方法时,将线程通过suspend()方法暂停,则会出现以上值不同步的现象。
线程的优先级
在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务。
线程优先级的一些特性:
- 继承性:在Java中,线程的优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A是一样的。
- 规则性:两个优先级不同的线程同时开始处理某些任务时了,高优先级的线程总是大部分先执行完。
- 随机性:随机性是说,优先级较高的线程不一定每一次都会被先执行完。
守护线程
在Java线程中有两种线程,一种是用户线程,另一种是守护线程。守护线程是一种特殊的线程,它的特性有”陪伴“的含义,当进程中不存在非守护线程了,则守护线程自动销毁。守护线程最典型的应用就是GC(垃圾回收器)
- Java中的多线程(一)关于线程的基本操作
- java多线程系列----------- 基本的线程机制(一)
- java多线程(一):线程的建立及简单操作
- 关于多线程学习总结(三) 线程简单基本操作
- 关于多线程(一)---线程的状态
- [Java多线程 二]---线程的状态与基本操作
- [Java多线程 二]---线程的状态与基本操作
- [Java多线程 一]---线程的概念与基本性质
- [Java多线程 一]---线程的概念与基本性质
- 线程的基本操作(一)
- java多线程(一)----线程基本介绍
- Java多线程研究03-线程的基本操作(notify,notifyAll,interrupt,join,sleep)
- java高级多线程编程(一)--关于线程的停止问题
- java高级多线程编程(一)--关于线程的停止问题
- java高级多线程编程(一)--关于线程的停止问题
- Java 多线程(一) 线程的创建
- java 多线程 (一)线程的实现
- Java 多线程(一) 线程的创建
- SpringMVC异步化请求初探(续-請求背压)
- 图书管理系统(C语言)
- idea 下部署web项目遇到问题总结
- Spring autowiring自动装配
- Thinking in Java 4th
- Java中的多线程(一)关于线程的基本操作
- lua面试题:热更新简介、原理及示例
- NIO中注册channel注册多个感兴趣事件-位运算符“或”的使用
- Java设计模式之工厂模式(利用反射实现)
- C++ 笔试题简单练习----求N 以内质数 和 计算多少对质数等于N
- 【unity3d-C#学习笔记】C#中常用的数据结构及遍历方法
- Linux环境下安装python3
- 虚拟机实现代码
- 表达式计算——by Java