线程
线程有三类
主线程:系统线程,如Java中的Java虚拟机(主线程是最先执行的)
用户线程:Java中main函数执行的那些玩意
守护线程(精灵):比如Java中的GC(垃圾回收器,他是为主线程服务的,当系统启动后,GC就随之产生了;Java虚拟机断掉了,GC也就不干活了)
实现线程的过程(有两个方法):
实现线程的两个前提条件(继承Thread是最方便的,但是Java是单继承的,很可能会和其他的继承冲突;所以还可以通过实现Runnable接口,不过它要额外的通过写一个Thread类来执行start()方法)
方法一:继承一个父类Thread
方法二:实现一个接口Runnable
我们都必须重写run()方法,
因为这个方法来源于cpu,是操作系统给JDK提供的接口,JDK将它包装成run(),其实我们执行的线程就是写在run()方法中的代码执行起来了
new一个线程对象(我们是无法调用run()的,它只有cpu才可以调用,cpu分配时间碎片才能开始执行)
我们只能调用start()方法(这个方法继承自Thread),它会使这个线程除了cpu资源的其它资源都得到满足,让它进入就绪状态,在就绪队列中等待着cpu执行它
public class Thread01 {
public static void main(String[] args) throws InterruptedException{
Cat cat=new Cat(); //创建Cat对象,可以当作线程使用
cat.start(); //启动子线程
//说明:当main线程启动一个子线程 Thread-0,主线程不会阻塞,会继续执行,这时 主线程和子线程会交替执行
System.out.println("主线程继续执行"+Thread.currentThread().getName());
for (int i = 0; i <10 ; i++) {
System.out.println("主线程 i="+i);
Thread.sleep(1000);
}
}
}
/*
1 当一个类继承了Thread类,该类就可以当作线程类使用
2 我们会重写run方法,写上自己的业务代码
3 Thread类 内部也是实现了 Runnable 接口的run方法
*/
class Cat extends Thread{
int times=0;
@Override
public void run() {
while (true){
//该线程每隔1秒种,在控制台输出 “喵喵,我是小喵喵”
System.out.println("喵喵,我是小喵喵 "+(++times)+" 线程名="+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(times==80){
break; //当times到80,退出while,这时线程也就退出。。。
}
}
}
}
当程序执行时,之后开启main方法这个主线程,则进行一个进程的执行,若有子线程,子线程A.start()开启一个子线程,当主线程和子线程全部执行完毕,进程结束。注意:只有所有的线程结束,进程才会结束
class Dog implements Runnable { //通过实现Runnable接口,实现线程
int count = 0;
@Override
public void run() { //普通方法
while (true) {
System.out.println("小狗汪汪叫..hi" + (++count) + Thread.currentThread().getName());
try {
Thread.sleep(1000); //休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 10) {
break;
}
}
}
}
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
Thread thread = new Thread(dog); //创建了Thread对象,把 dog对象(实现Runnable),放入Thread
thread.start(); //启动一个子线程
}
}
继承Thread VS 实现Runnable的区别
1 从Java设计来看,通过继承Thread或者实现Runnable接口创建线程本质上没有区别,从jdk帮助文档上看Threrad类本身就是实现了Runnable接口。
2 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制,建议使用Runnable。
线程常用的方法
1 setName //设置线程名称,使之与参数name相同
2 getName //返回线程的名称
3 start //是该线程开始执行;java虚拟机底层调用该线程的start0()方法
4 run //调用该线程的run方法
5 setPriority //更改线程的优先级
6 getPriority //获取线程的优先级
7 sleep //在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
8 interrupt //中断线程,不是停止,一般用于正在休眠的线程
9 线程优先级:MAX_PRIORITY MIN_PRIORITY NORM_PRIORITY
线程礼让
yield线程礼让,让出cpu,让其他线程执行,但是礼让的时间不确定,所以也不一定礼让成功
线程插队
class T2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(1000); //休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程(大哥)吃了 "+i+" 个包子");
}
}
}
public static void main(String[] args) throws InterruptedException {
T2 t2=new T2();
t2.start();
for(int i=0;i<20;i++){
Thread.sleep(1000);
System.out.println("主线程(小弟)吃了 "+i+" 个包子");
if (i==5){
System.out.println("主线程(小弟)让(大哥)先吃");
// join 线程插队 一定会成功
// t2.join();
// 礼让 不一定会成功
Thread.yield();
System.out.println("子线程(老大)吃完了,我们接着吃");
}
}
}
线程同步
为什么要线程同步
Lock锁
public class Lock {
public static void main(String[] args) {
BuyTickett buyTicket = new BuyTickett();
new Thread(buyTicket,"a").start();
new Thread(buyTicket,"b").start();
new Thread(buyTicket,"c").start();
}
}
class BuyTickett implements Runnable{
private int ticketNum = 10;
boolean flag = true;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (flag){
try {
lock.lock();
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
lock.unlock();
}
}
}
private void buy() throws InterruptedException {
if (ticketNum <= 0){
flag = false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"获得了第"+ticketNum--+"张票");
}
}
notify()
一个线程调用共享对象的notify)方法后,会唤醒一个在该共享变量上调用wait系列方法后被挂起的线程。一个共享变量上可能会有多个线程在等待,具体唤醒哪个等待的线程是随机的。
此外,被唤醒的线程不能马上从 wait方法返回并继续执行,它必须在获取了共享对象的监视器锁后才可以返回,也就是唤醒它的线程释放了共享变量上的监视器锁后,被唤醒的线程也不一定会获取到共享对象的监视器锁,这是因为该线程还需要和其他线程一起竞争该锁,只有该线程竞争到了共享变量的监视器锁后才可以继续执行。
类似wait系列方法,只有当前线程获取到了共享变量的监视器锁后,才可以调用共享变量的notify()方法,否则会抛出IllegalMonitorStateException异常。
ackage thread.notify;
public class NotifyTest {
private static volatile Object resourceA = new Object () ;
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread (new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
//获取resourceA共享资源的监视器锁
synchronized (resourceA) {
System.out.println("threadA get resourceA lock");
try {
System.out.println("threadA begin wait" ) ;
resourceA.wait() ;
System.out.println("threadA end wait" ) ;
}catch (InterruptedException e) {
e.printStackTrace() ;
}
}
}
});
Thread threadB = new Thread (new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
//获取resourceA共享资源的监视器锁
synchronized (resourceA) {
System.out.println("threadB get resourceA lock");
try {
System.out.println("threadB begin wait" ) ;
resourceA.wait() ;
System.out.println("threadB end wait" ) ;
}catch (InterruptedException e) {
e.printStackTrace() ;
}
}
}
});
Thread threadC = new Thread (new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
//获取resourceA共享资源的监视器锁
synchronized (resourceA) {
System.out.println("threadC begin notify");
resourceA.notify();
//resourceA.notifyAll();
}
}
});
threadA.start() ;
threadB.start() ;
Thread.sleep(1000);
threadC.start () ;
}
}
notifyAll()
不同于在共享变量上调用notify()函数会唤醒被阻塞到该共享变量上的一个线程,notifyAll()方法则会唤醒所有在该共享变量上由于调用wait系列方法而被挂起的线程。