Thread 多线程-java.lang.Thread描述线程的类
特点:随机性
进程:一个正在执行中的程序。每个进程都有一个执行顺序
该顺序是一个执行路径,或控制单元
线程:进程中一个独立的控制单元
一、概述
1.线程控制进程执行
2.一个进程至少有一个线程(主线程)
3.JVM启动一个程序,不止有一个线程,还有一个回收垃圾机制的线程
4. 线程的运行状态
5.获取线程信息的方法:
线程对象,线程名称
getName()/setName()
currentThread()//返回当前线程对象
二、创建线程
创建线程的2个方法:
1.继承Thread类
2.实现Runnable接口
(一)继承Thread类
继承Thread类-覆盖run/创建实例/调用start
public void run()//run()用于存放需要通过多线程执行的代码
void start()//start()使该线程开始执行
class New extends Thread{
public void run(){
//运行代码
}
}
New t=new New();
t.start();
(二)实现Runnable接口
实现Runnable接口 –实现接口/实例做参数/调用start
class New implements Runnable{
public void run(){
//运行代码
}
}
new Thread(new New()).start();
(三)两种方法的区别
实际上就是Thread的构造函数的继承和重载(子类空参数ctor与Thread(<T extendsRunnable> t))。
1.一种是继承,一种是实现
2.一种将运行代码放在继承的run方法中,一种将运行代码放在实现的run方法中
3.一种是单继承,不具备扩展性,另一种可以继承多个接口和其他类。
三、多线程安全问题
原因:多条语句操作同一个线程间共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程就参与进来执行,导致共享数据出错。
解决:只让单一线程进行操作,操作结束其他线程才可以操作。
同步synchronized
同步前提:
1.两个以上的线程
2.多个线程使用同一个锁
优点:解决多线程安全问题
缺点:须要判断锁,消耗资源
1.同步代码块
synchronized(obj){//…}
2.同步函数
synchronized void func(){//…}
注意只把须要同步的代码抽取出来
同步函数不需要设置锁,因为函数都被对象调用,函数都有一个所属对象引用就是this.即同步函数的锁就是this
静态同步函数-因为静态函数进内存时尚未有本类对象,所以锁肯定不是this.是本类的字节码class对象。
如何寻找多线程中的安全问题
a.明确哪些代码是多线程运行代码。
b.明确共享数据。
c.明确多线程运行代码中哪些语句是操作共享数据的。
写出一个死锁程序
(标记为真进入第一个代码块中,先进obj1的锁,修改标记为假,这时候需要标记真的线程等待,需要标记假的线程可以进入,如果发生死锁则,持有obj1的锁无法进入obj2,持有obj2的锁无法进入obj1)
if(true==bFlag){
synchronized(obj1){
//bFlag=!bFlag;
synchronized(obj2){}
}
}else{
synchronized(obj2){
//bFlag=!bFlag;
synchronized(obj1){}
}
}
四、单例设计模式
懒汉式:
class Single{
private static Single s=null;
private Single(){}
public static Single getInstance(){
if(null==s){
s=new Single();
}
return s;
}
}
线程安全:添加同步代码块
public static Single getInstance(){
if(null==s){
synchronized(Single.class){
if(null==s){
s=new Single();
}
}
}
return s;
}
五、线程间通信问题
多个线程操作同一个资源,但是动作不同。
操作同一个资源时,注意使用同一个锁。
等待唤醒机制:
wait(),notify(),notifyAll()
定义在Object类中的原因:
1. wait(),notify(),notifyAll()是用于同步的方法,必须要标识其所操作线程的锁。只有同一个锁上的wait可以被同一个锁上的notify唤醒。
2.锁可以是任一对象。而任一对象的超类是Object,所以将这三种方法归在Object下。
wait使当前线程进入等待,会抛出InterruptedException。
notify在其他线程中使用,对持有同一个锁进入wait的线程进行唤醒。
wait和notify必须是不同线程使用相同的锁。
线程运行时内存会建立线程池存放等待线程,notify会唤醒其中的等待线程,通常按照顺序唤醒。
wait()与sleep()区别:
wait():释放cpu执行权,释放锁。
sleep():释放cpu执行权,不释放锁。
notifyAll:在生产消费模型中,需要唤醒对方线程时,若只用notify,容易出现只唤醒本方线程的情况。导致程序中的所以线程都等待。
六、生产消费模型
生产方-producer
消费方-consumer
资源-resource
p/c:
1实现Runnable接口。
2所有p/c在构造函数中都传入同一个r对象。
3在p中资源++,在c中资源—
4改进3,将资源+-的动作提取出来放入r类中。
5r类
a设置一个flag,false时可以生产,true时可以消费。
b设置一个name,表示资源内容
c设置一个cnt,表示资源数量记录
6r类中资源+的方法:
当flag为真时,说明当前生产出的产品还没有被消费,则进入等待
当flag为假时,说明之前生产的产品已经被消费掉,则进行新的生产,资源数量+1;生产结尾将flag设置为true,并唤醒所有等待线程。
7r类中资源-的方法:
当flag为假时,说明当前没有生产出产品,则进入等待
当flag为真时,说明当前有生产出产品,则进行新的消费,资源数量-1;消费结尾将flag设置为false,并唤醒所有等待线程。
class Resource{
boolean bFlag=false;
int cnt=0;
String content;
public synchronized void produce(String name) {
if(bFlag){
try{wait();}catch(InterruptedException e){}
}
content=name+" : "+(++cnt);
System.out.println(Thread.currentThread().getName()+":produced:"+content);
bFlag = true;
notifyAll();
}
public synchronized void consume() {
if(!bFlag){
try{wait();}catch(InterruptedException e){}
}
try{Thread.sleep(100);}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+":\tconsumed:"+content);
bFlag = false;
notifyAll();
}
}
class Producer implements Runnable{
private Resource r;
public Producer(Resource r) {
super();
this.r = r;
}
public void run() {
int i=0;
while(true){
if(0==i){
r.produce("cake");
}else{
r.produce("cookie");
}
i=(++i)%2;
}
}
}
class Consumer implements Runnable{
private Resource r;
public Consumer(Resource r) {
super();
this.r = r;
}
public void run() {
while(true){
r.consume();
}
}
}
class test2
{
public static void main(String[] args)
{
Resource r=new Resource();
Producer p=new Producer(r);
Consumer c=new Consumer(r);
new Thread(p).start();
//new Thread(c).start();//p/c只能各开启一个线程
new Thread(c).start();
//new Thread(p).start();
}
}
七、Lock
新方法-只唤醒对方线程不唤醒本方:
java.util.concurrent.locks
Condition
|--Lock
synchronized 方法或语句的使用提供了对于每个对象相关的隐式监视器锁的访问,但却强制所有锁获取和释放均要出现在一个块结构中:当获取了多个锁时,它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的词法范围内释放所有锁。
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock替代了 synchronized 方法和语句的使用,Condition替代了 Object 监视器方法的使用。
Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。
Condition接口中有方法await(),signal(),signalAll();替代wait,notify,notifyAll
通用格式:
Lock myLock=new ReentrantLock();//新建锁
myLock.lock();//上锁
// Condition con = myLock.newCondition(); //新建一个等待 set,可以为同一个对象提供多个
try{
// con.await();
//…
}finally{
mylock.unlock;//一定解锁
}
await(),signal(),signalAll()则需要配合对应的锁使用,比如myLock.await();
现在Lock可以对同一对象提供多个wait-set,即可以在资源中针对生产和消费用同一个Lock对象设置两个不同的Condition,生产和消费线程使用其对应的Condition。这样就可以区分开,实现多个生产消费线程。
import java.util.concurrent.locks.*;
class Resource{
boolean bFlag=false;
int cnt=0;
String content;
Lock myLock=new ReentrantLock();
Condition conCon=myLock.newCondition();
Condition conPro=myLock.newCondition();
public void produce(String name) {
myLock.lock();
try{
while(bFlag){
conPro.await();
}
content=name+" : "+(++cnt);
System.out.println(Thread.currentThread().getName()+":produced:"+content);
bFlag = true;
conCon.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
myLock.unlock();
}
}
public void consume() {
myLock.lock();
try{
while(!bFlag){
conCon.await();
}
try{Thread.sleep(100);}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName()+":\tconsumed:"+content);
bFlag = false;
conPro.signalAll();
}catch(InterruptedException e){
e.printStackTrace();
}finally{
myLock.unlock();
}
}
}
class Producer implements Runnable{
private Resource r;
public Producer(Resource r) {
super();
this.r = r;
}
public void run() {
int i=0;
while(true){
if(0==i){
r.produce("cake");
}else{
r.produce("cookie");
}
i=(++i)%2;
}
}
}
class Consumer implements Runnable{
private Resource r;
public Consumer(Resource r) {
super();
this.r = r;
}
public void run() {
while(true){
r.consume();
}
}
}class test2
{
public static void main(String[] args)
{
Resource r=new Resource();
Producer p=new Producer(r);
Consumer c=new Consumer(r);
new Thread(p).start();
new Thread(c).start();
new Thread(c).start();
new Thread(p).start();
new Thread(p).start();
}
}
八、线程停止方法
1.run中设置停止标记,自然结束
2.线程处于冻结状态时,无法读取到标记,线程无法结束。可以使用Interrupt
interrupt方法可以用来请求终止线程:
当对一个线程调用interrupt方法时,线程的中断状态将被置位,将冻结状态的线程强制清除,恢复运行状态。当对一个阻塞的线程调用interrupt,会产生中断异常。可以在中断异常的捕获中修改标记。从而使run可以结束。
a在Runnable子类中,添加一个更改标记的方法;run中设置结束标记,try-catch捕获Interrupted异常,在catch中调用更改标记的方法
b在主线程中显示声明新的线程
c启动线程
d让线程进入冻结状态
e经过一段时间的运行后,主线程中对已声明的线程对象使用interrupt()方法。
package test;
class TestStop implements Runnable{
private boolean bFlag=true;
public synchronized void run() {
System.out.println("start :"+Thread.currentThread().getName());
while(bFlag){
try{
wait();
}catch(InterruptedException e){
stopAll();
}
try{Thread.sleep(100);}catch(InterruptedException e){}
System.out.println("in while :"+Thread.currentThread().getName());
}
System.out.println("out of while :"+Thread.currentThread().getName());
}
public void stopAll(){
bFlag=false;
}
}
class test2
{
public static void main(String[] args)
{
TestStop ts=new TestStop();
int i=0;
Thread ts1=new Thread(ts);
ts1.start();
Thread ts2=new Thread(ts);
ts2.start();
Thread ts3=new Thread(ts);
ts3.start();
while(6!=i++){
try{Thread.sleep(100);}catch(InterruptedException e){}
System.out.println(Thread.currentThread().getName());
}
ts1.interrupt();
ts2.interrupt();
ts3.interrupt();
System.out.println("over");
}
}
九、守护线程及其他
setDaemon(boolean on)
若想将某个线程标记为守护线程。当线程启动前必须使用一次此方法。
t1=new Thread(…);
t1.setDaemon(true);
t1.start();
当在运行的线程都是守护线程时,JVM自动退出。
join()
当A线程执行到了b线程的join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。
yield()
暂停当前正在执行的线程对象,并执行其他线程。
setPriority()
设置优先级
MAX_PRIORITY 最高优先级10
MIN_PRIORITY 最低优先级1
NORM_PRIORITY 分配给线程的默认优先级