一、线程
1、线程原理
内存原理:线程开启后,都会有一个自己独立的栈内存空间,进行压栈和弹栈。
每个线程中运行的方法其局部变量都是独立的,不能共享。如果数据保存在堆中,那么此时堆中的数据是可以给其他线程给共享
2、Thread类
构造方法:
public Thread()
:分配一个新的线程对象。–用于继承Thread
public Thread(String name)
:分配一个指定名字的新的线程对象。–用于继承Thread
public Thread(Runnable target)
:分配一个带有指定目标新的线程对象。–用于创建实现Runnable接口的对象
public Thread(Runnable target,String name)
:分配一个带有指定目标新的线程对象并指定名字–用于创建实现Runnable接口的对象
常用方法:
public String getName()
:获取当前线程名称。
public void start()
:导致此线程开始执行; Java虚拟机调用此线程的run方法。
public void run()
:此线程要执行的任务在此处定义代码。
public static void sleep(long millis)
:使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
public static Thread currentThread()
:返回对当前正在执行的线程对象的引用。
3、线程的创建方式之一:继承Thread
线程的两种创建方式:
1)使用子类继承Thread,创建线程
2)实现Runnable接口,创建其对象,进行直接创建Thread对象
public Thread(String name)的使用,自定义线程名字
public static void main(String[] args) {
//Thread(String name)创建对象
MyThread mt3 = new MyThread("小强");
mt3.start();//结果是:小强线程执行起来
public class MyThread extends Thread {
public MyThread(){
super(); // public Thread()
}
public MyThread(String name) {
super(name);/// public Thread(String name)
}
@Override
public void run() {
//如果没有给线程起名:默认 Thread-x
String name = getName(); //获取线程的名字
System.out.println(name +"线程执行起来");
}
}
综上可知,继承创建线程方法下,自定义线程名字时:需要子类中构建空参和name参数
4、线程的创建方式之二:实现Runnable接口
一般使用步骤:
1)创建实现Runnable接口的子类,重写run方法
2)构造实现Runnable接口的子类的实例,此实例作为Thread的target来创建Thread的对象,此时的对象才是真正的线程对象
3)调用线程的start方法开启线程
//1. public Thread(Runnable target):分配一个带有指定目标新的线程对象。
//2. public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread mth1 = new Thread(runnable);
mth1.start();//输出结果:Thread-0线程执行run方法【匿名内部类实现】
Thread mth2 = new Thread(runnable, "旺财");
mth2.start();}//输出结果:旺财线程执行run方法【匿名内部类实现】
//匿名内部类方法的实现:
Runnable target = new Runnable() {
@Override
public void run() {
Thread thread = Thread.currentThread();
String name = thread.getName();
System.out.println(name+"线程执行run方法【匿名内部类实现】");}};
new Thread(target).start();
new Thread(target,"旺财").start();
public class MyRunnable(){
@Override
public void run() {
Thread thread = Thread.currentThread();
String name = thread.getName();
System.out.println(name+"线程执行run方法【匿名内部类实现】");
}}
注:Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体。而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法
5、Thread和Runnable的区别
实现Runnable接口比继承Thread类所具有的的优势为:
1、适合多个相同的程序代码的线程去共享同一个资源,如:可重复调用(接口中的成员变量存在堆中,可以重复调用,类的成员变量不可以重复调用)
2、可以避免java中的单继承的局限性
3、增加程序的健壮性,实现解耦操作(耦合容易出错,解耦不容易出错),代码可以被多个线程共享,代码和线程独立
4、线程池只能放入实现Runnable或Callable类线程,不能直接放入继承Thread的类
二、线程安全
结合Runnable使用,因其拥有可共享的堆资源,可使安全方法顺利使用(多次调用下可以上锁使其按照顺序“睡觉时间“排队操作)从而实现安全操作,但类中没有共享的堆资源,每一个都独立存在,故而不能多次按照顺序调用,只能同时调用同时运行,存在隐患。
1、同步代码块:
synchronized(同步锁){
需要同步操作的代码
}
锁对象 可以是任意类型。多个线程对象 要使用同一把锁。
public class TicketSafe1 implements Runnable {
int ticketNum = 100;//在空间中共享
Object lock = new Object();
@Override
public void run() {
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Object lock = new Object(); //不能方放在这里
//同步锁,把需要共同操作资源的代码给包裹起来
synchronized (lock) {
if (ticketNum > 0) {
String name = Thread.currentThread().getName();
System.out.println(name + ticketNum);
ticketNum--;}}}}}
2、同步方法
使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等
public synchronized void method(){
可能会产生线程安全问题的代码
}
public class Ticket2Safe2 implements Runnable {
int ticketNum = 100;
@Override
public void run() {
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
sell();}} //卖票功能
private synchronized void sell() { //同步方法,锁是this
if (ticketNum > 0) {
String name = Thread.currentThread().getName();
System.out.println(name + ticketNum);
ticketNum--;
}}}
3、Lock锁
public void lock()
:加同步锁。
public void unlock()
:释放同步锁
public class Ticket implements Runnable{
private int ticket = 100;
Lock lock = new ReentrantLock();
@Override
public void run() {
while(true){ //每个窗口卖票的操作、窗口永远开启
lock.lock();// synchronized(){
if(ticket>0){//有票 可以卖
try { //出票操作、使用sleep模拟一下出票时间
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
String name = Thread.currentThread().getName();
System.out.println(name+"正在卖:"+ticket--);//获取当前线程对象的名字
}
lock.unlock();}}
三、线程状态
线程状态 | 导致状态发生条件 |
---|---|
NEW(新建) | 线程刚被创建,但是并未启动。还没调用start方法。 |
RUNNABLE(可运行) | 线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器(cpu)。 |
BLOCKED(锁阻塞) | 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。 |
WAITING(无限等待) | 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。 |
TIMED_WAITING(计时等待) | 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Object.wait。 |
Terminated(被终止) | 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。 |