进程和线程
进程:应用程序的执行实例,有独立的内存空间和系统资源
线程:CPU调度和分派的基本单位,进程中执行运算的最小单位,可完成一个独立的顺序控制流程
进程包含线程,一个进程至少有一个线程。
多线程
什么是多线程?
1.如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”
2.多个线程交替占用CPU资源,而非真正的并行执行
多线程好处
1.充分利用CPU的资源
2.简化编程模型
3.带来良好的用户体验
主线程
1.main()
方法即为主线程入口
2.产生其他子线程的线程
3.必须最后完成执行,因为它执行各种关闭动作
使用线程的步骤
1.定义线程
2.创建线程对象
3.启动线程
4.终止线程
在Java中创建线程的两种方式
Thread类:
Java提供了java.lang.Thread
类支持多线程编程
步骤:
1.定义MyThread
类继承Thread类
2.重写run()方法,编写线程执行体
3.创建线程对象,调用start()方法启动线程
//第一步:创建MyThread类继承Thread public class MyThread extends Thread{ //第二步:重写Thread中的run方法 @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+"-"+i); } } }
public class MyThreadTest { public static void main(String[] args) { //第三步:创建线程类对象 MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); mt1.setName("线程A"); mt2.setName("线程B"); //mt1.run( ); //启动线程要使用strart()方法来启动线程,如果通过线程对象直接调用run()方法, //那是由主线程来调用的,不是多现成的实现方式 mt1.start(); mt2.start(); } /* *多线程并不是真正意义上的并行执行,而是多个线程交替性抢占CPU资源, *谁获取了CPU资源就执行谁*在执行过程中,有可能丢失CPU资源, *被另外一个线程执行,所以会出现交替执行的现象 * ***/ }
Runnable接口:
实现java.lang.Runnable
接口
步骤:
1.定义MyRunnable
类
2.实现Runnable接口实现run()方法,编写线程执行体
3.创建线程对象,调用start()方法启动线程
//1.定义MyRunnable类 public class MyRunnable implements Runnable{ //2.实现Runnable接口实现run()方法,编写线程执行体 @Override public void run() { for(int i =1;i<=10;i++){ System.out.println( Thread.currentThread( ).getName()+" :"+i); } } }
public class MyRunnableTest { public static void main(String[] args) { // 创建线程对象,调用start()方法启动线程 MyRunnable mr1 = new MyRunnable(); // MyRunnable类中没有start()方法,其父类Object类中也没有start()方法,其实现的接口Runnable中只有run()方法,没有start()方法 Thread thread1 = new Thread(mr1,"线程A"); thread1.start(); Thread thread2 = new Thread(mr1,"线程B"); thread2.start(); Thread thread3 = new Thread(mr1,"线程C"); thread3.start(); } }
比较两种创建线程的方式
继承Thread类
1.编写简单,可直接操作线程
2.适用于单继承
实现Runnable接口
1.避免单继承局限性
2.便于共享资源
线程的状态
线程调度
线程调度指按照特定机制为多个线程分配CPU的使用权
方 法 | 说 明 |
---|---|
void setPriority(int newPriority) | 更改线程的优先级 |
static void sleep(long millis) | 在指定的毫秒数内让当前正在执行的线程休眠 |
void join() | 等待该线程终止 |
static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
void interrupt() | 中断线程 |
boolean isAlive() | 测试线程是否处于活动状态 |
线程优先级
线程优先级由1~10表示,1最低,默认优先级为5优先级高的线程获得CPU资源的概率较大
public static void main(String[] args) { MyRunnable mr = new MyRunnable(); Thread t1= new Thread (mr,"线程A"); Thread t2 = new Thread ( mr,"线程B"); //输出两个线程的优先级 System.out.println(t1.getPriority()); System.out.println(t2.getPriority()); t1.setPriority (Thread.MAX_PRIORITY); t2.setPriority (Thread.MIN_PRIORITY); // 输出两个线程的优先级 System.out.println(t1.getPriority()); System.out.println(t2.getPriority()); t1.start(); t2.start(); //线程优先级高只能说明线程获取CPU资源的概率大一些, //不能保证每一次优先级高的线程先获取CPU资源 }
线程休眠
让线程暂时睡眠指定时长,线程进入阻塞状态睡眠时间过后线程会再进入可运行状态
public class MyRunnable implements Runnable { public void run() { for (int i = 1; i <= 10; i++) { System.out.println(Thread.currentThread().getName() + ": " + i); if (i == 5) { try { System.out.println(Thread.currentThread().getName()+"开始休眠"); //线程休眠5s Thread.sleep(5000); System.out.println(Thread.currentThread().getName()+"休眠结束"); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
线程的强制运行
使当前线程暂停执行,等待其他线程结束后再继续执行本线程
-
public final void join()
-
public final void join(long mills)
-
public final void join(long mills,int nanos)
millis
:以毫秒为单位的等待时长
nanos
:要等待的附加纳秒时长
需处理InterruptedException
异常
public class MyRunnableTest { public static void main(String[] args) { Thread tr = new Thread(new MyRunnable(), "线程A"); tr.start(); for (int i = 1; i <= 10; i++) { if (i == 3) { try { /* * 线程强制执行:tr强制执行,一直等到tr执行完毕之后, * 才会释放CPU资源给其它线程执行,在调用join()方法之前, * 多个线程依然是抢占CPU */ tr.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() + ": " + i); } } }
线程的礼让
暂停当前线程,允许其他具有相同优先级的线程获得运行机会该线程处于就绪状态,不转为阻塞状态
public class MyRunnable implements Runnable { public void run() { for (int i = 1; i <= 10; i++) { System.out.println(Thread.currentThread().getName() + ": " + i); if(i==3){ System.out.print("线程礼让:"); /* * 线程礼让:让当前执行run()方法的线程释放CPU资源, * 给其它线程一个获得CPU资源的机会, * 但是当前线程还会抢占CPU资源,也就是说, * 当前线程释放CPU资源后,会与其它线程再一次抢占CPU, * 就看其它线程能不能抓住这个机会 */ Thread.yield(); } } } }
多线程共享数据引发的问题
public class Site implements Runnable { private int count = 10; private int num = 0; @Override public void run() { while (true) { if (count <= 0) { break; } //如果票的数量小于日,就不在卖票 count--; num++; //买票过程中模拟网速缓慢 System.out.println(Thread.currentThread().getName() + "买到了第" + num + "张票,还剩余" + count + "张票"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
public class Test { public static void main(String[] args) { Site site = new Site( ); Thread t1 = new Thread( site,"张三"); Thread t2 = new Thread( site,"李四"); Thread t3 = new Thread( site,"王五"); t1.start(); t2.start(); t3.start(); /* * 当多个线程操作共享数据的时候容易出现问题: * 在一个线程操作这个共享数据的时候,还没有完全操作完毕, * 线程失去了CPU资源,CPU被另外一个线程获取, * 另外一个线程会接着上一个线程操作的数据继续进行操作 * 当另外一个线程操作数据完毕之后,第一个线程又获取了CPU资源, * 此时,第一个线程看到的数据是第二个线程操作后的数据, * 由此就会引发数据不安全的问题。 */ } }
运行结果 张三买到了第3张票,还剩余7张票 李四买到了第3张票,还剩余7张票 王五买到了第3张票,还剩余7张票 王五买到了第6张票,还剩余4张票 张三买到了第6张票,还剩余4张票 李四买到了第6张票,还剩余4张票 李四买到了第7张票,还剩余3张票 王五买到了第7张票,还剩余3张票 张三买到了第7张票,还剩余3张票 张三买到了第8张票,还剩余2张票 李四买到了第8张票,还剩余2张票 王五买到了第8张票,还剩余2张票 张三买到了第10张票,还剩余0张票 王五买到了第10张票,还剩余0张票
同步方法
1.使用synchronized修饰的方法控制对类成员变量的访问
访问修饰符 synchronized 返回类型 方法名(参数列表){……}
或者
synchronized 访问修饰符 返回类型 方法名(参数列表){……}
public class Site implements Runnable { private int count = 10; private int num = 0; @Override public void run() { while (true) { if(!sale()){ break; } } } public synchronized boolean sale() { if (count <= 0) { return false; } //如果票的数量小于日,就不在卖票 count--; num++; //买票过程中模拟网速缓慢 System.out.println(Thread.currentThread().getName() + "买到了第" + num + "张票,还剩余" + count + "张票"); try { Thread.sleep(1000); //this.notifyAll(); //this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return true; } }
运行结果: 张三买到了第1张票,还剩余9张票 张三买到了第2张票,还剩余8张票 张三买到了第3张票,还剩余7张票 王五买到了第4张票,还剩余6张票 王五买到了第5张票,还剩余5张票 王五买到了第6张票,还剩余4张票 王五买到了第7张票,还剩余3张票 王五买到了第8张票,还剩余2张票 王五买到了第9张票,还剩余1张票 李四买到了第10张票,还剩余0张票
2使用synchronized关键字修饰的代码块
public class Site implements Runnable { private int count = 10; private int num = 0; @Override public void run() { while (true) { synchronized (this) { if (count <= 0) { break; } // 如果票的数量小于日,就不在卖票 count--; num++; // 买票过程中模拟网速缓慢 System.out.println(Thread.currentThread().getName() + "买到了第" + num + "张票,还剩余" + count + "张票"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
运行结果: 张三买到了第1张票,还剩余9张票 张三买到了第2张票,还剩余8张票 张三买到了第3张票,还剩余7张票 张三买到了第4张票,还剩余6张票 张三买到了第5张票,还剩余5张票 张三买到了第6张票,还剩余4张票 王五买到了第7张票,还剩余3张票 王五买到了第8张票,还剩余2张票 李四买到了第9张票,还剩余1张票 李四买到了第10张票,还剩余0张票
线程安全的类型
** ** | 方法是否同步 | 效率比较 | 适合场景 |
---|---|---|---|
线程安全 | 是 | 低 | 多线程并发共享资源 |
非线程安全 | 否 | 高 | 单线程 |
常见类型对比
Hashtable && HashMap
Hashtable
1.继承关系实现了Map接口,Hashtable
继承Dictionary类
2.线程安全,效率较低
3.键和值都不允许为null
HashMap
1.继承关系实现了Map接口,继承AbstractMap
类
2.非线程安全,效率较高
3.键和值都允许为null
StringBuffer && StringBuilder
前者线程安全,后者非线程安全