实现方法
方法1 继承thread类
1)创建一个类来继承thread类
2)重写run方法
run 方法中编写的是线程要执行的代码
然后就可以实例化这个类 调用start方法来开启线程
方法2 实现Runnable接口
1)创建一个类来实现Runnable接口
2)重写run方法
run 方法中编写的是线程要执行的代码
3)创建这个类的实现类
4)再去调用java的Thread方法
//例如创建的类是myrun 继承了runnable接口
myrun mr = new myrun();
Thread t = new Thread(mr);
然后就可以调用start方法来开启线程
方法3 实现callable接口和Future接口
特点:可以获取线程运行的结果
1)创建一个类mycalll来实现callable接口
2)并且重写call方法 //这个方法是有返回值的 返回的就是线程运行的结果
3)创建这个mycall的对象(来表示多线程要执行的对象)
4)创建这个Future的实现类FutureTask的对象 来管理多线程的运行结果
将mycall的对象作为参数
5)创建线程的对象
将FutureTask的对象作为参数
public class demo1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
mycall mc = new mycall();
FutureTask ft = new FutureTask<>(mc);
Thread t = new Thread(ft);
t.start();
System.out.println(ft.get());
}
}
三种实现方法的特点比较
常见成员方法
给线程设置名字
使用Thread类的 setName方法 或者是 使用Thread有参构造方法来设置
线程的优先级
在java中实现的是抢占式调度 抢占到线程的概率和优先级有关
最小是1 最大是10 默认是5
优先级越高 抢占到cpu的概率越高
main线程
当java虚拟机启动的时候 会自动开启多条的线程
其中一条线程就是main线程,这个线程的作用就是来运行main方法的代码
守护线程(备胎线程)
怎么开启守护线程:线程.setdeamon(true)
当其他的非守护线程结束时,守护线程也会陆续结束
这里的陆续结束就是:
守护线程不必要强制全部执行后结束
应用场景:
当(非守护线程)整个聊天结束,(守护线程)传输文件的这个过程也会陆陆续续的结束
出让线程/礼让线程
怎么开启礼让线程:Thread.yield();
当一个线程执行后会出让当前cpu的执行权 让所有线程重新抢夺执行权。可以保证线程的执行尽可能的平均(只是尽可能!!仍然会出现一个线程连续执行的情况)
插入线程
怎么开启礼让线程:
//例如存在一个aaa线程
aaa.join();
//就会把aaa线程插入到当前线程的前面
//只有当aaa线程执行完之后才会执行当前的线程
这里说的当前线程 就是指 这个aaa.join();代码是写在哪个线程中的
比如写在main方法中 将aaa线程插入到main线程之前
线程的生命周期
线程安全
在电影院买票问题中 同时创建三个线程
1)首先会出现 三个线程卖的票是相互独立的
这个可以将票设置成static关键字,这样子三个线程的票是同一个变量。
2)但是 在这个过程中会出现 三个线程卖出的票是一样 或者多卖的这种情况
原因是sleep这个方法会将cpu的执行权放出 然后线程2,线程3就又可以进行线程的抢夺
在这个过程种,三个线程开始执行。可能出现当线程1执行完ticket++ ,没来得及执行sout,线程2却又执行了ticket++ 。导致这个静态变量变为2,然后导致三个线程输出的ticket值均为2,出现错误
综合原因是因为线程执行是具有随机性的,cpu可能会被线程抢夺
同步代码块
将操作共享数据的代码锁起来
格式:
synchronized(锁){操作共享数据的代码}
特点 :
1)默认的锁是开启的,当一个线程进去后,锁自动关闭
2)里面的代码全部执行完毕之后,线程出来,锁自动打开
同步方法
格式:修饰符 synchronized 返回值 方法名(方法参数){}
特点 1
同步方法锁的是方法内的全部方法
特点 2
锁对象不能直接确定 非静态就是this 静态是当前类的字节码文件(.class)
Lock锁
创建一个Lock的对象调用其 lock()和unlock()方法来进行自定义锁的打开和关闭位置
(Lock是一个抽象类得创建其实现类ReentrantLock()的对象)
在使用的过程如果break跳出循环后,没有执行到unlock()方法。那么程序就不会停止
解决方法:将lock()方法后的内容用try catch 包裹 在finally中写入unlock方法
死锁
产生的四个必要条件
互斥条件:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。
不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
循环等待条件:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。
线程池
创建了线程池 在提交任务后 如果线程池总有线程就直接调用 如果没有就创建线程(直到上线)
线程池 可以创建有上限和无上限的两种
//无上限
ExecutorService pool = Executors.newCachedThreadPool();
//上限为3
ExecutorService pool = Executors.newFixedThreadPool(3);
用sumbit来提交任务