什么是线程?
1.线程由进程创建,是进程的一个实体
2.一个线程可有多个进程
单线程:
同一时刻只有一个
多线程:
同一时刻多个
并发:
一会一会,交替执行
并行:
一边一边,同时执行
创建线程的两种方式:
1.继承Thread类,重写run方法
class Cat extends Thread{
public void run(){
int times=0;
while(true) {
System.out.println("哈喽哈喽"+(++times)+"线程-
>"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(times==20){
break;
}
}
}
}
/*-----------------------------------------------------------*/
public class ThreadEgTest {
public static void main(String[] args) throws InterruptedException {
Cat cat = new Cat();
cat.start();//启动线程,最终会执行cat的run方法
//调start,底层是start0(),是本地方法,JVM调用,底层是c/c++实现,
//真正实现多线程的效果,是start0(),而不是run
//用的是start而不是直接用run方法,因为run方法就是一个普通的方法,
//没有真正的启动线程,它会把run方法执行结束后才向下执行
System.out.println("主线程继续执行"+Thread.currentThread().getName());
//主线程结束,如果还有其他子线程在跑,那么应用程序不会结束,还有子线程要继续执行完
for(int i=0;i<10;i++){
System.out.println("主线程 i="+i);
Thread.sleep(1000);
}
}
}
2.实现Runnable接口,重写run方法
class Dog implements Runnable{//通过实现Runnable接口,开启线程
int count=0;
public void run(){
while(true){
System.out.println("小狗汪汪叫"+(++count)+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(count==10){
break;
}
}
}
}
/*--------------------------------------------------------------------------------------*/
public class DogTest {
public static void main(String[] args) {
Dog dog=new Dog();
//dog.start(); 这里不能调用start;
//创建Thread对象,把dog对象(实现Runnable)接口,
Thread thread=new Thread(dog);
thread.start();
}
}
3.实现Callable接口:
实现call()方法,用FutureTask()封装实现类。使用FutureTask对象作为Thread对象调用start()启动线程,调用FutureTask对象的get()方法获取返回值()。
Thread和Runnable的区别:
1.本质上二者没有区别,因为底层都是靠start()来实现(底层调用start0()),Thread类本身就实现了Runnable接口.
2.用法上有所区别,实现Runnable接口方式更适合多个线程共享一个资源的情况,并且避免了单继承的限制
Runnable和Callable的区别:
(1)Callable规定的方法是call(),Runnable规定的方法是run()。
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得。
(3)call方法可以抛出异常,run方法不可以,因为run方法本身没有抛出异常,所以自定义的线程类在重写run的时候也无法抛出异常
(4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
线程常用方法:
第一组:
1.setName 设置线程名称,使之与参数name相同
2.getName 返回该线程的名称
3.start 使线程开始执行 ; Java虚拟机底层调用该线程的start0方法
4.run 调用线程对象run方法
5.setPriority 更改线程的优先级
6.getPriority 获取线程的优先级
7.sleep 在指定毫秒数内让当前正在执行的线程休眠
8.interrupt 中断线程(不是终止)
第二组:
1.yield:
线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
2.join:
线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任内。
守护线程:
当所有的用户线程结束,守护线程自动结束
常见的守护线程:垃圾回收机制
线程的七大状态:
Runnable 可细分为Ready和Running
可运行状态 可细分为就绪状态和运行状态
互斥锁:
1.Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
2.每个对象都对应一个可称为互斥锁的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象
3.关键字synchronized来与对象的互斥锁联系。当某个对象用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问
4.同步的局限性:导致程序的执行效率要降低
5.同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
6.同步方法(静态的)的锁为当前类本身
注意事项和细节:
1.同步方法如果没有使用static修饰,默认锁对象为this
2.如果方法使用static修饰,默认锁对象:当前类.class
3.实现步骤:先分析上锁的代码,选择同步代码块或方法(同步代码块更好,范围更小,效率更高),要求多个线程的锁对象为同一个(才能锁住,如果每个对象各有一把锁,就等同于各自执行导致锁无效,相当于一个厕所有三个门但只有一个坑位,三个人进去在同一个坑位上厕所)。
public class SellSynchronized {
public static void main(String[] args) {
SellTicket windows=new SellTicket();
new Thread(windows).start();
new Thread(windows).start();
new Thread(windows).start();
}
}
class SellTicket implements Runnable{
private static int ticketsNum=1000;
private boolean loop=true;
Object object=new Object();
//同步方法(静态的)的锁为当前类本身
public synchronized static void m1(){
}
//如果在静态方法中,实现一个同步代码块,在类本身,用类.class
public static void m2(){
synchronized (SellTicket.class) {
System.out.println("m2");
}
}
public /*synchronized*/ void sell() { //这个锁在this对象,同步方法锁,是在方法上加锁
//也可以在代码块上加,就是同步代码块
synchronized (/*this*/object/*new Object()*/) {
if (ticketsNum <= 0) {
System.out.println("没票啦!");
loop = false;
return;
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("窗口" + Thread.currentThread().getName() + "售出一张票"
+ "剩余票数为" + (--ticketsNum));
}
}
public void run(){
while(loop){
sell();
}
}
}
死锁:
案例:
妈妈:你先完成作业才让你玩手机
小明:你先让我玩一会我就完成作业
public class DieLock {
public static void main(String[] args) {
DieLockDemo A=new DieLockDemo(true);
DieLockDemo B=new DieLockDemo(false);
A.setName("A线程");
B.setName("B线程");
A.start();
B.start();
}
}
class DieLockDemo extends Thread{
static Object o1=new Object();
static Object o2=new Object();
boolean flag;
public DieLockDemo(boolean flag){
this.flag=flag;
}
public void run(){
//业务逻辑分析
//1.如果flag为true,线程A就会先得到o1对象锁,然后尝试去获取o2锁
//2.如果线程A得不到o2锁,就会Blocked
//3.如果flag为false,线程B就会先得到o2对象锁,然后尝试去获取o1锁
//4.如果线程B得不到o1锁,就会Blocked
if(flag){
synchronized (o1){
System.out.println(Thread.currentThread().getName()+"进入1");
synchronized (o2){
System.out.println(Thread.currentThread().getName()+"进入2");
}
}
}else{
synchronized (o2){
System.out.println(Thread.currentThread().getName()+"进入3");
synchronized (o1){
System.out.println(Thread.currentThread().getName()+"进入4");
}
}
}
}
}