进程:进行(执行)中的应用程序,我们称之为进程。进程属于CPU分配资源的最小单位。
线程:线程属于CPU执行调度的最小单位。
1.线程包含在进程之中,一个进程至少包含一个线程。
举例:进程和线程就像车身和车轮,车轮不是越多越好(线程不是越多越好,要根据实际的硬件环境)
多个线程是由CPU根据每个线程分配的时间片来决定随机轮流执行的。每个线程最多占用20ms,过了这个时间将切换的其他的线程。
举例:比如你去饭店点了两盘菜,最后你将这两盘菜吃完了,从饭店老板的角度来看属于同时吃完的,但是严格意义上来说,属于轮流交替吃完的。这中属于并发。并行是指比如你和你的同桌两个人,一人点一盘菜,这样才属于并行。
并发:同时发生,轮流交替来执行
并行:真正意义上的同时执行
1.继承Thread类,重写run()方法
2.适用于单继承,书写简单
package com.qfedu.test1; /** * 创建线程方式1: * 继承Thread类 重写run方法 创建线程对象 * @author WHD * */ public class Test2 extends Thread{ @Override public void run() { System.out.println(Thread.currentThread().getName() + "在 执行"); } public static void main(String[] args) { Test2 th1 = new Test2(); th1.start(); // th1.run(); Test2 th2 = new Test2(); th2.start(); } }实现Runnable接口,重写run()方法
1.避免了单继承的局限性
2.便于数据的共享
package com.qfedu.test1; /** * 创建线程方式2: * 实现Runnable接口 重写run方法 * * @author WHD * */ public class Test3 implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName() + "在执行"); } public static void main(String[] args) { Test3 t = new Test3(); Thread th1 = new Thread(t, "线程A"); th1.start(); // th1.run(); } }调用start方法将会开启一个新的线程,但是调用run方法不会开启新的线程,只是使用main线程调用方法。
1.创建 new Thread对象
2.就绪 start方法
3.运行 run方法
4.阻塞 sleep 、join方法
5.死亡 线程执行完毕
package com.qfedu.test2; /** * 线程的状态 * @author WHD * */ public class Test2 implements Runnable{ @Override public void run() { // 执行 try { // 阻塞 Thread.sleep(2000); // 休眠属于阻塞状态的表现之一 } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "线程" + i); } // 死亡 } public static void main(String[] args) { Test2 t1 = new Test2(); Thread th1 = new Thread(t1,"线程A"); // 创建 Thread th2 = new Thread(t1,"线程B"); th1.start(); // 就绪 th2.start(); } }currentThread() 获取当前线程对象
getName() 获取线程名称
setName() 设置线程名称
getPriority() 获取线程优先级
stop() 停止当前线程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dp5tmCjH-1603197207912)(.\img\线程方法1.png)]
package com.qfedu.test2; /** * 线程的优先级 * 默认为5 最大10 最小1 * MAX_PRIORITY 10 * MIN_PRIORITY 1 * NORM_PRIORITY 5 * setPriority()设置优先级 * getPriority()获取优先级 * @author WHD * */ public class Test3 extends Thread{ @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "线程执行" + i); } } public static void main(String[] args) { Test3 th1 = new Test3(); Test3 th2 = new Test3(); th1.setName("线程A"); th2.setName("线程B"); th1.setPriority(Thread.MAX_PRIORITY); th2.setPriority(Thread.MIN_PRIORITY); System.out.println(th1.getPriority()); System.out.println(th2.getPriority()); th1.start(); th2.start(); } } package com.qfedu.test2; /** * 线程的休眠 * sleep(long 毫秒) * sleep(long 毫秒,int 纳秒) * @author WHD * */ public class Test4 extends Thread{ @Override public void run() { // 检查异常 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()); } public static void main(String[] args) { Test4 th1 = new Test4(); th1.start(); } } package com.qfedu.test2; /** * 线程的插队 * join() 直到插队线程执行完毕 被插队线程再继续执行 * join(long 毫秒) 被插队线程等待指定的时间 时间过后继续轮流交替来执行 * join(long 毫秒,int 纳秒) 被插队线程等待指定的时间 时间过后继续轮流交替来执行 * @author WHD * */ public class Test5 extends Thread{ @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + "在执行" + i + "============================"); } } public static void main(String[] args) throws InterruptedException { Test5 th1 = new Test5(); th1.start(); for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "主线程" + i); if(i == 10) { th1.join(); } } } } package com.qfedu.test2; /** * 线程的礼让 * 并不能保证一定会礼让其他线程 只是提供一种可能 用于缓解并发的问题 * yield() 礼让 * @author WHD * */ public class Test6 extends Thread{ @Override public void run() { for (int i = 0; i < 20; i++) { if(i == 3) { System.out.println("线程礼让"); Thread.yield(); // 当前线程会礼让 如果此时本来执行的是线程A 执行完礼让 有可能会执行线程B } System.out.println(Thread.currentThread().getName() + "在执行"); } } public static void main(String[] args) { Test6 th1 = new Test6(); Test6 th2 = new Test6(); th1.setName("线程A"); th2.setName("线程B"); th1.start(); th2.start(); } } package com.qfedu.test2; /** * 线程的中断(停止) * interrupt() 设置线程中断标识为true 标识 此线程可以被中断 * interrupted() 打印当前线程的是否可被中断 * stop() 停止线程 * @author WHD * */ public class Test7 extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { if(i == 5) { Thread.currentThread().interrupt(); Thread.currentThread().stop(); } System.out.println(Thread.currentThread().getName() + "再执行" + i + "是否可被中断状态" + Thread.interrupted()); } } public Test7(String name) { super(name); } public static void main(String[] args) { Test7 th1 = new Test7("线程A"); th1.start(); } } package com.qfedu.test2; /** * isAlive() 判断线程是否还存活 * @author WHD * */ public class Test8 extends Thread{ @Override public void run() { for (int i = 0; i < 10; i++) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "执行" + i); } } public static void main(String[] args) throws InterruptedException { Test8 th1 = new Test8(); th1.setName("线程A"); th1.start(); Thread.sleep(1000); System.out.println("是否还存活" + th1.isAlive()); Thread.sleep(5000); System.out.println("是否还存活" + th1.isAlive()); } }在多线程访问同一个资源的情况,因为线程运行的特点:线程是随机轮流交替来执行的,所以多个线程访问同一个数据,会出现问题,我们可以使用同步关键字来解决
synchronized:
1.用于修饰代码块
a.同一时间只能有一个线程访问同步代码块中的内容
b.但是 其他线程可以访问没有被同步代码块包括的代码
c.其他的同步代码块 同样被锁定 (这些多个同步代码块所锁定的是同一个对象 比如 this)
2.用于修饰方法
表示被同步关键字修饰的方法同一时间只能有一个线程访问
package com.qfedu.test4; /** * 假设有10张票 * 三个人抢票 * 票号是唯一的 总数不能超过10张 * 目前存在问题: * 数量不能保证10个 * 票号不能保证唯一 * synchronized 同步的意思 * 适用场景 * 1.同步代码块 * a.同一时间只能有一个线程访问同步代码块中的内容 * b.但是 其他线程可以访问没有被同步代码块包括的代码 * c.其他的同步代码块 同样被锁定 (这些多个同步代码块所锁定的是同一个对象 比如 this) * 2.同步方法 * a.表示当前方法同一时间只有一个线程可以访问 * @author WHD * */ public class BuyTicket2 implements Runnable{ private int ticketCount = 10; Object obj = new Object(); @Override public void run() { while(true) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } // synchronized 同步 synchronized (obj) { if(ticketCount == 0) { break; } ticketCount --; System.out.println(Thread.currentThread().getName() + "抢到第"+(10 - ticketCount) +"张票,还剩余" + ticketCount); } // synchronized (this) { // System.out.println("hello word"); // } // 其他逻辑代码 } } public static void main(String[] args) { BuyTicket2 bt = new BuyTicket2(); Thread zhaosi = new Thread(bt, "赵四"); Thread guangkun = new Thread(bt, "广坤"); Thread dana = new Thread(bt, "大拿"); zhaosi.start(); guangkun.start(); dana.start(); } } package com.qfedu.test4; /** * 假设有10张票 * 三个人抢票 * 票号是唯一的 总数不能超过10张 * 目前存在问题: * 数量不能保证10个 * 票号不能保证唯一 * synchronized 同步的意思 * 适用场景 * 1.同步代码块 * a.同一时间只能有一个线程访问同步代码块中的内容 * b.但是 其他线程可以访问没有被同步代码块包括的代码 * c.其他的同步代码块 同样被锁定 (这些多个同步代码块所锁定的是同一个对象 比如 this) * 2.同步方法 * a.表示当前方法同一时间只有一个线程可以访问 * @author WHD * */ public class BuyTicket3 implements Runnable{ private int ticketCount = 10; Object obj = new Object(); @Override public synchronized void run() { while(ticketCount > 0) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } ticketCount --; System.out.println(Thread.currentThread().getName() + "抢到第"+(10 - ticketCount) +"张票,还剩余" + ticketCount); } } public static void main(String[] args) { BuyTicket3 bt = new BuyTicket3(); Thread zhaosi = new Thread(bt, "赵四"); Thread guangkun = new Thread(bt, "广坤"); Thread dana = new Thread(bt, "大拿"); zhaosi.start(); guangkun.start(); dana.start(); } }线程安全:StringBuffer,Vector,Hashtable 以上这些类的方法都使用同步关键字修饰
线程不安全:StringBuilder,ArrayList,LinkedList,HashSet,HashMap,TreeSet…… 没有使用同步关键字修饰
() + “抢到第”+(10 - ticketCount) +“张票,还剩余” + ticketCount); } }
public static void main(String[] args) { BuyTicket3 bt = new BuyTicket3(); Thread zhaosi = new Thread(bt, "赵四"); Thread guangkun = new Thread(bt, "广坤"); Thread dana = new Thread(bt, "大拿"); zhaosi.start(); guangkun.start(); dana.start(); }}
#### 6. 线程安全的类 > 线程安全:StringBuffer,Vector,Hashtable 以上这些类的方法都使用同步关键字修饰 > > 线程不安全:StringBuilder,ArrayList,LinkedList,HashSet,HashMap,TreeSet…… 没有使用同步关键字修饰