
/*
线程相关概念
程序:为完成特定任务,用某种语言编写的一组指令的集合,简单来说就是我们写的代码
进程
1.进程是指运行中的程序,比如使用IDEA,就启动了一个进程,操作系统就会为该进程分配内存空间
当使用微信,又启动了一个进程,操作系统将为微信分配新的内存空间
2.进程是程序的一次执行过程,或是正在运行的一个程序,是动态过程,有自身产生、存在和消亡的过程
线程
1.线程是由进程创建的,是进程的一个实体
2.一个进程可以拥有多个线程
3.单线程:同一时刻,只允许执行一个线程
4.多线程:同一时刻,可以执行多个线程,如一个微信进程,可以同时打开多个聊天窗口
并发:同一时刻,多个任务交替执行,即单核CPU实现的多任务就是并发
并行:同一时刻,多个任务同时执行,即多核CPU实现多任务就是并行
线程基本使用
创建线程的两种方式
1.继承Thread类,重写run方法
2.实现Runnable接口,重写run方法
Java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时不能在继承Thread类创建线程
//从Java设计上看,这两种方式没有区别,因为Thread类本身就实现了Runnable接口
实现Runnable接口方式更加适合多个线程共享一个资源的情况,且避免了单继承限制
*/
// 演示继承Thread类,重写run方法
public class Thread_ {
public static void main(String[] args) throws InterruptedException {
AA aa = new AA();//创建AA对象
aa.start();//启动线程-->最终执行aa的run方法
//aa.run();是一个普通的方法, 没有真正的启动一个线程,把 run 方法执行完毕,才向下执行
// 部分源码
// public synchronized void start() {
// start0();
// }
//
// start0() 是本地方法,是 JVM 调用, 底层是 c/c++实现
// 真正实现多线程的效果, 是 start0(), 而不是 run
// start()方法调用start0()方法后,该线程并不一定会马上执行,只是将线程变成可运行状态
// 什么时候执行取决于CPU,由CPU统一调度
//当main线程启动一个子线程Thread-0后,主线程会继续执行,所以主线程和子线程是交替执行
for (int i = 0; i < 10; i++) {
System.out.println("主线程执行" + i + "\t线程名=" + Thread.currentThread().getName());
//休眠1秒
Thread.sleep(1000);
}
}
}
//当AA继承了Thread类,AA类就可以当作线程使用
//Thread类实现了Runnable接口的run方法
class AA extends Thread{
int times = 0;
@Override
public void run() {//重写run方法,写上要实现的业务代码
while(true){
System.out.println("hello,java" + (++times) + "\t线程名=" + Thread.currentThread().getName());
//进程---->main线程---->Thread-0
try {//休眠1秒
Thread.sleep(1000);//1000毫秒=1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
if(times == 25){//25次退出while,这时线程也退出
break;
}
}
}
}
// 演示实现Runnable接口,重写run方法
public class Thread02 {
public static void main(String[] args) {
BB bb = new BB();
//bb.start(); 这里不能调用 start
//创建了 Thread 对象,把 bb 对象(实现了Runnable),放入 Thread
Thread thread = new Thread(bb);
thread.start();
}
}
class BB implements Runnable { //通过实现 Runnable 接口,创建线程
int count = 0;
@Override
public void run() {//重写run方法
while (true) {
System.out.println("hello" + (++count) + "\t线程名=" + Thread.currentThread().getName());
//休眠 1 秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 10) {//10次后线程退出
break;
}
}
}
}
// 演示 代码模拟 实现Runnable接口 开发线程的机制
public class Thread03 {
public static void main(String[] args) {
CC cc = new CC();
Proxy proxy = new Proxy(cc);
proxy.star();
}
}
class Proxy implements Runnable{//把Proxy当作Thread
private Runnable target = null;//属性,类型为Runnable
@Override
public void run() {
if (target != null){
target.run();
}
}
public Proxy(Runnable target){
this.target = target;
}
public void star(){
start0();
}
public void start0(){
run();
}
}
class CC implements Runnable{
@Override
public void run() {
System.out.println("hei~");
}
}
/*
线程终止
1.线程完成任务时自动终止
2.设置一个变量控制run方法退出停止线程,即通知方式
*/
//启动线程t,在main线程去停止线程t
public class Thread05 {
public static void main(String[] args) throws InterruptedException {
T t = new T();
t.start();
//修改loop,让t线程退出---->通知方式
Thread.sleep(2000);//休眠2秒后,通知t线程退出
t.setLoop(false);
}
}
class T extends Thread{
private int count = 0;
//设置一个控制变量loop
private boolean loop = true;
@Override
public void run() {
while (loop){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程运行中......." + (++count));
}
}
public void setLoop(boolean loop) {
this.loop = loop;
}
}
/*
线程常用方法
setName //设置线程名称
getName //获取线程名称
start //开始执行线程
run //调用线程对象run方法
setPriority //更改线程优先级
getPriority //获取线程的优先级
sleep //在指定毫秒内让当前执行的线程休眠
interrupt //中断线程
yield //线程礼让,让其他线程先执行,但不一定礼让成功
join //线程插队,先执行完插队的线程的所有任务
细节
1.start底层会创建新的线程,调用run,run就是一个简单的方法调用,不会启动新线程
2.线程优先级的范围
MAX_PRIORITY 10
MIN_PRIORITY 1
NORM_PRIORITY 5
3.interrupt中断线程并没有结束线程,一般用于中断正在休眠的线程
*/
public class Thread06 {
public static void main(String[] args) throws InterruptedException {
DD dd = new DD();
dd.setName("jack");//设置线程名称
dd.setPriority(Thread.MIN_PRIORITY);//更改线程优先级
dd.start();//启动子线程
//中断子线程休眠
System.out.println("中断子线程休眠.....");
dd.interrupt();
System.out.println("线程优先级=" + dd.getPriority());
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
System.out.println("主线程" + i);
if(i == 5){
System.out.println("让子线程插队");
dd.join();
System.out.println("子线程执行完毕,主线程继续执行....");
}
}
}
}
class DD extends Thread{
int count = 0;
@Override
public void run() {
while (true) {
for (int i = 0; i < 10; i++) {
//Thread.currentThread().getName()获取当前线程名称
System.out.println("hi\t" + Thread.currentThread().getName() + i);
count++;
}
if (count > 20){
break;
}
System.out.println("休眠中.......");
try {
Thread.sleep(10000);//休眠10秒
} catch (InterruptedException e) {//执行到interrupt方法时,InterruptedException e捕获中断异常
System.out.println(Thread.currentThread().getName() + "被中断了");
}
}
}
}
/*
用户线程:也叫工作线程,当线程的任务执行完毕 或 通知方式 后结束
守护线程:一般是为工作线程服务的,当所有用户线程结束,守护线程自动结束,如:垃圾回收机制
*/
public class Thread07 {
public static void main(String[] args) throws InterruptedException {
FF ff = new FF();
//当main线程结束后,子线程自动结束---->将子线程设为守护线程
ff.setDaemon(true);
ff.start();
for (int i = 1;i <= 10;i++){
System.out.println("主线程执行中.....");
Thread.sleep(1000);
}
}
}
class FF extends Thread{
@Override
public void run() {
for (;;){//无限循环
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程执行中.......");
}
}
}
/*
线程的生命周期
JDK 中用 Thread.State 枚举表示了线程的几种状态
New //尚未启动的线程处于此状态
Runnable(2种) //在Java虚拟机执行的线程处于此状态
Ready //线程被挂起
Running //线程被执行
Blocked //被阻塞等待监视器锁定的线程处于此状态
Waiting //正在等待另一个线程执行特定动作的线程处于此状态
Timed_Waiting //正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
Terminated //已经退出的线程处于此状态
*/
public class Thread08 {
public static void main(String[] args) throws InterruptedException {
GG gg = new GG();//创建
System.out.println(gg.getName() + " 状态为 " + gg.getState());//NEW
gg.start();//启动
while (Thread.State.TERMINATED != gg.getState()) {
System.out.println(gg.getName() + " 状态为 " + gg.getState());
Thread.sleep(500);
}
System.out.println(gg.getName() + " 状态为 " + gg.getState());
}
}
class GG extends Thread {
@Override
public void run() {
while (true) {
for (int i = 0; i < 10; i++) {
System.out.println("hi " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;//退出
}
}
}
//模拟三个窗口同时售票,共100张---->同时进行出现超卖现象(出现负数)--->使用Synchronized
public class Thread04 {
public static void main(String[] args) {
SellTicket sellTicket = new SellTicket();
new Thread(sellTicket).start();//第一个窗口
new Thread(sellTicket).start();//第二个窗口
new Thread(sellTicket).start();//第三个窗口
}
}
class SellTicket implements Runnable{
private int ticketNum = 100;//多个线程共享ticketNum
@Override
public void run() {
while (true){
if (ticketNum <= 0){
System.out.println("===============票已卖光===============");
break;
}
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票,剩余票数=" + (--ticketNum));
}
}
}
/*
Synchronized
线程同步机制
1.在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术
保证在同一时刻最多只有一个线程访问,以保证数据完整性
2.线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作
直到该线程完成操作,其他线程才能对该内存地址进行操作
同步方法
1.同步代码块
synchronized (对象){//得到对象的锁,才能操作同步代码
//需要被同步的代码
}
2.放在方法声明种
public synchronized void m(String name){
//需要被同步的代码
}
互斥锁
1.Java引入了对象互斥锁的概念,来保证共享数据操作的完整性
2.每个对象对应于一个可称为"互斥锁"的标记,这个标记用来保证在任意时刻,只能有一个线程访问对象
3.关键字synchronized用来与对象的互斥锁联系,当某个对象用synchronized修饰时,表明该对象在任意时刻只能有一个线程访问
4.同步的局限性:导致程序的执行效率降低
5.同步非静态方法的锁可以是this,也可以是其他对象
同步静态方法的锁为当前类本身,即当前类.class
步骤
1.分析需要上锁的代码
2.选择方法:同步方法或同步代码块
3.要求多个线程的锁对象为同一个
线程死锁
多个线程占用了对方的锁资源,不肯想让,导致死锁
如:A:你先去干活,才能给钱. B:你给我钱,才能干活
释放锁
1.当前线程的同步方法、同步代码块执行结束
2.当前线程的同步方法、同步代码块中有break,return
3.当前线程的同步方法、同步代码块出现了未处理的ERROR或Exception,导致异常结束
4.当前线程的同步方法、同步代码块执行了wait()方法
注意下列操作不会释放锁:
1.当前线程的同步方法、同步代码块调用Thread.sleep(),Thread.yield()方法暂停当前线程执行
2.当前线程的同步代码块执行,其他线程调用了该线程的suspend()方法将线程挂起
*/
//解决售票超卖问题
//使用synchronized互斥锁实现线程同步
public class Thread09 {
public static void main(String[] args) {
SellTicket2 sellTicket2 = new SellTicket2();
new Thread(sellTicket2).start();//第一个窗口
new Thread(sellTicket2).start();//第二个窗口
new Thread(sellTicket2).start();//第三个窗口
}
}
@SuppressWarnings({"all"})
class SellTicket2 implements Runnable{
private int ticketNum = 100;//多个线程共享ticketNum
private boolean loop = true;
Object object = new Object();//非静态方法的锁可以是this,也可以是其他对象
// 静态方法中实现同步,锁在类本身 SellTicket2.class
// public static void sell2(){
// synchornized(SellTicket2.class){}
// }
//public synchronized void sell(){}就是一个同步方法,锁在this对象
public /*synchronized*/ void sell(){//同步方法
synchronized (/*this*/object) {//同步代码块,可以是this对象,也可以是其他对象
if (ticketNum <= 0) {
System.out.println("===============票已卖光===============");
loop = false;
return;
}
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票,剩余票数=" + (--ticketNum));
}
}
@Override
public void run() {
while (loop){
sell();//调用sell()
}
}
}
// 演示死锁
public class Thread10 {
public static void main(String[] args) {
//模拟死锁现象
DeadLockDemo A = new DeadLockDemo(true);
A.setName("A 线程");
DeadLockDemo B = new DeadLockDemo(false);
B.setName("B 线程");
A.start();
B.start();
}
}
class DeadLockDemo extends Thread {
static Object o1 = new Object();// 保证多线程,共享一个对象,这里使用 static
static Object o2 = new Object();
boolean flag;
public DeadLockDemo(boolean flag) {//构造器
this.flag = flag;
}
@Override
public void run() {
//1. 如果 flag 为 T, 线程 A 就会先得到 o1 对象锁, 然后尝试去获取 o2 对象锁
//2. 如果线程 A 得不到 o2 对象锁,就会 Blocked 阻塞
//3. 如果 flag 为 F, 线程 B 就会先得到 o2 对象锁, 然后尝试去获取 o1 对象锁
//4. 如果线程 B 得不到 o1 对象锁,就会 Blocked 阻塞
if (flag) {
synchronized (o1) {//对象互斥锁
System.out.println(Thread.currentThread().getName() + " 得到o1对象锁");
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " 得到o2对象锁");
}
}
} else {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + " 得到o2对象锁");
synchronized (o1) {
System.out.println(Thread.currentThread().getName() + " 得到o1对象锁");
}
}
}
}
}