线程、同步

一 线程

多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属栈内存空间。进行方法的压栈和弹栈。

当执行线程的任务结束了,线程自动在栈内存中释放了。但是当所有的执行线程都结束了,那么进程就结束了。

1.1 Thread类

获取线程的名称(两种方式):

1,使用Thread类中的方法getName,String getName()返回该线程的名称。

2,可以先获取到当前正在执行的线程,使用线程中的方法getName()获取线程的名称 static Thread currentThread()返回当前正在执行的线程对象的引用。

设置线程的名称:

1,使用Thread类中的方法setName,void setName(String name)使之与参数name相同。

2,创建一个带参数的构造方法,参数传递线程的名字,调用父类带参数的构造方法,把线程名称传递给父类,让父类Thread给子线程起一个名字分配给Thread。

构造方法:

public Thread() :分配一个新的线程对象。

public Thread(String name) :分配一个指定名字的新的线程对象。

public Thread(Runnable target):分配一个带有指定目标新的线程对象。

public Thread(Runnable target,String name) :分配一个带有指定目标新的线程对象并指定名字。

常用方法:

public String getName() :获取当前线程名称。

public void start() :导致此线程开始执行; Java虚拟机调用此线程的run方法。

public void run():此线程要执行的任务在此处定义代码。

public static void sleep(long millis) :使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。毫秒结束后,程序继续执行

public static Thread currentThread() :返回对当前正在执行的线程对象的引用。

1.2 创建线程方式二

采用java.lang.Runnable 也是非常常见的一种,我们只需要重写run方法即可。

步骤如下:

  1. 创建Runnable接口的实现类,

  2. 重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

  3. 创建Runnable实现类的对象,构造方法中传递Runnable接口的实现类对象。

  4. 调用线程对象的start()方法来启动线程。

代码:

public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}

public class Demo {
public static void main(String[] args) {
//创建自定义类对象 线程任务对象
MyRunnable mr = new MyRunnable();
//创建线程对象
Thread t = new Thread(mr, "小强");
t.start();
for (int i = 0; i < 20; i++) {
System.out.println("旺财 " + i);
}
}
}

1.3 Thread和Runnable的区别

实现Runnable接口创建多线程程序的好处:

1,避免了单继承的局限性

一个类只能继承一个类(一个人只有一个亲爹),类继承了Thread类就不能继承其他的类。实现了Runnable接口,还可以继承其他的类,实现其他的接口。

2,曾强了程序的扩展性,降低了程序的耦合性(解耦)

实现类中,重写了run方法;用来设置线程任务

创建Thread类对象,调用start方法,开启新线程

1.4 匿名内部类方式实现线程的创建

匿名:没名字

内部类:写在其他类内部的类

匿名内部类:简化代码。

把子类继承父类,重写父类的方法,创建子类对象一步完成

把实现类接口,重写接口中的方法,创建实现类对象合成一步完成

格式: new 父类/接口(){ 重复父类/接口中的方法 };

public class NoNameInnerClassThread {
public static void main(String[] args) {
// new Runnable(){
// public void run(){
// for (int i = 0; i < 20; i++) {
// System.out.println("黑马:"+i);
// }
// }
// }; //‐‐‐这个整体 相当于new MyRunnable()
Runnable r = new Runnable(){
public void run(){
for (int i = 0; i < 20; i++) {
System.out.println("程序员:"+i);
}
}
};
new Thread(r).start();

二 线程安全问题

如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的

线程安全产生原理:

 2.1  同步代码块

同步代码块: synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
格式:

synchronized(锁对象){
可能会出现线程安全问题 代码(访问了共享数据的代码)
}

注意:

1,通过代码块中的锁对象,可以使用任意的对象。

2,但是必须保证多个线程使用的锁对象是同一个。

3,锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行。

public class Ticket implements Runnable{
private int ticket = 100;

//创建一个锁对象
Object lock = new Object();
/*
* 执行卖票操作
*/
@Override
public void run() {
//每个窗口卖票的操作
//窗口 永远开启
while(true){
synchronized (lock) {
if(ticket>0){//有票 可以卖
//出票操作
//使用sleep模拟一下出票时间
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto‐generated catch block
e.printStackTrace();
}
//获取当前线程对象的名字
String name = Thread.currentThread().getName();
System.out.println(name+"正在卖:"+ticket‐‐);
}
}

谁抢到CPU执行权,发现同步锁,会进入到同步锁对象中,另一个线程抢到了,看到同步锁,需要进入锁对象才能执行,否则就堵塞等待锁对象的释放。

解决线程安全的两种方案:使用同步方法

使用步骤:

1,把访问了共享数据的代码抽出来,放到一个方法中

2,在方法上添加共享synchronized修饰符

格式:定义方法的格式

修饰符 synchronized 返回值类型 方法名(参数列表){
   可能会出现线程安全问题的代码(访问了共享数据的代码)
}

静态同步方法:

静态方法的锁对象是本类的class属性--》class文件对象(反射)

2.2  LOCK锁

Lock实现提供了比synchronized方法和语句可获得的更广泛的操作

Lock锁也称同步锁,加锁与释放锁方法化了,如下:
public void lock() :加同步锁。
public void unlock() :释放同步锁。

使用步骤:

1,在成员位置创建一个Reentrantlock对象

2,在可能出现安全问题的代码前调用Lock接口中的方法Lock获取锁

3,在可能出现安全问题的代码后调用Lock接口中方法unlock释放锁

三  线程状态

线程状态导致状态发生条件


NEW(新建) 线程刚被创建,但是并未启动。还没调用start方法。

Runnable(可运行)线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。

Blocked(锁阻塞)当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态

Waiting(无限等待)一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
TimedWaiting(计时等待)同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法Thread.sleep 、Object.wait。

Teminated(被终止)因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

3.1 Waiting(无限等待)

线程之间的通信

注意事项:

线程必须使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行

同步使用的锁对象必须保证唯一

只有锁对象才能调用wait和notify方法

wait带参数方法:

进入到TimeWaiting(计时等待)有两种方式

1,使用sleep(Long m)方法,在毫秒结束后,线程喊醒进入到Runnable/Blocked状态

2,使用wait(Long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,会自动唤醒,线程进入到Runnable/Blocked状态

notifyAll:唤醒所有等待的线程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值