一、线程
- 1 多线程原理:在主线程的过程中,开启一个新的线程,其实是在栈空间内又开辟了新的空间,进行方法的压栈和弹栈。
1.2 Thread常用方法:
getName():获取当前线程名称。
start();:开始这个线程(调用run方法)
run(); :此线程要执行的任务在此处定义代码
sleep(); :使当前线程以指定的毫秒数暂停
Thread currentThread(); : 获取当前的线程名称
1.3创建线程的第二种方法:
采用 java.lang.Runnable 也是非常常见的一种,我们只需要重写run方法即可。
- 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
- 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正
的线程对象。 - 调用线程对象的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);
}
}
}
Note:实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是继承Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。
1.4 Thread和Runnable的区别
实现Runnable接口比继承Thread类所具有的优势:
- 适合多个相同的程序代码的线程去共享同一个资源。
- 可以避免java中的单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
- 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
1.5 匿名内部类方式实现线程的创建
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();for (int i = 0; i < 20; i++) {
System.out.println("费玉清:"+i);
}
}
}
二、 线程安全
在开启新线程的时候,由于JAVA的机制是抢占式,就有可能导致多个线程同时执行我们可能只需要执行一次的代码这样的问题。
线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。
1.线程同步
为了解决问题我们引入了线程同步机制。
- 同步代码块。
- 同步方法。
- 锁机制。
2.同步代码块
synchronized(同步锁){
需要同步操作的代码
}
- 同步方法
同步方法:使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外
等着。
public synchronized void method(){
可能会产生线程安全问题的代码
}
- Lock锁
public class Ticket implements Runnable{
private int ticket = 100;
Lock lock = new ReentrantLock();
/*
* 执行卖票操作
*/
@Override
public void run() {
//每个窗口卖票的操作
//窗口 永远开启
while(true){
lock.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‐‐);
}
lock.unlock();
}
}
}
我们可以将unlock,放在finally代码块中,保证资源的释放。
三、线程状态
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,有几种状态呢?在API中 java.lang.Thread.State 这个枚举中给出了六种线程状态:
NEW(新建):线程被创建,并未启动,但是还没有调用start。
Runnable(可运行):线程可以在java虚拟机中运行的状态,可能正在运行自己的代码,可能没有,取决于操作系统处理器。
Blocked(尝试获取锁的时候,被其他线程持有):获取之后转为Runnable。
Waiting:无限等待,需要被其他线程唤醒,需要notify/notifyAll。
Timed Waiting:计时等待,带有超时参数,这一状态一直保持到超时期满或者被唤醒,如Thread.sleep、Object.waiting。
Terminated:因为run方法正常退出死亡,或者因为没有捕获的异常终止了run而死亡。