1.线程、进程
1.1 概念
进程:引入:为了程序能够并发执行,且为了对并发执行的程序加以描述和控制,引入了进程的概念。
(1)进程是程序的一次执行。
(2)进程是一个程序及其数据在处理机上顺序执行时所发生的活动。
(3)进程是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。
(4)进程是作为能够拥有资源和独立运行的基本单位。
线程: 引入:在操作系统中引入线程,是为了减少程序并发执行时锁付出的空间开销,使OS具有更好的并发性。1.2
1.2 线程与进程进行比较
线程具有许多传统进程所具有的特征,所以又称为轻量级进程或进程元,相应的把进程称为重量级的进程,传统进程相当于只有一个线程的任务,在引入了新城的操作系统中,通常一个进程都拥有若干个线程,至少也有一个线程。
相同点:
(1)调度:
(2)并发性:在引入了线程的操作系统中,进程之间或一个线程中的多个线程之间都可以并发执行。
(3)拥有资源:不论是传统的操作系统还是引入了线程的操作系统,进程都可以拥有资源,是系统中拥有资源的有个基本单位。
(4)系统开销:在某些操作系统中,线程的切换、同步和通信都无需操作系统内核的干预。
不同点:
(1)调度:在传统操作系统中,进程是拥有资源的基本和独立调度、分派的基本单位,在引入了线程的操作系统中,线程是调度和分派的基本单位,而进程是拥有资源的基本单位。
(2)并发性:
(3)拥有资源:进程拥有资源,线程自己不拥有资源(也有一点必不可少的资源)。但可以吧访问其他隶属进程的资源。
(4)系统开销:
a.在创建或撤销进程的时候,系统都要为之创建和回收进程控制块,分配或回收资源。如内存空间和I/O设备,系统开销明显大于线程创建或撤销时的开销。
b.在进程切换时,涉及到当前进程CPU环境的保存及新被调度运行进程的CPU环境的设置,而线程的切换则仅需要保存和设置少量寄存器内容,不涉及存储器管理方面的操作,所以,进程的代价远高于线程。
c.一个进程的多个线程具有相同的地址空间,在同步和通信的实现方面线程也比进程容易。
(5)进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
(6)进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮。
(7)每个独立的线程都有一个程序运行的入口,顺序执行序列和程序的入口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
1.3 线程的生命周期
1.3.1.线程处于就绪状态有以下几种方法
(1)调用sleep()方法
(2)调用wait()方法
(3)等待输入/输出完成
1.3.2.当线程处于就绪状态后,可以用以下几种方法使线程进入运行状态:
(1)线程调用notify()方法
(2)线程调用notifyAll()方法
(3)线程调用interrupt()方法
(4) 线程的休眠时间结束
(5)输入/输出结束
线程生命周期状态图:
2.线程的调度与控制
2.1 线程优先级
java虚拟机主要负责则线程的调度,获取CPU的使用权,目前有两种调度模型:分时调度模型和抢占调度模型;java使用抢占调度模型。
分时调度模型:所有线程轮流使用CPU的使用权,平均分配给每个线程占用CPU的时间片。
抢占调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么随机选择一个,优先极高的线程获得的CPU时间片相对多些。
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 线程优先级高的获取的时间片相对高 3 */ 4 class Threadtest04 5 { 6 public static void main(String[] args) 7 { 8 //线程优先级 9 System.out.println(Thread.MAX_PRIORITY); //10 优先级最高 10 System.out.println(Thread.MIN_PRIORITY); //1 优先级最低 11 System.out.println(Thread.NORM_PRIORITY); //5 默认优先级 12 Thread t1 = new P(); 13 t1.setName("t1"); 14 15 Thread t2 = new P(); 16 t2.setName("t2"); 17 18 System.out.println(t1.getPriority()); //默认优先级为5 19 System.out.println(t2.getPriority()); //默认优先级为5 20 //设置优先级 21 t1.setPriority(4); 22 t2.setPriority(9); 23 24 System.out.println(t1.getPriority()); //默认优先级为5 25 System.out.println(t2.getPriority()); //默认优先级为5 26 //启动线程 27 t1.start(); 28 t2.start(); 29 } 30 } 31 32 class P extends Thread 33 { 34 public void run() 35 { 36 for(int i=0;i<10;i++) 37 { 38 System.out.println(Thread.currentThread().getName()+"..."+i); 39 } 40 } 41 } 42 /* 43 运行结果: 44 10 45 1 46 5 47 5 48 5 49 4 50 9 51 t1...0 52 t2...0 53 t1...1 54 t2...1 55 t1...2 56 t2...2 57 t1...3 58 t2...3 59 t1...4 60 t2...4 61 t1...5 62 t2...5 63 t1...6 64 t2...6 65 t1...7 66 t2...7 67 t1...8 68 t2...8 69 t1...9 70 t2...9 71 请按任意键继续. . . 72 73 74 */
2.2 sleep方法
sleep和wait的区别:
(1)所属类不同
Thread.sleep() ; Object.wait()
(2)对于线程已经占有资源的处理
sleep在休息的时候,不释放资源 ; wait在等待的时候释放自己占用的资源
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 1.thread.sleep(毫秒) 3 2.sleep方法是一个静态方法 4 3.该方法作用:阻塞当前线程,将CPU让给其他线程 5 */ 6 class Threadtest05 7 { 8 public static void main(String[] args) throws InterruptedException 9 { 10 Thread t1 = new P(); 11 t1.setName("t1"); 12 t1.start(); 13 Thread.sleep(5000); 14 //t1.interrupt();打断线程的睡眠 15 for(int i = 0;i<10;i++) 16 { 17 System.out.println(Thread.currentThread().getName()+".."+i); 18 Thread.sleep(500);//阻塞主线程0.5秒 19 } 20 } 21 } 22 23 class P extends Thread 24 { 25 //被重写的方法不能抛出异常,在run方法的声明为置上不能呢使用Throws, 26 public void run() 27 { 28 for(int i = 0;i<10;i++) 29 { 30 31 try{ 32 Thread.sleep(1000);//让当前线程阻塞1s 33 }catch(InterruptedException e) 34 { 35 e.printStackTrace(); 36 } 37 System.out.println(Thread.currentThread().getName()+".."+i); 38 } 39 } 40 } 41 /* 42 运行结果: 43 main..0 44 t1..0 45 main..1 46 t1..1 47 main..2 48 main..3 49 main..4 50 t1..2 51 main..5 52 t1..3 53 main..6 54 main..7 55 t1..4 56 main..8 57 main..9 58 t1..5 59 t1..6 60 t1..7 61 t1..8 62 t1..9 63 请按任意键继续. . . 64 65 */
2.3 yield方法
使用yield方法,与sleep方法类似,不能由用户指定暂停多长时间,并且yield方法只能让同优先级的线程由执行机会,让位时间不固定。静态方法。
2.4 Join方法
合并线程
3.创建线程的三种方法
Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。Java可以用三种方式来创建线程,如下所示:
(1)继承Thread类创建线程
(2)实现Runnable接口创建线程
(3)使用Callable和Future创建线程
3.1.概念及特点
(1).概念
进程:计算机运行的应用程序。(多进程作用:提高CPU的使用率,不提高速度);
线程;一个进程中的执行场景,一个进程可以包多个线程。(多线程作用:提高应用程序的使用率,不提高速度)。
(2).内存特点
进程和进程之间的内存是独立的;
线程和线程共享“堆内存的方法区内存”,栈内存是独立的,一个线程一个栈。
(3).java程序的运行原理
Java命令启动Java虚拟机,启动JVM,等同于启动了一个应用程序,表示启动了一个进程,该进程会自动启动一个“主线程”,然后主线程去调用某个类的main方法,所 以main方法运行在主线程中。
3.2 线程的创建和启动
3.2.1 实现多线程的第一种方式
Thread类在包java.lang中,从这个类中实例化的对象代表线程,启动一个新线程需要建立Thread实例,Thread类中常用的两个构造方法如下:
(1)public Thread(String threadName)
(2)public Thread()
其中第一个构造方法是创建一个名称为threadName的线程对象
创建一个新的线程的语法如下:
public class ThreadTest extends Thread{}
完成线程真正功能的代码方法类run()方法中,当一个类继承Thread类后,就可以在该类中覆盖run方法,将实现该线程功能的代码写入run()方法中国,然后调用Thread类中的start()方法执行线程,即调用run()方法.
Thread对象需要一个任务来执行,任务是指线程在启动时执行的工作,该工作的功能代码被写在run()方法中。当执行一个线程程序时,就自动产生一个线程,主方法是在这个线程上运次的,当不再启动其他线程时,该程序就为单线程程序。
以下是一个Thread类的实例:
/** * 类ThreadTest继承继承Thread雷诺方法创建线程 * @author z * */ public class ThreadTest extends Thread { private int count = 10; public void run(){ while(true){ System.out.println(count+""); if(--count == 0){ return; } } } public static void main(String[] args){ new ThreadTest().start(); } }
运行结果:10 9 8 7 6 5 4 3 2 1
分析:
在上例中,继承了Thread类,然后覆盖了run()方法,通常在run()方法中使用无限循环的形式,使得线程一直运行下去,所以要指定一跳出循环的条件,本例使用变量count递减为0的方法跳出循环条件。
在main()方发中,使线程执行需要调用Thread类中的start()方法,start()方法调用被覆盖的run()方法,如果不调用start()方法,线程永远不会启动,在主方法没有调用stsrt()方法之前,Thread()对象只是一个实例,而不是一个正真的线程。
再举一个例子:
第一步: 继承Java.lang.Thread
第二步;重写run方法
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 class Threadtest01 2 { 3 public static void main(String[] args) //main方法在主线程 4 { 5 //1.创建线程 6 Thread t = new p(); 7 //2.启动线程 8 t.start(); /*启动t线程,执行后,t线程瞬间结束,JVM再分配一个新的栈给t线程 9 run方法不需要手动调用,系统线程启动之后自动调用run方法。*/ 10 11 for(int i=0;i<100;i++) //在主线程中运行 12 { 13 System.out.println("main....."+i); 14 } 15 } 16 } 17 /* 18 在多线程中,main方法结束后只是主线程踪没有方法栈帧了,但 19 其他线程中或者其他栈中还有栈帧,main方法结束,程序可能还在运行 20 */ 21 22 //3.定义一个线程 23 class p extends Thread 24 { 25 //重写run方法 26 public void run () 27 { 28 for(int i=0;i<100;i++) 29 { 30 System.out.println("run...."+i); 31 } 32 } 33 } 34 /* 35 运行部分结果如下: 36 main.....0 37 main.....1 38 main.....2 39 run....0 40 main.....3 41 run....1 42 main.....4 43 run....2 44 main.....5 45 run....3 46 main.....6 47 run....4 48 main.....7 49 run....5 50 main.....8 51 run....6 52 */
上述代码的图解如下:
3.3.2 实现多线程的第二种方式:
Runnable接口的说用是使线程不仅可以继承Thread类实现,还可以继承其他类(比如:JFrame)。
此接口具有两个构造方法:
(1)public Thread(Runnable r)
(2)public Thread(Runnable r,String name).
这两个构造方法的参数中都存在Runnable实例,使用构造方法就可以将Runnable实例与Thread实例相关联。
使用Runnable的步骤如下:
(1)建立Runnable对象
(2)使用参数为Runnable对象的构造方法创建Thread实例
(3)调用start()方法。
例:在项目中创建SwingAndThread类,该类继承了JFrame类,实现图标移动的功能,其中使用了Swing与线程相结合的技术:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import java.awt.*; 2 import java.net.*; 3 4 import javax.swing.*; 5 6 public class SwingAndThread extends JFrame { 7 /** 8 * 9 */ 10 private static final long serialVersionUID = 1L; 11 private JLabel jl = new JLabel(); // 声明JLabel对象 12 private static Thread t; // 声明线程对象 13 private int count = 0; // 声明计数变量 14 private Container container = getContentPane(); // 声明容器 15 16 public SwingAndThread() { 17 setBounds(300, 200, 250, 100); // 绝对定位窗体大小与位置 18 container.setLayout(null); // 使窗体不使用任何布局管理器 19 URL url = SwingAndThread.class.getResource("/1.gif"); // 获取图片的URL 20 Icon icon = new ImageIcon(url); // 实例化一个Icon 21 jl.setIcon(icon); // 将图标放置在标签中 22 // 设置图片在标签的最左方 23 jl.setHorizontalAlignment(SwingConstants.LEFT); 24 jl.setBounds(10, 10, 200, 50); // 设置标签的位置与大小 25 jl.setOpaque(true); 26 t = new Thread(new Runnable() { // 定义匿名内部类,该类实现Runnable接口 27 public void run() { // 重写run()方法 28 while (count <= 200) { // 设置循环条件 29 // 将标签的横坐标用变量表示 30 jl.setBounds(count, 10, 200, 50); 31 try { 32 Thread.sleep(1000); // 使线程休眠1000毫秒 33 } catch (Exception e) { 34 e.printStackTrace(); 35 } 36 count += 4; // 使横坐标每次增加4 37 if (count == 200) { 38 // 当图标到达标签的最右边,使其回到标签最左边 39 count = 10; 40 } 41 } 42 } 43 }); 44 t.start(); // 启动线程 45 container.add(jl); // 将标签添加到容器中 46 setVisible(true); // 使窗体可视 47 // 设置窗体的关闭方式 48 setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 49 } 50 51 public static void main(String[] args) { 52 new SwingAndThread(); // 实例化一个SwingAndThread对象 53 } 54 }
运行结果:
再举一个例子:
第一步:写一个实现类java,lang.Runnable;接口
第二步:实现run方法
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 package test; 2 3 class Main 4 { 5 public static void main(String[] args) 6 { 7 //创建线程 8 Thread t = new Thread(new p()); 9 //启动线程 10 t.start(); 11 for(int i = 0;i<100;i++) 12 System.out.println("mian..."+i); 13 14 } 15 } 16 //优先,一个类实现接口之外保留了类的继承 17 class p implements Runnable 18 { 19 public void run() 20 { 21 for(int i = 0;i<100;i++) 22 System.out.println("run..."+i); 23 } 24 } 25 /* 26 * 部分运行结果: 27 * run...8 28 mian...10 29 run...9 30 mian...11 31 run...10 32 mian...12 33 run...11 34 mian...13 35 run...12 36 */
4.线程的同步(加锁)
4.1 基本概念及特点
(1)概念:
a.线程同步,是指某一个时刻,只允许一个线程访问共享资源,线程同步实际上是对对象加锁,如果对象中的方法都是同步方法,那么某一时刻只能执 行一个方法,采用线程同步解决以上问题; 为了数据安全,尽管应用程序的使用效率降低,但是为了数据安全,必须加入线程同步机制,线程同步机制使程序等同于单线程。
b.异步编程模型:多个线程分别执行,各线程互不影响。
c.同步编程模型:多个线程执行只有一个线程执行结束才能执行另一个线程。(作用:达到数据安全)
(2)使用线程同步机制的条件:
a:必须是多线程环境
b:多线程环境共享一个数据
c:共享的数据涉及到数据的修改
4.2 举例
例1:(对象锁,方法一,控制精确,常用)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 模拟银行取款系统:对于同一个账号,用两个线程同时对其取款。 3 */ 4 class Threadtest06 5 { 6 public static void main(String[] args) 7 { 8 //创建公共账号 9 Account act = new Account("账号sss",10000); 10 //创建两个线程对同一个账户取款 11 Thread t1 = new Thread(new P(act)); 12 Thread t2 = new Thread(new P(act)); 13 t1.start(); 14 t2.start(); 15 } 16 } 17 //取款线程 18 class P implements Runnable 19 { 20 //账户 21 Account act; 22 P(Account act) 23 { 24 this.act = act; 25 } 26 public void run() 27 { 28 act.withdraw(2000); 29 System.out.println("取款成功,余额为:"+act.getBalance()); 30 } 31 32 } 33 //账户 34 class Account 35 { 36 private String action; 37 private double balance; 38 public Account(){} 39 public Account(String action,double balance){ 40 this.action = action; 41 this.balance = balance; 42 } 43 public void setAction(String action) 44 { 45 this.action = action; 46 } 47 48 public void setBalance(double balance) 49 { 50 this.balance = balance; 51 } 52 public String getAction(String action) 53 { 54 return action; 55 } 56 public double getBalance() 57 { 58 return balance; 59 } 60 public void withdraw(double money) 61 { 62 synchronized(this){ //this表示共享对象 63 double after = balance - money; 64 try{ 65 Thread.sleep(1000); 66 }catch(Exception e){} 67 this.setBalance(after); 68 } 69 } 70 } 71 72 /* 73 运行结果: 74 取款成功,余额为:8000.0 75 取款成功,余额为:6000.0 76 请按任意键继续. . . 77 78 */
上例原理: t1线程执行到synchronized关键字处,就会去找this对象锁,如果找到this对象锁,就回进入同步语句块中执行,当同步语句块中的代码执行结束后,
t1线程归还this对象锁。在t1线程执行同步语句块的过程中,如果t2线程也过来执行此代码,也遇到synchronized关键字,所以也去找this对象锁,但是
该对象锁被t1线程持有,只能等待this对象的归还。
例2;synchronized关键字添加到成员方法上,线程拿到的扔是this的对象锁(对象锁,方法二,执行效率低)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 模拟银行取款系统:对于同一个账号,用两个线程同时对其取款。 3 */ 4 class Threadtest06 5 { 6 public static void main(String[] args) 7 { 8 //创建公共账号 9 Account act = new Account("账号sss",10000); 10 //创建两个线程对同一个账户取款 11 Thread t1 = new Thread(new P(act)); 12 Thread t2 = new Thread(new P(act)); 13 t1.start(); 14 t2.start(); 15 } 16 } 17 //取款线程 18 class P implements Runnable 19 { 20 //账户 21 Account act; 22 P(Account act) 23 { 24 this.act = act; 25 } 26 public void run() 27 { 28 act.withdraw(2000); 29 System.out.println("取款成功,余额为:"+act.getBalance()); 30 } 31 32 } 33 //账户 34 class Account 35 { 36 private String action; 37 private double balance; 38 public Account(){} 39 public Account(String action,double balance){ 40 this.action = action; 41 this.balance = balance; 42 } 43 public void setAction(String action) 44 { 45 this.action = action; 46 } 47 48 public void setBalance(double balance) 49 { 50 this.balance = balance; 51 } 52 public String getAction(String action) 53 { 54 return action; 55 } 56 public double getBalance() 57 { 58 return balance; 59 } 60 public synchronized void withdraw(double money) 61 { 62 63 double after = balance - money; 64 try{ 65 Thread.sleep(1000); 66 }catch(Exception e){} 67 this.setBalance(after); 68 69 } 70 } 71 72 /* 73 运行结果: 74 取款成功,余额为:8000.0 75 取款成功,余额为:6000.0 76 请按任意键继续. . . 77 78 */
(另:StirngBuffer Vector Hashtable 是线程安全的 )
5.死锁
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 死锁 3 */ 4 class DeadLook 5 { 6 public static void main(String[] args) 7 { 8 Object o1 = new Object(); 9 Object o2 = new Object(); 10 11 Thread t1 = new Thread(new T1(o1,o2)); 12 Thread t2 = new Thread(new T2(o1,o2)); 13 t1.start(); 14 t2.start(); 15 } 16 } 17 18 class T1 implements Runnable 19 { 20 Object o1; 21 Object o2; 22 T1(Object o1,Object o2) 23 { 24 this.o1 = o1; 25 this.o2 = o2; 26 } 27 public void run() 28 { 29 synchronized(o1) 30 { 31 try{Thread.sleep(1000);}catch(Exception e){} 32 synchronized(o2) 33 { 34 System.out.println("t1"); 35 } 36 } 37 } 38 } 39 class T2 implements Runnable 40 { 41 Object o1; 42 Object o2; 43 T2(Object o1,Object o2) 44 { 45 this.o1 = o1; 46 this.o2 = o2; 47 } 48 public void run() 49 { 50 synchronized(o2) 51 { 52 try{Thread.sleep(1000);}catch(Exception e){} 53 synchronized(o1) 54 { 55 System.out.println("t2"); 56 } 57 } 58 } 59 }
6.守护线程
从线程的分类上可以分为:用户线程和守护线程,所有的用户线程结束生命周期,只有一个用户线程存在,那么守护线程就不会结束,Java中的垃圾回收器就
是一个守护线程,只有应用程序中所有的线程结束,它才会结束。
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 守护线程: 3 所有的用户线程结束,守护线程才会结束,守护线程是一个无限循环执行的。 4 */ 5 class Threadtest08 6 { 7 public static void main(String[] args) throws Exception 8 { 9 Thread t1 = new P(); 10 t1.setName("t1"); 11 t1.setDaemon(true);//将用户线程修改为守护线程 12 t1.start(); 13 //主线程 14 for(int i = 0;i < 10; i++) 15 { 16 System.out.println(Thread.currentThread().getName()+".."+i); 17 Thread.sleep(1000); 18 } 19 } 20 } 21 22 class P extends Thread 23 { 24 public void run() 25 { 26 int i = 0; 27 while(true) 28 { 29 i++; 30 System.out.println(Thread.currentThread().getName()+".."+i); 31 try{Thread.sleep(1000);}catch(Exception e){} 32 } 33 } 34 } 35 /* 36 运行结果: 37 main..0 38 t1..1 39 t1..2 40 main..1 41 t1..3 42 main..2 43 main..3 44 t1..4 45 main..4 46 t1..5 47 main..5 48 t1..6 49 main..6 50 t1..7 51 main..7 52 t1..8 53 main..8 54 t1..9 55 main..9 56 t1..10 57 请按任意键继续. . . 58 59 60 */
7.Timer定时器
作用:每隔一段固定的时间执行一段代码
例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /* 2 定时器 3 */ 4 import java.text.*; 5 import java.util.*; 6 class Timertest 7 { 8 public static void main(String[] args) throws Exception 9 { 10 //创建定时器 11 Timer t = new Timer(); 12 //指定定时任务 13 t.schedule(new LogTimerTask(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").parse("2017-04-21 11:42:00 000"),10*1000); 14 15 } 16 } 17 //指定任务 18 class LogTimerTask extends TimerTask 19 { 20 public void run() 21 { 22 System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date())); 23 } 24 }
8.线程池
参考资料 http://www.importnew.com/19011.html
http://lavasoft.blog.51cto.com/62575/27069/
https://mp.weixin.qq.com/s?__biz=MzI1NDQ3MjQxNA==&mid=2247484346&idx=1&sn=f065ebd404771a90988bb097ea4e26dd&chksm=e9c5fa0bdeb2731d12b838372fc8aa97890dd2e3379d1a486df7cf795f8a1538c1ae974a13e7&mpshare=1&scene=23&srcid=1022Iecc2i96j9Drfxgzd274#rd