活动地址:CSDN21天学习挑战赛
文章目录
1、什么是线程
要明白线程是什么必须先弄明白进程
进程:简单理解就是正在运行的程序
线程:线程是进程中的最小单位,一个进程中至少有一个线程
在CPU中没有进程的概念,他只有线程的概念,CPU会快速的切换线程 从而执行代码
线程会议CPU为资源,都来抢占CPU 谁抢占到 就运行谁的线程
注意:在同一时间片中 CPU只能进行一个线程
时间片:指的一个非常微观的时间
2、线程的使用
Thread 是java提供给我们的一个线程类,只要继承了Thread 我们自定义的类就是一个线程类
需求 :
定义了一个游戏类 :他的核心方法是打印 ”正在玩游戏“;
再顶一个音乐类:他的核心方法是 打印 “白龙马…”
线程类中 有一个核心方法 run(); 我们需要对其重写
然后我们将线程对象实例化后 需要通过 Thread类中的start()方法 对线程对象进行线程激活 使其拥有线程抢占权
代码如下
public class Thread_Text_1 {
static class GameThread extends Thread{
@Override
public void run() {
for (int i = 0; i <100; i++) {
System.out.println("---->游戏进行中"+i);
}
}
}
static class MusicThread extends Thread{
@Override
public void run() {
for (int i = 0; i <100; i++) {
System.out.println("---->音乐播放中"+i);
}
}
}
public static void main(String[] args) {
GameThread gameThread=new GameThread();// 创建一个线程对象
gameThread.start();// 激活线程 让对象获取CPU的执行权
MusicThread musicThread=new MusicThread();
musicThread.start();
for (int i = 0; i <100; i++) {
System.out.println("---->main");
}
}
}
在代码中一共存在 三个 线程
1、主方法Main Thread
2、Game Thread
3、Music Thread
3、线程使用的注意事项
在以后的代码书写中 逻辑代码可以直接写在 Thread类中的Run方法中
但是不能直接调用 Run()方法,直接调用Run()方法 不会开启一个线程
而需要调用start()方法才会激活线程,使当前线程处于活跃状态
当一共线程激活启动后 便和主线程没有任何关系了 两个线程不会有任何联系
4、线程使用案例–火车站卖票
需求
当前火车站 一共有200张火车票
然后 开辟了三个买票窗口 同时进行火车票的销售
代码如下
public class Thread_Text_2 {
static class PiaoThread extends Thread{
static int sum=100;
String ThreadName;
public PiaoThread(String threadName){
ThreadName=threadName;
}
@Override
public void run() {
while (sum>0){
System.out.println(ThreadName+"正在出票,票号为"+sum);
sum--;
}
}
}
public static void main(String[] args) {
// 实例化线程对象
PiaoThread piaoThread1=new PiaoThread("窗口一");
PiaoThread piaoThread2=new PiaoThread("窗口二");
PiaoThread piaoThread3=new PiaoThread("窗口三");
// 激活线程
piaoThread1.start();
piaoThread2.start();
piaoThread3.start();
}
}
该代码理论上是成立的。但是 会出现重票的问题 这便涉及到了线程安全
窗口一正在出票,票号为100
窗口三正在出票,票号为100
窗口二正在出票,票号为100
5、线程安全问题
当线程A抢占到了CPU的执行权 并且条件满足 开始出票 票号为100
这时还未执行sum–的操作 CPU便被线程B抢占 因此还是出票的票号依旧是100
多个线程抢占CPU所出现的信息误差现象,这便是线程安全问题
线程安全问题 解决方式:加锁
6、修改线程名
currentThread():该方法用于返回当前所在执行的线程对象的引用
getName():该方法用于返回当前线程的名称
修改线程名 有两种方式
1、通过构造方法给线程取名
static class PiaoThread extends Thread{ static int sum=100; public PiaoThread(String threadName){ super(threadName); } }
2、通setName()方法修改线程名
static class PiaoThread extends Thread{ static int sum=100; public PiaoThread(String threadName){ setName(threadName); } }
7、线程睡眠
Thread中提供了sleep()方法用于使线程在指定时间内进行睡眠
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
8、线程两种实现方式
第一种通过类去继承Thread
直接extends Thread即可
第二种通过自定义类去实现Runnable
new Thread(Rannable 的实现类) 即可
因为 Thread 种只有run()的方法
如果 想要线程活跃起来拥有CPU控制权 就必须通过 Rannable去实现
代码如下
public class Thread_Text_3{
public static void main(String[] args) {
Thread_Runnable thread_runnable1=new Thread_Runnable();
Thread thread1=new Thread(thread_runnable1);
thread1.start();
Thread_Runnable thread_runnable2=new Thread_Runnable();
Thread thread2=new Thread(thread_runnable2);
thread2.start();
Thread_Runnable thread_runnable3=new Thread_Runnable();
Thread thread3=new Thread(thread_runnable3);
thread3.start();
}
static class Thread_Runnable implements Runnable{
static int sum=100;
@Override
public void run() {
while (sum>0){
System.out.println(Thread.currentThread().getName()+"卖出"+sum);
sum--;
}
}
}
}
9、解决线程安全问题
解决线程安全问题有三种办法
1、synchronized 同步代码块
2、同步方法
3、使用Lock锁
这三种方式的本质就是 给代码加锁
注意 :
给代码加锁 一定要保证 需要上锁的代码都是使用的同一把锁
同步代码块如何保证上锁的代码都是同一把锁呢
只需使用字节码对象 每一个类都存在一个唯一的字节码对象
类名.class:即可
1、代码同步块
@Override
public void run() {
while (sum>0){
synchronized (Thread_Runnable.class){
if (sum>0){
System.out.println(Thread.currentThread().getName()+"卖出"+sum+"");
sum--;
}
}
}
}
2、同步方法
同步方法只需将要执行的代码封装成一个方法
在方法void 声明前面添加synchronized 声明即可
注意如果你使用 继承方式实现Thread 那么不可以使用同步方法
3、Lock锁
该方法相比较synchronized 更加的广泛和灵活
我们通过 实例化 Lock对象从而创建一个锁对象
代码如下
public class Thread_Text_4 {
static class Thread_ extends Thread{
int sum=100;
static Lock lock =new ReentrantLock(); // 创建锁对象
@Override
public void run() {
// 上锁
while (sum>0){
lock.lock();
try {
System.out.println(Thread.currentThread().getName()
+ "正在出票,票号为"
+ sum
+ " "
+ Thread.currentThread().getName() +
" "
);
sum--;
}finally {
lock.unlock();
}
}
}
}
public static void main(String[] args) {
Thread_ thread1=new Thread_();
Thread_ thread2=new Thread_();
Thread_ thread3=new Thread_();
thread1.start();
thread2.start();
thread3.start();
}
}
注意:
Lock实例对象一定要确保参与的线程都是使用的同一把锁 所以需要加上Static
然后通过 Lock.lock()方法上锁
通过 Lock.unlock()方法释放锁
使用这两个方法一定要配合 Try…finally 保证即使代码报错也能释放资源