【网络编程4】Java多线程
来源:互联网 发布:mysql还原数据库 编辑:程序博客网 时间:2024/05/02 01:21
这篇博文是本人学习《Java网络程序设计》书中第6章中多线程的学习总结,这篇博客只是让大家总体了解一下多线程,并没有深入讲解多线程,算是对多线程的入门吧!所有源代码都在文章最后我的github链接代码中。
——惠州学院13网络工程 吴成兵 20160619
目录
- 目录
- 一 多线程概述
- 二 在Java中实现多线程
- 21 继承Thread类
- 22 实现Runnable接口
- 三 线程的五种状态
- 31 创建new状态
- 32 就绪runnable状态
- 33 运行running状态
- 34 阻塞block状态
- 35 死亡dead状态
- 四 线程的优先级
- 五 线程的同步
- 51 synchronized方法
- 52 synchronized块
- 六 线程的阻塞
- 61 sleep方法
- 62 wait和notify方法
- 63 suspend和resume方法
- 七 守护线程
- 八 线程组
一 多线程概述
多线程是一种机制,它允许在一个程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其他纯种共享一个存储空间,这使得线程间的通信远较进程简单。
多线程的执行是并发的。并发在宏观上来说是并行,在微观上来说是串行,因为每个线程执行由操作系统调度并快速切换执行,所以在肉眼看来是并行的。
二 在Java中实现多线程
Java虚拟机允许应用程序并发地运行多个执行线程。Java语言提供了多线程编程的扩展类,并提供了功能强大的线程控制API。在Java中,多线程的实现有两种方式:
- 继承java.lang.Thread类
- 实现java.lang.Runnable接口
2.1 继承Thread类
步骤:1、继承Thread类;2、new出实例;3、重写run();4、调用start()。
package _6_1在Java中实现多线程._1_继承Thread类;public class TestMitiThread { public static void main(String[] args) { System.out.println(Thread.currentThread().getName()+"线程运行开始"); new MitiThread("A").start(); new MitiThread("B").start(); System.out.println(Thread.currentThread().getName()+"线程运行结束"); }}
package _6_1在Java中实现多线程._1_继承Thread类;public class MitiThread extends Thread{ public MitiThread(String threadname){ super(threadname); } public void run(){ System.out.println(getName()+"线程运行开始"); for (int i = 0; i < 5; i++) { System.out.println(i+" "+getName()); try { sleep((long) (Math.random()*10)); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(getName()+"线程运行结束"); }}
2.2 实现Runnable接口
package _6_1在Java中实现多线程._2_实现Runnable接口;public class MyThread implements Runnable{ int count=1,number; public MyThread(int num){ number=num; System.out.println("创建线程》》》》》》》》》》》》》》"+number); } @Override public void run() { while(true){ System.out.println("线程"+number+":计数 "+count); if(++count==3){ System.out.println("线程#####################"+number+" 结束"); return; } } } public static void main(String[] args) { for (int i = 0; i < 3; i++) { new Thread(new MyThread(i+1)).start(); } }}
三 线程的五种状态
一个线程创建之后,总是处于生命周期的五个状态之一。线程的状态表明些线程当前正进行的活动,而线程的状态是可以通过程序的操作来控制的。这些操作包括启动(start),终止(stop),睡眠(sleep),挂起(suspend),恢复(resume),等待(wait)和通知(notify)。每一个操作对应一个方法,这些方法是由软件包java.lang提供的。在Java当中,线程通常都有创建、可运行(就绪)、运行、阻塞和死亡五种状态。
3.1 创建(new)状态
如果创建了一个线程而没有启动它,此线程就处于创建状态。如:
Thread myThread=new MyThreadClass();//MyThreadClass是Thread的子类
处于创建状态的线程没有获得应有的资源,所以这是一个空线程,只有启动了该线程,系统才会给它分配资源。创建状态的线程有两种操作:
- 启动(start)操作:进入可运行状态;
- 终止(stop)操作:进入死亡状态。
3.2 就绪(runnable)状态
如果线程在创建状态,可通过下面方法进入可运行(就绪)状态:
myThread.start();
调用start()方法后并不是立即执行多线程run()方法中的代码,而是使得线程变为可运行态,什么时间运行由操作系统决定的。
3.3 运行(running)状态
线程调度程序处于可运行状态的线程设置为当前线程,就进入了运行状态,开始运行run函数中的代码。此时线程独占CPU的控制权,会出现下面情况:
- 如果有更高优先级线程出现,则该线程被迫放弃进入可运行状态;
- 使用yield()方法可以使线程主动放弃CPU控制权;
- 执行结束或使用stop()方法放弃控制权面进入死亡态。
3.4 阻塞(block)状态
阻塞状态都由运行状态转变过来的,阻塞结束进入可运行态。例如正在运行的线程需要等待IO(网络IO、硬盘IO等)获得相关资源,而进入阻塞状态,当IO获取相关资源后就进入可运行态,等待操作系统调试运行。一般有如下三种情况:
- 睡眠(sleep)进入阻塞状态,睡眠结束并自动恢复到可运行态;
- 挂起操作(suspend)进入阻塞状态,并用恢复方法(resume)使其恢复到可运行态;
- 等待(wait)操作进入阻塞状态,必用notify()方法或notifyAll()方法到可运行态。
3.5 死亡(dead)状态
线程运行结束或使用stop()方法而进入死亡状态,有时是因异常退出。就不死亡状态能进行其他状态了,该线程已不再存在。
四 线程的优先级
线程的优先级代表线程的重要性,当有多个线程同时处于可运行状态并等待获得CPU控制时,系统会根据线程的优先级给哪个线程分配CPU控制权。
Thread类定义了3个线程优先级常量:
- static int MAX_PRIORITY:最高优先级,取值为了10;
- static int MIN_PRIORITY:最低优先级,取值为了1;
- static int NORM_PRIORITY:默认优先级,取值为了5。
当两个线程前有继承关系时,其优先级也是一样的,但可以通过setPriority()方法来改变线程的优先级,设计线程的优先级范围在[1,10],如果超过这个范围会抛出IllegalArgumentException异常。
五 线程的同步
由于同一进程的多线程共享同一片存储空间,当这些多线程访问同享数据会带来冲突。Java语言为了有效避免这种冲突提出了专门机制,这种机制就是synchronized关键字,它包括两种用法:synchronized方法和synchronized块。
5.1 synchronized方法
public synchronized void accessVal(int newVal);
通过在方法声明中加入synchronized关键字来声明synchronized方法。该方法被加上锁,只允许一个线程来访问,直到该线程访问结束才释放该方法的锁,以供其他线程访问。如果在未释放该锁时有其他线程要访问该锁中的方法,那么后访问的线程就会进入阻塞状态。
5.2 synchronized块
synchronized(synchObject){//允许访问控制的代码}
通过synchronized关键字来声明synchronized块,其中代码必须获得对象syncObject(可以是类也可以是类的实例)的锁才能执行。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
六 线程的阻塞
线程阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪),Java提供了大量方法来支持阻塞,下面让我们逐一分析:
6.1 sleep()方法
指定以毫秒为单位进入休眠阻塞状态,指定时间结束就进入可运行状态。
6.2 wait()和notify()方法
wait()方法使得线程进入等待阻塞状态,调用notify()方法会随机解除一个等待阻塞状态的线程,调用notifyAll()方法会解除所有等待阻塞状态的线程。notify()方法有两种形式:
- 指定以毫秒为单位的一段时间为参数时(如:wait(1000)),当对应的notify()被调用或超出指定的时间时线程进入可执行状态。
- 没有指定参数时(如:wait()),必须对应的notify()被调用。
6.3 suspend()和resume()方法
suspend()方法使得线程进入挂起阻塞状态,并且不能自动恢复,必须其对应的resume()方法被调用才能进入可运行状态。
挂起阻塞状态会释放占用的锁。使得这对方法有如下一些不同:
- 前面的方法都隶属于Thread类,但这对方法直接隶属于Object类,也就是对所有对象都拥有这对方法。
- 前面的方法可以用在任何位置,而这对方法必须用在synchronized方法或块中,因为synchronized方法或块中才有占有锁,才有锁可以释放。否则运行会有IllegalMonitorStateException异常。
七 守护线程
守护线程(Daemon线程)是一类特殊的线程,它和普通线程的区别在于它并不是应用程序的核心部分,当一个应用程序的所有非守护线程终止运行时,即使仍然有守护线程在运行,应用程序也将终止,反之,只要有一个非守护线程在运行,应用程序就不会终止。守护线程一般被用在后台为其他线程提供服务。
- isDaemon()调查一个线程是否是一个守护线程;
- setDaemon(true)设置一个线程为袒护线程;
如果一个线程是守护线程,那么它创建的线程的任何线程都是守护线程,请看例子演示:
package _6_6守护线程;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;/** * * Copyright ? 2016 Authors. All rights reserved. * * FileName: .java * @author : Wu_Being <1040003585@qq.com> * Date/Time: 2016-6-20/上午10:59:06 * Description: * 当一个应用程序的所有非守护线程终止运行时,即使仍然有守护线程在运行,应用程序也将终止。 * 反之,只要有一个非守护线程在运行,应用程序就不会终止。 */public class DaemonTest { public static void main(String[] args) { Thread thread = new Daemon(); System.out.println("thread.isDaemon()=" + thread.isDaemon()); //等待非守护线程的回车结束运行 InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr); System.out.println("UnDaemon Waiting for CR to end all daemon threads ......"); try { br.readLine(); } catch (IOException e) { e.printStackTrace(); } }}
package _6_6守护线程;/** * * Copyright ? 2016 Authors. All rights reserved. * * FileName: .java * @author : Wu_Being <1040003585@qq.com> * Date/Time: 2016-6-20/上午10:59:36 * Description: */public class Daemon extends Thread { private static final int SIZE = 10; private Thread[] t = new Thread[SIZE]; public Daemon() { setDaemon(true); start(); } public void run() { for (int i = 0; i < SIZE; i++) { t[i] = new DaemonSpawn(i); try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } for (int i = 0; i < SIZE; i++) { System.out.println("t[" + i + "].isDaemon()=" + t[i].isDaemon()); try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } while (true) { yield(); } } /** * * Copyright ? 2016 Authors. All rights reserved. * * FileName: .java * @author : Wu_Being <1040003585@qq.com> * Date/Time: 2016-6-20/上午11:00:15 * Description: */ public class DaemonSpawn extends Thread { public DaemonSpawn(int i) { System.out.println("DaemonSpawn " + i + " started..."); start(); } public void run() { while (true) { yield(); } } }}
八 线程组
线程组是Java特有的概念。在Java中,线程组是类ThreadGroup的对象,每个线程都隶属于唯一一个线程组,这个线程组在线程创建时指定并在线程的整个生命期内都不能更改。
在java中,除了预建的系统线程组外,所有线程组都必须显式创建。若没有指定,则线程默认地隶属于名为system的系统线程组,除了系统线程组外的线程组又可以隶属于另一个线程组。这样,所有线程组组成了一棵以系统线程组为根的树。
下面我们来看一个例子:
package _6_7线程组;/** * * Copyright ? 2016 Authors. All rights reserved. * * FileName: .java * @author : Wu_Being <1040003585@qq.com> * Date/Time: 2016-6-20/下午01:23:45 * Description: */public class TestAccess { public static void main(String[] args) { // xGroup1隶属于系统线程组 ThreadGroup xGroup1 = new ThreadGroup("xGroup1N"); // yGroup11隶属于xGroup1组 ThreadGroup yGroup11 = new ThreadGroup(xGroup1, "yGroup11N"); // zGroup111隶属于yGroup11组 ThreadGroup zGroup111 = new ThreadGroup(yGroup11, "zGroup111N"); // Thread one1 = new TestThread1(xGroup1, "one1N"); // Thread one2 = new TestThread1(xGroup1, "one2N"); // Thread two1 = new TestThread3(yGroup11, "two1N"); Thread three1 = new TestThread3(zGroup111, "three1N"); // Thread three2 = new TestThread3(zGroup111, "three2N"); }}
package _6_7线程组;public class TestThread1 extends Thread { private int i; TestThread1(ThreadGroup g, String name) { super(g, name); } void f() { i++; System.out.println(getName() + " f()"); }}package _6_7线程组;public class TestThread3 extends TestThread1 { public TestThread3(ThreadGroup g, String name) { super(g, name); start(); } public void run() { // 1.用getParent()向上移动两级到xGroup1name ThreadGroup group = getThreadGroup().getParent().getParent(); System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>00"); group.list();// output // 2.调用activeCount()查询这个线程组及所有子线程组内有多少个线程, // 从而创建由指定Thread的句柄构成一个数组。 Thread[] groupThreads = new Thread[group.activeCount()]; // 3.enumerate()方法将指向所有这些线程的句柄置入数组groupThreads里 group.enumerate(groupThreads); // 4.然后调用每个线程的f()方法,同时修改优先级 System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>11"); for (int i = 0; i < groupThreads.length; i++) { groupThreads[i].setPriority(MIN_PRIORITY); ((TestThread1) groupThreads[i]).f();// output } System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>22"); group.list();// output }}
文中所有源代码链接
Wu_Being博客声明:本人博客欢迎转载,请标明博客原文和原链接!谢谢!
《Java多线程》:
http://blog.csdn.net/u014134180/article/details/51720913
如果你看完这篇博文,觉得对你有帮助,并且愿意付赞助费,那么我会更有动力写下去。
- 【网络编程4】Java多线程
- java网络编程、多线程
- java 多线程网络编程
- Java网络编程:多线程聊天
- Java网络编程+多线程通信
- Java多线程与网络编程
- Java基础-多线程下载(网络编程-多线程)
- java网络编程(二)多线程网络编程
- 关于java多线程网络编程的注意事项!
- java 基TCP的多线程 网络编程
- Java的Socket网络编程以及多线程
- Java多线程与网络编程综合使用
- Java正则、Mysql、网络编程、多线程总结
- Java网络编程--多线程的Socket
- Java网络编程-IO多路复用(多线程)
- java编程网络编程,多线程,小型聊天系统
- Java网络编程4
- java网络编程(4)
- android.support.v4.app.Fragment$InstantiationException问题解决
- 广东海洋大学 电子1151 孔yanfei python语言程序设计 第九周
- This application's application-identifier entitlement does not match that of the installed applicati
- Ubuntu下pdf阅读器
- kafka
- 【网络编程4】Java多线程
- Log4j日志级别
- QAction类
- 别因为要学的太多反而压垮自己
- Android图片裁剪库——cropper使用,完美解决图片填充不满布局问题
- C++ 简易 Simple_ATM_drawmoney
- 广东海洋大学 电子1151 孔yanfei python语言程序设计 第十周
- 91.【block编程第一篇】 block编程热点介绍(官方文档翻译的)
- Android开发EditText属性