多线程
2015-07-07 17:14
639 查看
一、线程概述
线程和进程
线程是指进程内的一个执行单元,也是进程内的可调度实体.进程:正在运行的程序。
线程:线程依赖于进程,CPU运行进程,其实就是在运行这个进程的线程。
区别:
地址空间:进程内的一个执行单元,进程至少有一个线程;它们共享进程的地址空间,而进程有自己独立的地址空间。
资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
线程是处理器调度的基本单位,但进程不是。
简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源。
Java语言无法直接操作系统,比如IO和我们的线程都要依赖于第三方语言,java语言又把这些内容封装成类,多线程封装成一个类Thread。Java语言如果要实现多线程,必须依赖于Thread这个类。
线程工作机理
线程的随机原理:多个程序其实是CPU的在做着高效切换执行的二、创建线程
方法一:继承Thread类,并重写Thread类的run方法。
package CounterThread; public class MyThread extends Thread { int count = 1; int number; public MyThread(int count, int number) { this.count = count; this.number = number; System.out.println("创建线程:" + number); } // run()方法内的代码是线程的执行内容,这些代码在执行过程中可能被打断执行 // 因此进行异常处理 public void run() { while (true) { System.out.println("线程" + number + ":计数" + count); if (++count == 10) { return; } } } public static void main(String[] args) { for (int i = 0; i < 3; i++) { MyThread myThread = new MyThread(1, i + 1); myThread.start(); } } }
方法二:实现Runnable接口,并重写run方法。
三、线程状态
多线程生命周期:新建、就绪、运行、死亡、 特殊状态存在(睡眠,阻塞,等待)四、线程优先级
线程的优先级表示一个线程被CPU执行的机会的多少。优先级低只说明该线程 被执行的概率小,同理优先级高的线程获得CPU周期的概率大。通过Thread类的setPriority方法设置线程的优先级。public class MyPriorityThread extends Thread { int count = 1; int number; int priority; public MyPriorityThread(int count, int number, int priority) { this.count = count; this.number = number; setPriority(priority); System.out.println("创建线程:" + number); } @Override public void run() { while (true) { System.out.println("线程" + number + ":计数" + count); if (++count == 100) return; } } public static void main(String[] args) { MyPriorityThread myPriorityThread = new MyPriorityThread(1, 3, Thread.MAX_PRIORITY); myPriorityThread.start(); for (int i = 0; i < 2; i++) { MyPriorityThread myPriorityThread1 = new MyPriorityThread(1, i + 1, Thread.MIN_PRIORITY); myPriorityThread1.start(); } } }
五、线程同步
在多线程中经常遇到的一个问题就是资源共享问题。在多数编程语言中解决共享资源冲突的方法是此采用顺序机制(Serialize)通过为共享资源加锁 的方法实现资源的顺序访问。为什么存在安全问题?
1、有共享数据 2、共享数据被多条 语句操作 3、在多线程环境中
线程安全问题解决方法
1、同步代码块synchronized(锁对象){
被同步代码
}
2、同步方法
把 synchronized加在方法上
package SynchronizedThread; public class FooOne extends Thread { private int val; public FooOne(int v) { val = v; } // 使用synchronized修饰方法printVal,使得调用该方法的对象获得锁,实现互斥当问 // 该方法实现无限循环输出一个int型变量 public synchronized void printVal(int v) { while (true) System.out.println(v); } public void run() { printVal(val); } } ------------------------------------------------------------------------ package SynchronizedThread; public class FooTwo extends Thread { private FooOne sameFoo; // 该类的构造函数传入一个类FooOne的对象引用 public FooTwo(FooOne f) { sameFoo = f; } public void run() { sameFoo.printVal(2); } } ------------------------------------------------------------------------ package SynchronizedThread; /** * * @author wolfbigbig * 代码分析: 首先创建类FooOne的对象f1,构造函数传入参数1,此时变量val的值为1.通过f1启动线程。 * 程序执行类FooOne中的run * ()的内容,即调用方法printVal()。因为synchronized关键字修饰方法printVal()。 * 我们称该线程为线程A,则线程A获得FooOne对象f1的锁,而后打印数值1.因为是无限循环,run()方法不会退出。 * 线程A将不会释放对象f1的锁。 * * 接着程序创建类FooTwo的对象b。此时的构造函数参数为对象引用f1,启动该线程,我们称该线程为线程B。 * 该线程调用同一个对象f1的synchronized关键字修饰方法printVal()。由于线程A将不会释放对象f1的锁。 * 所以线程B无法获得对象f1的锁,因此线程B处于阻塞状态。 * * 最后首先创建类FooOne的对象f2。称为线程C。显然f2和f1的锁不同,所以线程C可以获得对象f2的锁。 * * 因此在执行结果上只有线程A和线程C交替执行的输出结果,却永远不会出现线程B的输出。 */ public class MainTest { public static void main(String[] args) { // 创建类FooOne的对象,并启动线程 FooOne f1 = new FooOne(1); f1.start(); // 创建类FooTwo的对象,构造函数参数为类FooOne的对象f1,并启动线程 FooTwo b = new FooTwo(f1); b.start(); // 创建类FooOne的对象,构造函数参数为3,并启动线程 FooOne f2 = new FooOne(3); f2.start(); } }
同步机制锁定的是对象,而不是代码或者函数。只要是不同的对象就有不同的锁。所以函数和代码部分被声明为synchronized并不意味着同一时刻只能有一个线程执行同步资源。
六、线程的控制
Sleep()与Wait()区别
sleep用于线程控制,而wait用于线程间的通信,与wait配套使用的还有notify和notifyAllsleep是Thread类的方法,是线程用来控制自身流程的,比如有一个要报时的线程,每一秒钟打印出
一个时间,那么我就需要在print方法前面加一个sleep让自己每隔一秒执行一次。就像闹钟一样
wait是Object类的方法,用来线程之间的通信,这个方法会使当前拥有该对象锁的进程等待直到其他线程调用notify方法时再醒来。这个方法主要是用于不同线程之间的调度。
3、调用sleep方法不会释放锁。调用wait方法会释放当前线程的锁。
七、死锁问题
由于线程会进入阻塞状态,并且由于对象同步锁的存在,使得只有获得对象锁才能访问该对象,因此很容易发生循环死锁。在编程过程中我们应该避免死锁的出现。但是在有些面试中会出一些设计死锁的编程题目,现在附上代码一篇。
package DeadLock; /** * * @author wolfbigbig * 一个简单的死锁: T1:线程1 T2:线程2 * 当类的对象flag=true时,(T1),先锁定ObjA,然后输出“if objA”,睡眠500毫秒,然后锁定ObjB * 而T1在睡眠的时候。T2,先锁定ObjB,然后输出“else objB”,睡眠500毫秒,然后锁定ObjA * T1睡眠结束后,需要锁定objB才能继续执行,而此时objB已经被T2锁定。 * T2睡眠结束后,需要锁定objA才能继续执行,而此时objA已经被T1锁定。 * T1、T2互相等待,都需要对方锁定的资源才能继续执行,从而形成死锁。 * */ public class DeadLockDemo { public static void main(String[] args) { // 创建接口对象,用于资源共享、同步 MyLock ml = new MyLock(true); MyLock ml1 = new MyLock(false); // 设计两个线程 Thread t = new Thread(ml); Thread t1 = new Thread(ml1); t.setPriority(10); t1.setPriority(1); t.start(); t1.start(); } } ------------------------------------------------------------- package DeadLock; public class MyLock implements Runnable { public static Object objA = new Object(); public static Object objB = new Object(); private boolean flag; /* * 有参构造函数 */ public MyLock(boolean flag) { super(); this.flag = flag; } public void run() { // TODO Auto-generated method stub if (flag) { synchronized (MyLock.objA) { System.out.println("if objA"); try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (MyLock.objB) { System.out.println("if objB"); } } } else { synchronized (MyLock.objB) { System.out.println("else objB"); try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (MyLock.objA) { System.out.println("else objA"); } } } } }
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- Python3写爬虫(四)多线程实现数据爬取
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树