线程
进程
实际就是程序的执行过程
特点
1/独立性,每个进程都有自己的空间,在没有经过进程本身允许的情况下,一个进程不可以直接访问其他二点进程空间
2/动态性:是动态产生,动态消亡的
3/并发性:任何进程都可以和其他的进程一起并发执行
并发与并行
并行:多个指令在多个cpu上同时执行
并发:多个指令在单个cpu上交替执行
多进程同时执行
对于一个cpu而言,他是在多个进程间轮换执行的
也可以说是例如微信qq一块运行,实际上是给微信用一会,qq用一会,只不过运行的十分快,
当然现在是多核的
线程
进程可以同时执行多个任务,每个任务就是线程
进程当中的任务
多线程意义
因为现在处理器的核心越来越多,现在大多数计算机都比以往更擅长并行运算
例如二核四线程
就会存在并行操作
1.提高执行的效率
2.同时处理多个任务***
java开启线程的方式
继承Thread类
实现Runnable接口
实现Callable接口
Thread
案例
package com.ithema.o2;
public class DOME {
/*
* 1.继承一个类继承Thread
* 2.重写run方法
* 3.将线程任务代码写在run方法里
* 4.创建线程对象
* 5.调用start方法开启线程
* */
public static void main(String[] args) {
mytread mytread = new mytread();
mytread m1 = new mytread();
mytread m2 = new mytread();
m1.start();
m2.start();
}
}
class mytread extends Thread {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println(i);
}
}
}
注意,只有调用了start方法,才是开启了线程的;
会发现打印的语句有错乱的现象!!
注意
java程序默认是多线程的,程序开启后会默认存在两个线程
1.主线程
2.垃圾回收线程
//当一些程序被默认为垃圾的时候,并且垃圾也比较多的时候,就会被清理
Runnable接口
扩展性更强,因为是接口
案例
package com.ithema.o2;
public class a1 {
/* 1.编写一个类实现Runnable接口
2.重写run方法
3.将线程任务代码写在run方法里面
4.创建线程资源对象
5.创建线程资源,将资源写入
6.使用线程对象调用start方法,开启线程
* */
public static void main(String[] args) {
myrunnable m1=new myrunnable();
m1.run();
//创建线程对象,将资源放入
Thread t1=new Thread(m1);
//使用线程对象调用start方法,开启线程
t1.start();
for (int i=1;i<=100;i++){
System.out.println(i);
}
}
}
class myrunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 330; i++) {
System.out.println(i);
}
}
}
Callable接口
会有返回值
案例
package com.ithema.o2;
import java.util.Scanner;
import java.util.TreeMap;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class a2 {
/*
1.编写一个类实现Callable接口
2.重写call方法
3.将线程任务写在call方法里
* */
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程的任务资源对象
clallable m1=new clallable();
//创建线程任务对象
FutureTask<Integer>task=new FutureTask<>(m1);
//创建线程对象,传入任务资源
Thread thread=new Thread(task);
thread.start();
//接受返回值
//一定要在返回值之前打开这个get,否者线程都没开,又怎么会有结果呢
Integer m3= task.get();
}
}
class clallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum=0;
for(int i=1;i<=1000;i++){
System.out.println(i);
sum=sum+i;
}
return sum;
}
}
注意,因为有返回值,我们需要加上泛型,也就是需要什么就加上需要的那个返回值对象
需要一个中介值FutureTask
线程的相关方法
Thread类中的名字
getName
获取名字
setName
设置线程的名字
当然不仅仅又这样的一种方法,还有构造方法*
currentThread
获取当前的对象
注意Runnale实现的是接口的,所以不可以调用名字
sleep
public static void sleep(long time);
让当前线程休眠一会
线程的调度方式
抢占式调度(随机)
也就是java目前的
我们可以设置setPriority(int x);设置线程的优先级,1-10,默认为5;
getPriority();得到优先级
非抢占式调度
设置优先级并不是说先让某个线程先执行完,而是提高他的被给予资源的概率
setDaemon
设置为守护线程
当其他线程结束的时候就会不动了
但不是瞬间就结束
线程的安全和同步
案例
某电影场正在上映动画片,共有100张票,而他共有三个接口,请设置一个程序模拟该电影票卖票
-多线程共享操作同一份数据的时候,就会出现不安全的问题
package com.ithema.o2;
public class a3 {
public static void main(String[] args) {
Thread a1=new Thread(new thread(),"号码1:");
Thread a2=new Thread(new thread(),"号码2:");
Thread a3=new Thread(new thread(),"号码3:");
a1.start();
a2.start();
a3.start();
}
}
class thread implements Runnable {
private int sum=100;
@Override
public void run() {
while(true){
if(sum<0){
break;
}
System.out.println(Thread.currentThread().getName()+sum+"号票");
sum--;
}
}
}
问题
运行上述代码的时候,我们会发现程序会将一些相同的票卖出
这个问题实际上就是,线程的并发性,在线程1没有将该线程的该语句执行完的时候,就让处理器去执行另外一个线程了,就造成了这种问题
处理方式就是加上锁
解决
同步代码块
synchronized(锁对象){
多条语句操作的共享数据的代码
}
注意一点,需要的是同一把锁,需要加上static,或者是,只写一个对象
最好用类的字节码问价
锁对象可以是任意对象,但是需要保证多条线程的锁对象,是同一把锁
同步可以解决多线的数据安全问题,但是也会降低程序的运行效率
同步方法
public synchronized void fangfa(){
}
//这样线程里面的东西就都是同步的了
面试
实际上这样写出来的锁对象也会有锁对象
静态方法:类的字节码对象 lei.class返回的是当前的字节码文件
非静态方法:this
Lock锁
使用Lock锁,
为接口,无法直接进行使用,创建他的子类ReenreantLock即可
例如
创建的为lock
lock.lock();
lock.unlock();
将需要锁上的元素放到这里面即可
记得把锁进行关闭,否则程序结束不了
死锁
介于两个或者多个线程互相持有对方所需要的资源
导致这些线程处于等待状态,无法继续前往执行
产生死锁的情况:同步嵌套
线程通信
1.确保线程能够按照预定的顺序执行,并且能够安全地访问共享资源
2.使得多条线程更好的进行协同工作
等待唤醒机制
案例
void wait();//当前对象等待
void notify();//随机唤醒某个对象
为锁对象的类型,就跟上面一样,锁对象一般只有两个字节码class和this
注意:notify是随机唤醒,记住,是随机唤醒
所以还有notifyAll();
唤醒所有的线程
该案例效率过低,不需要进行记忆
sleep方法和wait方法的区别
sleep是线程休眠,时间到了会自动醒来,并且不会释放锁
wait方法是线程等待,需要有其他线程进行notify唤醒,并且会释放锁
案例的效率提高
生产者消费者模式
主要包含了两类线程
生产者线程
消费者线程
介绍
建立一个中间的类,
就相当于判断中间的类的状态,为true时开启生产的,关闭消费的,false则相反
为了处理他们之间的关系,通常会采取共享的数据区域(缓存区),就像是一个仓库,生产生产完餐品之后放到生产区,不需要关心消费者,对于生产者也是一样的
线程的生命周期
面试
调用start方法启动线程对象
就绪:有执行资格,没有执行权
运行:抢到cpu执行权,有执行资格,有执行权
run:死亡,线程死亡,变成垃圾
在运行的时候,可能会遇到:计时等待(sleep),无限等待(wait),阻塞(没有执行资格,没有执行权);
线程池
介绍
池里均为线程的对象
线程创建一个线程的成本比较高,因为他涉及到与系统的交互,因为相当于来了个任务,我现创建对象
线程池就相当于养着一些对象
线程资源必须通过线程池维护,不允许在应用中自行显示
jdk默认的线程池
package com.ithema.o2;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class a4 {
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
for(int i = 0; i < 10; i++){
pool.submit(new Runnable(){
@Override
public void run() {
System.out.println("提交的任务");
}
});
}
pool.shutdown();
}
}
创建一个线程池子对象,往里面放入线程池,但是默认的存入21亿个线程对象
这样可以指定养多少个线程,但是他底层的代码已经把线程的一些数据写死了,并不适合我们去写
ExecutorService pool = Executors.newCachedThreadPool(int x);
自定义线程池
package com.ithema.o2;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class a5{
public static void main(String args[]){
/* 参数1:核心线程的数量(正式员工);
参数2:指定最大线程的数量(临时+正式);
参数3:空闲时间,时间到了删除临时,也就是被销毁
参数4:时间单位
参数5:任务队列(指定排队人数)
//有界队列
//无界队列(最大为int的最大值)
参数6:线程对象的任务工厂:
参数7:拒绝策略
* */
ThreadPoolExecutor pool =new ThreadPoolExecutor(
2,
5,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10),//任务拍的队列
Executors.defaultThreadFactory(),//默认的工场一般都是这个
new ThreadPoolExecutor.AbortPolicy()//丢弃最新提交的任务,并且报高异常
);
for(int i=0;i<14;i++){
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"提交了了任务");
}
});
}
}
}
//临时线程什么适合会进行创建
//什么时候会,进行销毁
一些拒绝策略
一般只会使用第一个
单例设计模式
单例指的是单个实例,保证类的对象在内存中只有一份
使用场景
创建一个对象需要消耗的资源过多,比如io与数据库的链接
并且这个对象是可以进行复用的,我们可以设置为单例设计对象
使用
饿汉式
package com.ithema.o2;
public class a6 {
public static void main(String[] args) {
Single1 s1=Single1.getInstance();
}
}
//单例设计对象:饿汉版
class Single1{
private Single1(){
}
private static Single1 s = new Single1();
public static Single1 getInstance(){
return s;
}
}
懒汉式(延长加载模式)
package com.ithema.o2;
public class a7 {
public static void main(String[] args) {
Single2 s1=Single2.getInstance();
Single2 s2=Single2.getInstance();
System.out.println(s1==s2);
}
}
class Single2{
private Single2(){};
private static Single2 s;
public static Single2 getInstance(){
if(s==null){
s=new Single2();
}
return s;
}
}
相对于上一个,相当于什么时候需要再创建
但是这种写法
在线程并发的时候,可能会创建多个线程时,就是在创建的时候,还没有进行创建执行权就被抢走了
所以我们对需要执行的代码进行上锁就可以了
package com.ithema.o2;
public class a7 {
public static void main(String[] args) {
Single2 s1=Single2.getInstance();
Single2 s2=Single2.getInstance();
System.out.println(s1==s2);
}
}
class Single2{
private Single2(){};
private static Single2 s;
public static Single2 getInstance(){
//这边加上null的原因是提高线程的效率,不会被一直阻塞
if(s==null){
synchronized (Single2.class){
if(s==null){
s = new Single2();
}
}
}
return s;
}
}