进程
进程是一个应用程序(1个进程是一个软件)。线程是一个进程中的执行场景/执行单元。
线程
一个进程可以启动多个线程。
4.2、对于java程序来说,当在DoC命令窗口中输入: java Helloworld回车之后。 会先启动JVM,而JVM就是一个进程。JVM再启动一个主线程调用 main方法。 同时再启动一个垃圾回收线程负责看护,回收垃圾。最起码,现在 的java程序中至少有两个线程并发, 一个是垃圾回收线程,一个是执行main方法的主线程。
注意: 进程A和进程B的内存独立不共享。(阿里巴巴和京东资源不会共享的!) 魔兽游戏是一个进程 酷狗音乐是一个进程 这两个进程是独立的,不共享资源。 线程A和线程B呢? 在java语言中: 线程A和线程B,堆内存和方法区内存共享。但是栈内存独 定,**一个线程一个栈**。
第一种方法
run()方法内存图
start方法内存图
//此程序有先有后,且控制台只有一个,
第二种方式
新建一个class ,实现java.lang.Runnable
implements Runnable
第3种方式
匿名内部类,直接new接口Runnable
线程生命周期
获取线程的名字
Mythread t=new Mythread(); t.setName("线程1");//如果不改名字t.getName();得到的是THread-0 t.start();
1.
哪个线程执行的就是当前线程
线程的sleep() 方法
终止线程的睡眠interrupt(),不是中断线程
关于重写的方法,子类重写的方法不能比父类的方法抛出更多的异常
先是t.interrupt();之后然后,Thread.sleep()出异常 然后捕捉异常!然后程序再继续往下走!
强行终止线程的睡眠
t.stop();
这种方式存在很大的缺点:容易丢失数据。因为这种方式是直接杀死线程
这种比较合理,通过打一个布尔标记
线程优先级
Thread.MAX_PRIORITY 最高优先级 10 Thread.MIN_PRIORITY 最低优先级 1 Thread.NORM_PRIORITY 默认优先级5 Thread.currentThread().getName() 当前线程的名字线程 Thread.currentThread().getPriorty() 获取当前线程的优先级 Thread t=new Thread(new MyRunnable5()); 默认优先级都是5 优先级比较高的只是抢到的cpu时间片相对多一点。 Thread.currentThread().setPriorty(1);设置当前的主线程的优先级为1
线程让位yield()
Thread.yield(); public void run(){ for(int i=0;i<10000;i++){ if(i%100==0){ Thread.yield(); //当前线程暂停一下,让给主线程 } System.out.println(Thread.currentThread().getName()+i) } }
线程合并
线程安全(重点)!!
以后在开发中,我们的项目都是运行在服务器当中,而服务器已经将线程的定义,线程对象的创建,线程的启动等,都已经实现完了。这些代码我们都不需要编写。
程序要放在多线程的并发环境下运行,更需要关注的这些数据在多线程并发的环境下是否安全。
什么时候数据在多线程并发的环境下会存在安全问题呢? 三个条件: 条件1:多线程并发。条件2:有共享数据。 条件3:共享数据有修改的行为。 满足以上3个条件之后,就会存在线程安全问题。
怎么解决线程安全问题呢? 当多线程并发的环境下,有共享数据,并且这个数据还会被修改,此时就存在线程安全问题,怎么解决这个问题? 线程排队执行。(不能并发)。用排队执行解决线程安全问题。这种机制被称为:线同步机制。 怎么解决线程安全问题呀? 使用"线程同步机制"。
专业术语叫做:线程同步,实际上就是线程不能并发了,线程必须排队执行怎么解决线程安全问题呀? 使用"线程同步机制”。 线程同步就是线程排队了,线程排队了就会牺牲一部分效率,没办法,数据安全第一,只有数据安全了,我们才可以谈效率。
异步编程模型: 线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,谁也不需要等谁,,这种编程模型叫做:异步编程模型。其实就是:多线程并发效率较高。 异步就是并发。 同步编程模型: 线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,两个线程之间发生了等待关系,这就是同步编程模型。 效率较低。线程排队执行。 同步就是排队。
所以得线程同步,共享同一个资源的时候。
使用线程同步问题,解决线程安全问题
线程同步代码块,后面这个小括号中传的这个数据是相当管家你的。这个数据必须是多线程共享的数据,才能大道多线程排队。
()中的对象,是不是t12345共享的对象,是不是都有.有的话就同步
synchronized(){
}
哪些线程需要排队那么就填那些 多线程共享的资源对象。
在方法体内!
”abc“实际上没有意义,别的线程操作别的账户,完全没有意义,浪费了效率
this的这个对象没上锁,锁的是这个方法!!
synchronized 放在实例方法上锁的一定是this
出现在实例方法上表示整个方法体都需要同步,可能会无故扩大同步范围,导致程序的执行效率降低。所以不常用
如果同步的是整个方法体的时候,建议使用这个
类锁一个类只有一把,用同一个类的时候要等!
静态方法是类锁,类锁不管创建了几个对象,类锁只有一把.
class Myclass
public synchronized static void doSome()
public synchronized static void doOther()
总结
死锁
synchronized在开发中最好不要嵌套使用
聊一聊,我们以后开发中应该怎么解决线程安全问题? 是一上来就选择线程同步吗?synchronized 不是,synchronized会让程序的执行效率降低,用户体验不好。系统的用户吞吐量降低。用户体验差。在不得已的情况下再选择线程同步机制。 第一种方案:尽量使用局部变量代替"实例变量和静态变量"。 第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样实例变量的内存就不共享了。(一个线程对应1个对象,100个线程对应100个对象,对象不共享,就没有数据安全问题了。 第三种方案:如果不能使用局部变量,对象也不能创建多个,这个时候就只能选择synchronized了。线程同步机制。 比如秒杀功能!限定数量
守护线程
java语言中线程分为两大类: 一类是:用户线程 一类是:守护线程(后台线程) 其中具有代表性的就是:垃圾回收线程(守护线程)。
守护线程的特点: 一般守护线程是一个死循环,所有的用户线程只要结束, 守护线程自动结束。
注意:主线程main方法是一个用户线程。
守护线程用在什么地方呢? 每天00:00的时候系统数据自动备份。 这个需要使用到定时器,并且我们可以将定时器设置为守护线程。 一直在那里看着,没到00:00的时候就备份一次。所有的用户线程 如果结束了,守护线程自动退出,没有必要进行数据备份了。
代码模拟
守护线程需要在main中setDaemon,只要main(用户线程)一结束,守护线程就结束了!! class BakeDataThread extends Thread{ public void run(){ //即使是死循环,当由于该线程是守护者,当用户线程结束,守护线程自动终止. int i=0; while(true){ System.out.println(Thread.currentThread().getName()+(++i)); Thread.sleep(1100); } } }
定时器
定时器的作用:(鸡妈后台 间隔特定的时间,执行特定的程序。
每周要进行银行账户的总账操作。 每天要进行数据的备份操作。
在实际的开发中,每隔多久执行一段特定的程序,这种需求是很常见的, 那么在java中其实可以采用多种方式实现: 可以使用sleep方法,睡眠,设置睡眠时间,没到这个时间点醒来,执行 任务。这种方式是最原始的定时器。(比较low)
在java的类库中已经写好了一个定时器:java.util.Timer,可以直接拿来 用。 不过,这种方式在目前的开发中也很少用,因为现在有很多高级框架都是支持 定时任务的。
在实际的开发中,目前使用较多的是Spring框架中提供的SpringTask框架(底层是定时器),sleep方法太原始了不能当作定时器 这个框架只要进行简单的配置,就可以完成定时器的任务。
定时任务TimerTask implements Tunnable 是个线程,也是个抽象类,没办法new 只能被子类继承
抽象都可以使用匿名内部类,抽象类 或者接口
Timer timer=new Timer();
实现线程的第三种方式FutureTask,实现Callable
这种方式实现的线程可以获取线程的返回值。 之前讲解的那两种方式是无法获取线程返回值的,因为run方法返回void。 思考: 系统委派一个线程去执行一个任务,该线程执行完任务之后,可能 会有一个执行结果,我们怎么能拿到这个执行结果呢? 使用第三种方式:实现Callable接口方式。
效率比较低,但是能拿到线程的执行结果
public class FutureTask<V> implements RunnableFuture<V> public interface RunnableFuture<V> extends Runnable, Future<V> 多态 Runnable ..= FutureTask ..
Object类中的wait和notify方法(生产者和消费者模式)
第一:wait和notify方法不是线程对象的方法,是java中任何一个java对象 都有的方法,因为这两个方式是Object类中自带的。 wait方法和notify方法不是通过线程对象调用, 不是这样的:t.wait(),也不是这样的:t.notify()..不对。 第二:wait()方法作用? Object o = new Object(); o.wait(); 表示: 让正在o对象上活动的线程进入等待状态,无期限等待, 直到被唤醒为止。 o.wait();方法的调用,会让“当前线程(正在o对象上 活动的线程)”进入等待状态。 第三:notify()方法作用? Object o = new Object(); o.notify(); 表示: 唤醒正在o对象上等待的线程。 还有一个notifyAll()方法: 这个方法是唤醒o对象上处于等待的所有线程。
public class ThreadTest1 { public static void main(String[] args) { List list=new ArrayList(); Thread t1=new Thread(new Producer(list)); Thread t2=new Thread(new Comsumer(list)); t1.setName("生产者"); t2.setName("消费者"); t1.start(); t2.start(); } } class Producer implements Runnable{ List list; public Producer(List list) { this.list = list; } @Override public void run() { while (true){ synchronized (list){ if(list.size()>0){ try { list.wait();//如果生产满了,让当前线程等待.释放掉list锁.让消费者线程占锁开始消费!!! } catch (InterruptedException e) { e.printStackTrace(); } } Object o = new Object(); list.add(o); System.out.println(Thread.currentThread().getName()+o); list.notifyAll();//唤醒等待的线程 }//这个方法体结束才释放锁 } } } class Comsumer implements Runnable{ List list; public Comsumer(List list) { this.list = list; } @Override public void run() { while (true){ synchronized (list){ if(list.size()==0){ try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } Object remove = list.remove(0); System.out.println(Thread.currentThread().getName()+remove); list.notifyAll();//唤醒等待的 } } } }