线程基础
并行并发
-
并发:
同一个时刻,多个任务交替执行。简单的说 ,单核CPU实现的多任务就是并发。
-
并行:
同一时刻,多个任务同时执行。多核CPU可以实现并行.
创建线程
-
继承Thread类,重写run方法
public class Thread01 { public static void main(String[] args) { Cat cat = new Cat(); cat.start(); // cat.run(); 该方法就是一个普通的方法,直接调用Cat类中的方法。并没有去启动一个线程。就会把run方法执行完毕才会向下执行。会变成串行化 } } class Cat extends Thread{ @Override public void run() { int times = 0; while (true) { // 重写run方法,写出自己的业务逻辑 //每隔一秒 在线程输出“cat cat ” System.out.println("cat cat"+(++times)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (times == 8) { break; } } } }
-
实现Runnable接口,重写run方法
使用了代理模式
public class Thread02 { public static void main(String[] args) { Dog dog = new Dog(); Thread thread = new Thread(dog);//创建了Thread对象,把dog对象(实现Runnable),放入Thread。 thread.start(); } } class Dog implements Runnable{ @Override public void run() { int conut = 0; while (true) { System.out.println("Dog Dog>>>"+(++conut)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (conut == 8) { break; } } } }
-
两者区别
从Java设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别。
实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制,建议使用Runnable。
线程终止
-
通知线程退出
public class Thread01 { public static void main(String[] args) throws InterruptedException { T1 t1 = new T1(); t1.start(); // 通知退出 Thread.sleep(10*1000); t1.setLoop(false); } } class T1 extends Thread{ private int count = 0; private boolean loop = true; @Override public void run() { while (loop) { System.out.println("T1》》》》"+(++count)); } } public void setLoop(boolean loop) { this.loop = loop; } }
线程常用方法
-
setName 设置线程名称,使之余参数name相同
-
getName 返回该线程的名称
-
start 使该线程开始执行;Java虚拟机底层调用该线程的start0方法
-
run 调用线程对象run方法
-
setPriority 更改线程的优先级
-
getPriority 获取线程的优先级
-
sleep 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
-
interrupt 中断线程
-
yield 线程的礼让。让出CPU,让其他的线程执行,但礼让时间不确定,所以不一定礼让成功
-
join 线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
-
用户线程
也叫工作线程,但线程的任务执行完或通知方式结束
-
守护线程
一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
常见的守护线程:垃圾回收机制
先设置
setDaemon(true)
再启动线程start()
线程的生命周期
- 6个或7个生命周期取决于Runnable的细分(Ready和Running)
线程的同步
-
线程同步机制
在多线程编程中,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性
也可以理解为:线程同步,即担忧一个线程在对内存进行操作时,其他线程都不可以对该内存地址进行操作,直到该线程完成操作,其他线程才能对该地址进行操作
-
同步具体方法Synchronized
同步代码块:
synchronized(对象){
// 得到对象的锁,才能操作同步代码
//需要被同步的代码;
}
synchronized还可以放在方法声明中,表示整个方法为同步方法
public synchronized void m (String name){//需要被同步的代码块}
-
synchronized互斥锁
Java中,引入了对象互斥锁的概念,来保证共享数据的完整性
每个对象都对应于一个可称为”互斥锁“的标记,这个标记用来保证在任意时刻,只能有一个线程访问该对象。
关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任意时刻只能由一个线程访问。
同步的局限性:导致程序的执行效率要降低
同步方法(非静态)的锁可以是this,也可以是其他对象(要求是同一对象)
同步方法(静态的)的锁为当前类的本身
注意事项和细节:
同步方法如果没有使用static修饰;默认锁对象为this(非公平锁)
如果方法使用static修饰,默认锁对象为当前类.class
线程的死锁
-
基本介绍
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁。
-
释放锁
当前线程的同步方法、同步代码块执行结束
当前线程在同步代码块、同步方法中遇到了break、return
当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
当前线程在同步代码块、同步方法中执行了线程对象wait()方法,当前线程暂停,并释放锁。
-
不会释放锁
线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁。
块、同步方法中遇到了break、return
当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
当前线程在同步代码块、同步方法中执行了线程对象wait()方法,当前线程暂停,并释放锁。
-
不会释放锁
线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁。