第一种开启线程的方式
1.定义一个类继承Thread类
2.重写run方法(线程任务:线程将执行的内容)
3.开启线程
创建子类对象
调用start方法
//定义个类,继承线程Thread
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("sub..."+i);
}
}
}
public class Test {
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
for (int i = 0; i < 50; i++) {
System.out.println("main..."+i);
}
}
}
为什么要开启线程继承Thread类?
因为Thread类就是线程类,具备线程应该有的所有功能,继承了Thread类,就是线程类
就可以使用Thread类里面所有的功能.例如 start方法
第二种创建方法
1.定义实现类实现Runnable接口
2.重写run方法 (线程任务)
3.开启线程
创建Runnable接口的实现类对象
创建Thread类对象,将实现类对象传入
调用Thread类的start方法,开启线程
Thread类构造方法
public Thread(Runnable r)
方法的参数是接口,可以传入任意实现类对象
跟第一种就是,一个继承,一个实现,但重写的方法都是一样的,就没有代码示例了.
思考:为什么有了第一种方式,为什么还会有第二种方式?
1.避免了java单继承的局限性,一个类如果有了父类,是没有办法通过继承Thread类的方式开启线程的,但java是多实现的,所以此时可以通过实现接口的方式来开启线程
2.将线程任务与对象分离 降低了耦合性
第一种方式 将线程任务和线程对象耦合到了一起,创建对象,既是线程对象也包括了线程任务
第二种方式 线程对象和线程任务是分开创建的,降低了耦合性
3.更符合面向对象思想 更容易实现 多条线程共用一个任务
调用run方法和start方法的区别
如果调用run方法,那么run方法会到主线程的栈区去执行 依旧是个单线程程序
如果调用start方法 那么jvm会开启一条新的线程 并调用run方法 在新线程中执行 多线程程序
API
static void sleep(long time) 当前线程睡眠time毫秒后 醒来继续执行
setName/getName 设置与获取线程名字
当当前类不是线程的子类时,可以调用Thread的静态方法currentThread(),用来获取当前线程对象,然后就可getName();
线程的优先级,设置了也不一定先执行完毕.
public static final int MIN_PRIORITY //1 最低优先级
public static final int NORM_PRIORITY //5 默认优先级
public static final int MAX_PRIORITY //10 最大优先级
设置守护线程 用户线程执行完毕,守护线程无论执行怎样,jvm直接退出
public final void setDaemon(Boolean on) on的值为true时将当前线程设置为守护线程,注意一定要在线程开启前设置
线程等待 join
void join() 让当前线程等待 让调用方法的线程插队 调用方法的线程执行完毕后,当前线程才继续执行,对其他的线程没有影响
线程停止
void stop() 直接杀死对应的线程 直接结束,有点不友好
void interrupt() 中断线程
boolean isInterrupt() 返回true则线程中断,false没有
这种比较友好,中断线程后,可以利用返回的布尔值对代码进行进一步操作
线程安全问题
当多条线程共享一个资源时,有可能出现数据错误,导致线程不安全
解决方法:
线程同步 关键词synchronized 对象监视器
lock锁
线程同步
同步代码块
synchronized(任意对象){
有可能出现问题的代码
}
同步方法
在方法的返回之前加上 synchronized
?
同步方法有锁吗? 有的,是当前对象 this
同步方法可以是静态的吗? 可以,锁就不是this了,因为静态中不能使用this/super
静态的同步方法 锁对象时谁? 当前类名.class
Lock锁
方法
void lock() 获取锁
void unLock() 释放锁
常用实现类
java.util.concurrent.locks.ReentrantLock
示例:
public class SellTickets implements Runnable {
private int i = 100;
//创建lock锁实现类对象
private Lock l = new ReentrantLock();
@Override
public void run() {
while(true){
l.lock();
if(i>0){
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"卖票了..."+i);
i--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
l.unlock();
}
}else{
l.unlock();
}
}
}
}
Object类的线程方法
void wait() 当前线程等待 此方法必须由对象监视器调用 哪个对象监视器调用了这个方法 线程就在哪个对象监视器上等待
void notify() 唤醒正在当前对象监视器上等待的线程 中的一个线程
wait和notify方法必须在同步中使用 必须都由锁对象(对象监视器)来调用
wait和sleep的区别
sleep方法是Thread类的方法 睡眠n毫秒后 自动醒来继续执行代码,既可以在同步外使用,也可以在同步中使用,在同步中使用时,锁不会被释放掉
wait方法是Object类的方法 线程遇到wait方法进入到等待状态,必须由其他线程唤醒才能执行,其必须在同步中使用,且等待时,还会将锁释放掉