线程:
进程的概念:
进程是指在同一个操作系统(OS)中执行的子程序。
多进程:同一个操作系统中执行的多个并行的子程序。
线程:同一进程中执行的子程序流。
多线程:同一个进程中执行的多个并行的子程序流。
多进程的好处:提高CPU的使用率。
多线程的好处:提高CPU的使用率。
时间片方式(主流):全称叫CPU时间片,指的是CPU的一段执行时间,时间片分配给进程/线程,时间很短但能做很多事情。
有时间片的会抢占CPU,进程与进程抢,线程与线程抢。时间片用完了会再分。不管如何抢占,CPU的使用率也不会调到100%
并发是针对的时间段。严格来说在一个时间点是无并发的。人所感受到的都是时间段
线程与进程:
多进程是独立的堆空间,独立的栈空间。进程与进程是互不影响的
多线程是栈空间独立,堆空间共享。线程与线程是可以影响的。
线程是由进程内部的一些顺序执行的代码组成的。
没有进程不会有线程。一个进程可以有多个线程,有一个主线程,其它线程做为附加线程。
JAVA中如何调用进程
进程与操作系统相关,不能跨平台。JAVA中不推荐调进程。
与进程相关的两个类:都在java.lang包中
Runtime类(运行环境): exec()调用本地程序。
Process:进程类,是exec()的返回类型。
Runtime.getRuntime();返回与当前 Java 应用程序相关的运行时对象。
线程模型:
虚拟CPU(CPU时间片):Thead
代码和数据由Runnable提供,run()运行代码,数据是子类属性
写线程的方法:(课堂代码:TestThread.java,TestRunnable.java)
1、继承Thread 类:
1、extends Thead
2、重写run()方法
3、new 本类.start();
好处:编写简单,可读性好
2、实现Runnable接口
1、implements Runnable
2、重写run()方法
3、new 本类,new Thread(本类对象)
4、Thread对象.start();
好处:保留了类继承,代码和逻辑分离,便于实现多线程。
run()和start()是不一样的:
run()叫线程体方法,自身其实就是普通方法,run()不会启动线程
start()叫线程启动的方法,向线程调度来说明当前线程ready(准备好了)。而不是正在执行的状态。
优先级:1--10,1最小,10最大,在某些操作系统中优先级会失效的。
小优先级代表时间片小。
守护线程依赖于其它线程
join使自身加入主线程
线程中几个重要的方法: (课堂代码:TestDaemon.java,TestSleep.java,TestJoin.java)
Thread.currentThread()取当前线程(实现接口方式有时用这个)
sleep():暂停线程,本线程不会抢,除非sleep运行完,这个是静态方法。自己让出CPU,其它的来抢。
yield():暂停线程,给本类或>=本类优先级的。只给优先级高的让,一般优先级低的抢不到
join():让本线程加入主线程。在线程内部、程序依然是顺序执行的,乱序体现在不同线程。
tj.start();
tj.join();要按这个顺序执行,若把join放在前面是无意义的。
setDaemon(true)后台/守护线程
线程状态图:
▲ 初始状态 ▲阻塞状态 ▲终止状态
/ / ┍ 1等待输入 ┓
/ /sleep时间到 / 2sleep /
/start / / 3join /stop
/ / / /
┙ ┕ / /
▲ 可运行状态 _ _ _ OS选中 _ _ _/ ▲运行状态
(只缺CPU) / CPU到期或调用yield
┍ / /
/ / /wait
/ Synchronized/ /
/ / /
/ / /
/ / /
/ ┕ ┙
▲ 锁池状态 <------ ▲等待队列
notify/notifyall
.
同一个线程只能用start()启动一次。
多线程编码多半采用实现接口的方式
一个线程必须有自己的虚拟cpu
一个线程可以和其他线程共享代码和数据
synchronized可以加在方法层也可以加在代码块
当对一个方法或一个代码块加上后,意味着线程在同步代码中必须采用串行访问,不能再并行。
Synchronized用法
1.Synchronized修饰代码块(同步代码块),
public void push(char c){
synchronized(this)//只有持有当前对象锁标记的线程才能访问这个代码块,this就是对象锁,锁定代码块
{
...
}
}
对括号内的对象加锁,只有拿到锁标记的对象才能执行该代码块
2.Synchronized修饰方法
public synchronized void push(char c) {
...
}
在整个方法里,对当前对象的加锁,只有拿到锁标记的对象才能执行该方法。
对共享数据的读和写要一起同步,一般来说属性表示共享数据。
同步的问题:串行,大大的降低效率。
尽可能不要在方法层使用同步,使用synchronized。
所有的对象都有对象锁,一个钥匙。
池:池就是内存常驻
常量池、线程池、DB连接池、等待池、同步池、对象池
好处:快
坏处:占去了部分内存
同步最怕死锁
对象锁正在使用的对象不能直接操作。
interrupt()也会从运行状态到阻塞状态
stop()存在潜在的安全隐患,所以不再用。
1、对象-->锁--->对立
2、同步依赖对象锁(对象)、锁对象相同,同步语句串行
避免死锁:要保持顺序锁,不要回调,反向打开
因为线程的死锁,从而引发要多线程的通信
死锁:每个线程不释放自己拥有的资源,却申请别的线程拥有的资源,会造成死锁问题
没有获得加锁对象的锁标记的线程,不能访问只有获得该对象所标记才能访问的同步方法,但可以访问这个对象的非同步的方法。
例: t1 t2
synchronized(o1){ synchronized(o2){
synchronized(o2){ synchronized(o1){
} }
} }
t1和t2会形成死锁
Vector,Hashtable,StringBuffer 这些是同步的。
ArrayList,HashSet,StringBuild 这些是不同步的。
得到一个同步的ArrayList: List list=Collections.synchronizedList(new ArrayList());----外同步
List list=new Vector();-----外同步
wait();参数可选,可以是0个,1个,2个。 对象锁,放锁,自己等。
用 notify(); notifyAll(); 唤醒wait();
notify()随机唤醒一个,notifyAll()全部唤醒,随机选。
调用wait()方法表示让正在执行的线程停止执行进入对象的等待队列去等待,同时释放掉对象的锁
注意:只能对加锁的资源进行wait()和notify()。
1) wait():交出锁和CPU的占用;
2) notify():将从对象的等待池中移走一个任意的线程,并放到锁池中,那里的对象一直在等待,直到可以获得对象的锁标记。
3) notifyAll(): 将从等待池中移走所有等待那个对象的线程并放到锁池中,只有锁池中的线程能获取对象的锁标记,锁标记允许线程从上次因调用wait()而中断的地方开始继续运行
怎么用线程?
1、extends Thread,无共享时使用
2、implements Runnable,有共享时使用
同步有什么用:
线程是共享堆、独享栈的,所以有可能发生两个线程同时操作同一块内存。
同步可以使共同的操作变成单独操作。
同步原理:对象锁+同步
对象锁是对象所特有的锁,简单类型不是对象,没有锁。
同步的优缺点:
为了安全,同步不得不用。缺点就是效率非常低下。
多线程编码:
1、实现线程采用Runnable接口。
2、有共享数据的冲突用同步
3、避免死锁(采用顺序锁,不要回调)
4、若使用wait,notify进行交互时的原则
(1)用notifyAll代替notify
(2)wait是在同步语句的内部,用while循环
在什么情况下放锁(交钥匙):
1、同步代码执行完毕
2、异常未处理
3、wait()方法