1、进程:正在进行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径。或者叫一个控制单元。其实进程就是一个应用程序运行时的内存分配空间。
线程:其实就是进程中一个程序执行控制单元,一条执行路径。线程在控制着进程的执行。进程负责的是应用程序的空间的标示。线程负责的是应用程序的执行顺序。线程是程序中的执行线程,java虚拟机允许应用程序并发地运行多个执行线程。
一个进程至少有一个线程在运行,当一个进程中出现多个线程时,就称这个应用程序是多线程应用程序,每个线程在栈区中都有自己的执行空间,自己的方法区、自己的变量。
JVM启动的时候会有一个进程Java.exe,该进程中至少有一个线程负责Java程序的执行而且这个线程运行的代码存在于main方法中。该线程称之为主线程
扩展:其实更细节说明JVM,JVM启动不止一个线程,还有负责垃圾回收机制的线程。
因为当产生垃圾时,收垃圾的动作,是不需要主线程来完成,因为这样,会出现主线程中的代码执行会停止,会去运行垃圾回收器代码,效率较低,所以由单独一个线程来负责垃圾回收。
随机性的原理:因为cpu的快速切换造成,哪个线程获取到了cpu的执行权,哪个线程就执行。
返回当前线程的名称:Thread.currentThread().getName()
线程的名称是由:Thread-编号定义的。编号从0开始。线程要运行的代码都统一存放在了run方法中。线程要运行必须要通过类中指定的方法开启。start方法。(启动后,就多了一条执行路径)
start方法:1)、启动了线程;2)、让jvm调用了run方法。
如何在自定义的代码中,自定义一个线程
通过对api的查找,Java已经提供了对线程这类事物的描述,就是Thread类
创建线程的第一种方式:继承Thread,由子类复写thread类的run方法。
步骤:
1,定义类继承Thread类;
2,目的是复写run方法,将要让线程运行的代码都存储到run方法中;
3,通过创建Thread类的子类对象,创建线程对象;
4,调用线程的start方法,开启线程,并执行run方法。
发现运行结果每一次都不同:
因为多个线程都获取CPU的执行权,CPU执行到谁,谁就运行。(也就是随机性的原理)明确一点,在某一个时刻,只能有一个程序在运行(多核除外)。CPU在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象把多线程的运行行为在互相抢夺CPU的执行权。这就是多线程的一个特性:随机性。谁强到谁执行,至于执行多长,CPU说了算。
为什么要覆盖run方法呢?
Thread类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。也就是说Thread类中的run方法,用于存储线程要运行的代码。
class Demo extends Thread//定义类继承Thread类
{
public void run()//目的是复写run方法,将要让线程运行的代码都存储到run方法中;
{
for(int x=0; x<60; x++)
System.out.println("demo run----"+x);
}
}
class ThreadDemo
{
public static void main(String[] args)
{
//for(int x=0; x<4000; x++)
//System.out.println("Hello World!");
Demo d = new Demo();//创建好一个线程。
//d.start();//开启线程并执行该线程的run方法。
d.run();//仅仅是对象调用方法。而线程创建了,并没有运行。
for(int x=0; x<60; x++)
System.out.println("Hello World!--"+x);
}
}
练习:
创建两个线程,和主线程交替运行。
分析:原来线程都有自己默认的名称。Thread-编号 该编号从0开始。static Thread currentThread():获取当前线程对象。getName(): 获取线程名称。设置线程名称:setName或者构造函数。
class Test extends Thread
{
//private String name;
Test(String name)
{
//this.name = name;
super(name);
}
public void run()
{
for(int x=0; x<60; x++)
{
System.out.println((Thread.currentThread()==this)+"..."+this.getName()+" run..."+x);
}
}
}
class ThreadTest
{
public static void main(String[] args)
{
Test t1 = new Test("one---");
Test t2 = new Test("two+++");
t1.start();
t2.start();
// t1.run();
// t2.run();
for(int x=0; x<60; x++)
{
System.out.println("main....."+x);
}
}
}
线程状态:
被创建:start()
运行:具备执行资格,同时具备执行权;
冻结:sleep(time),wait()—notify()唤醒;线程释放了执行权,同时释放执行资格;
临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权;
消亡:stop()
Sleep方法需要指定睡眠时间,单位是毫秒。
一个特殊的状态:就绪。具备了执行资格,但是还没有获取资源。
线程状态图:
创建线程的第二种方式:实现runnable接口
步骤:
1.定义类实现runnable接口
2,覆盖runnable接口中的run方法
将线程要运行的代码存放在该run方法中
3,通过thread类建立线程对象。
4,将runnable接口的子类对象作为实际参数传递给thread类的构造函数
为什么要将runnable接口的子类对象传递给thread的构造函数
因为,自定义的run方法所属的对象是runnable接口的子类对象, 所以要让线程去指定指定对象的run方法,就必须明确该run方法所属对象。
5,调用thread类的start方法开启线程并调用runnable接口子类的run方法。
Ticket t = new Ticket();
直接创建Ticket对象,并不是创建线程对象。 因为创建对象只能通过new Thread类,或者new Thread类的子类才可以。所以最终想要创建线程。既然没有了Thread类的子类,就只能用Thread类。
Thread t1 = new Thread(t); //创建线程。
只要将t作为Thread类的构造函数的实际参数传入即可完成线程对象和t之间的关联
为什么要将t传给Thread类的构造函数呢?其实就是为了明确线程要运行的代码run方法。
t1.start();
-------------------------------------------------------
//面试
new Thread(new Runnable(){ //匿名
public void run(){
System.out.println("runnable run");
}
})
{
public void run(){
System.out.println("subthread run");
}
}.start(); //结果:subthread run
---------------------------------------------------------
Try {
Thread.sleep(10);
}catch(InterruptedException e){}//当刻意让线程稍微停一下,模拟cpu切换情况。
为什么要有Runnable接口的出现?
1:通过继承Thread类的方式,可以完成多线程的建立。但是这种方式有一个局限性,如果一个类已经有了自己的父类,就不可以继承Thread类,因为java单继承的局限性。
可是该类中的还有部分代码需要被多个线程同时执行。这时怎么办呢?
只有对该类进行额外的功能扩展,java就提供了一个接口Runnable。这个接口中定义了run方法,其实run方法的定义就是为了存储多线程要运行的代码。
所以,通常创建线程都用第二种方式。
因为实现Runnable接口可以避免单继承的局限性。
2:其实是将不同类中需要被多线程执行的代码进行抽取。将多线程要运行的代码的位置单独定义到接口中。为其他类进行功能扩展提供了前提。
所以Thread类在描述线程时,内部定义的run方法,也来自于Runnable接口。
实现Runnable接口可以避免单继承的局限性。而且,继承Thread,是可以对Thread类中的方法,进行子类复写的。但是不需要做这个复写动作的话,只为定义线程代码存放位置,实现Runnable接口更方便一些。所以Runnable接口将线程要执行的任务封装成了对象。
实现方式和继承方式有什么区别?考面试题
实现方式好处:避免了单继承的局限性,在定义线程时建议使用实现方式,
继承thread:线程代码存放在thread的子类run方法中。
实现runnable:线程代码存放在接口的子类的run方法。
练习:需求:简单的卖票程序。多个窗口同时买票。
class Ticket implements Runnable//extends Thread
{
private int tick = 100;
public void run()
{
while(true)
{
if(tick>0)
{
System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
}
}
}
}
class TicketDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);//创建了一个线程;
Thread t2 = new Thread(t);//创建了一个线程;
Thread t3 = new Thread(t);//创建了一个线程;
Thread t4 = new Thread(t);//创建了一个线程;
t1.start();
t2.start();
t3.start();
t4.start();
/*
Ticket t1 = new Ticket();
//Ticket t2 = new Ticket();
//Ticket t3 = new Ticket();
//Ticket t4 = new Ticket();
t1.start();
t1.start();
t1.start();
t1.start();
*/
}
}
通过分析,发现,打印出0,-1,-2等错票。
多线程的运行出现了安全问题。
多线程安全问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方式。
就是同步代码块。
格式:
synchronized(对象)//任意对象都可以。这个对象就是锁。
{
需要被同步的代码
}
对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
需求:简单的卖票程序。多个窗口同时买票。改进过的,解决打印出0,-1,-2等错票。
class Ticket implements Runnable
{
private int tick = 1000;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(tick>0)
{
//try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
}
}
}
}
}
class TicketDemo2
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
Thread t4 = new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
火车上的卫生间---经典。
同步
同步的前提:
1,必须要有两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程在运行。
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源,产生了死锁。
练习:
需求:银行有一个金库,有两个储户分别存300员,每次存100,存3次
目的:该程序是否有安全问题,如果有,如何解决?
如何找问题:
1,明确那些代码是多线程运行代码
2,明确共享数据
3,明确多线程运行代码中那些语句是操作共享数据。
class Bank
{
private int sum;
//Object obj = new Object();
public synchronized void add(int n)
{
//synchronized(obj)
//{
sum = sum + n;
try{Thread.sleep(10);}catch(Exception e){}
System.out.println("sum="+sum);
//}
}
}
class Cus implements Runnable
{
private Bank b = new Bank();
public void run()
{
for(int x=0; x<3; x++)
{
b.add(100);
}
}
}
class BankDemo
{
public static void main(String[] args)
{
Cus c = new Cus();
Thread t1 = new Thread(c);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
同步的第二种表现形式:
同步函数:其实就是将同步关键字定义在函数上,让函数具备了同步性。
同步函数是用的哪个锁呢?
通过验证,函数都有自己所属的对象this,所以同步函数所使用的锁就是this锁。
当同步函数被static修饰时,这时的同步用的是哪个锁呢?
静态函数在加载时所属于类,这时有可能还没有该类产生的对象,但是该类的字节码文件加载进内存就已经被封装成了对象,这个对象就是该类的字节码文件对象。
所以静态加载时,只有一个对象存在,那么静态同步函数就使用的这个对象。
这个对象就是 类名.class
同步代码块和同步函数的区别?
同步代码块使用的锁可以是任意对象。
同步函数使用的锁是this,静态同步函数的锁是该类的字节码文件对象。
在一个类中只有一个同步,可以使用同步函数。如果有多同步,必须使用同步代码块,来确定不同的锁。所以同步代码块相对灵活一些。
同步函数用的哪一个锁呢
函数需要被对象调用,那么函数都有一个所属对象引用,就是this,所以同步函数使用的锁是this。通过该程序进行验证
需求:使用两个线程来卖票,一个线程在同步代码块中,一个线程在同步函数中,都在执行买票动作。
class Ticket implements Runnable
{
private int tick = 100;
Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(this)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
}
}
}
}
else
while(true)
show();
}
public synchronized void show()//this
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
}
}
}
class ThisLockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(Exception e){}
t.flag = false;
t2.start();
// Thread t3 = new Thread(t);
// Thread t4 = new Thread(t);
// t3.start();
// t4.start();
}
}
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不再是this,因为静态方法中也不可以定义this
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。类名.class 该对象的类型是Class。 静态的同步方法,使用的锁是该方法所在类的字节码文件对象,类名.class
class Ticket implements Runnable
{
private static int tick = 100;
//Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(Ticket.class)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
}
}
}
}
else
while(true)
show();
}
public static synchronized void show()
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
}
}
}
class StaticMethodDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(Exception e){}
t.flag = false;
t2.start();
}
}
单例设计模式
★考点问题:请写一个延迟加载的单例模式?写懒汉式;当出现多线程访问时怎么解决?加同步,解决安全问题;效率高吗?不高;怎样解决?通过双重判断的形式解决。
//懒汉式:延迟加载方式。
当多线程访问懒汉式时,因为懒汉式的方法内对共性数据进行多条语句的操作。所以容易出现线程安全问题。为了解决,加入同步机制,解决安全问题。但是却带来了效率降低。
为了效率问题,通过双重判断的形式解决。
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){ //锁是谁?字节码文件对象;
if(s == null){
synchronized(Single.class){
if(s == null)
s = new Single();
}
}
return s;
}
}
懒汉式延迟加载,如果多线程访问时有安全问题,用同步锁解决,加同步使用的锁是该类所属的文件
public class SingleDemo {
public static void main(String[] args) {
}
}
//单例设计模式
// 饿汉式
/*class Single
{
private static final Single s=new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}*/
//懒汉式 在多线程时,就产生安全隐患,加上同步比较低效,懒汉式实例的延迟加载, 在多线程时,就产生安全隐患,加上同步比较低效,有同步代码块和同步函数,
//用双重否定,来改变低效,加同步时,使用的锁是该类所属的字节码文件对象
class Single
{
private static Single s=null;
private Single(){}
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
// --->A
// --->B
s=new Single();
}
}
return s;
}
}
《《《《————————————》》》》
同步死锁:通常只要将同步进行嵌套,就可以看到现象。同步函数中有同步代码块,同步代码块中还有同步函数。
死锁:
同步中嵌套同步,而锁不同。
public class DeadLockTest {
public static void main(String[] args) {
Thread t1=new Thread(new Test11(true));
Thread t2=new Thread(new Test11(false));
t1.start();
t2.start();
}
}
class Test11 implements Runnable
{
private boolean flag;
Test11(boolean flag)
{
this.flag=flag;
}
public void run()
{
if(flag)
{ while(true)
{
synchronized(MyLock.locka)
{
System.out.println("if locka");
synchronized(MyLock.lockb)
{
System.out.println("if lockb");
}
}
}
}
else{
while(true)
{
synchronized(MyLock.lockb)
{
System.out.println("else lockb");
synchronized(MyLock.locka)
{
System.out.println("else locka");
}
}
}
}
}
}
class MyLock
{
static Object locka=new Object();
static Object lockb=new Object();
}
线程间通信:思路:多个线程在操作同一个资源,但是操作的动作却不一样。
1:将资源封装成对象。
2:将线程执行的任务(任务其实就是run方法。)也封装成对象。
等待唤醒机制:涉及的方法:
wait:将同步中的线程处于冻结状态。释放了执行权,释放了资格。同时将线程对象存储到线程池中。
notify:唤醒线程池中某一个等待线程。
notifyAll:唤醒的是线程池中的所有线程。
注意:
1:这些方法都需要定义在同步中。
2:因为这些方法必须要标示所属的锁。
你要知道 A锁上的线程被wait了,那这个线程就相当于处于A锁的线程池中,只能A锁的notify唤醒。
3:这三个方法都定义在Object类中。为什么操作线程的方法定义在Object类中?
因为这三个方法都需要定义同步内,并标示所属的同步锁,既然被锁调用,而锁又可以是任意对象,那么能被任意对象调用的方法一定定义在Object类中。
wait和sleep区别: 分析这两个方法:从执行权和锁上来分析:
wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的notify或者notifyAll来唤醒。
sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。
wait:线程会释放执行权,而且线程会释放锁。
Sleep:线程会释放执行权,但不是不释放锁。
等待唤醒机制
notifyAll();唤醒所有的等待。
wait(),notify(),notifyAll()出现在同步里面。当前线程必须有此对象监视器。 wait()时要标识出哪个锁中的wait,例如r.wait()为何?因为同步会出现嵌套,wait()notify().notifyAll()都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁。
为什么这些操作线程的方法要定义object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们锁操作线程只有的锁,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒,不可以对不同锁中的线程进行唤醒,也就是说,等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义object类中。
public class InputOutputDemo {
public static void main(String[] args) {
Res r=new Res();
Input in=new Input(r);
Output out=new Output(r);
Thread t1=new Thread(in);
Thread t2=new Thread(out);
t1.start();
t2.start();
}
}
class Res
{
String name;
String sex;
boolean flag=false;
}
class Input implements Runnable
{
private Res r;
Object obj=new Object();
Input(Res r)
{
this.r=r;
}
public void run()
{
int x=0;
while(true)
{
synchronized(r/*Input.class*/)
{
if(r.flag)
try {
r.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(x==0)
{
r.name="mike";
r.sex="man";
}
else{
r.name="lilli";
r.sex="nv";
}
x=(x+1)%2;
r.flag=true;
r.notify();
}
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r=r;
}
public void run()
{ while(true)
{
synchronized(r/*Input.class*/)
{ if(!r.flag)
try {
r.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(r.name+"....."+r.sex);
r.flag=false;
r.notify();
}
}
}
}
《《《《——————————》》》》
代码优化
public class InputOutputDemo {
public static void main(String[] args) {
Res r=new Res();
new Thread(new Input(r)).start();
new Thread(new Output(r)).start();
/*Input in=new Input(r);
Output out=new Output(r);
Thread t1=new Thread(in);
Thread t2=new Thread(out);
t1.start();
t2.start();*/
}
}
class Res
{
private String name;
private String sex;
private boolean flag=false;
public synchronized void set(String name,String sex)
{ if(flag)
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.name=name;
this.sex=sex;
flag=true;
this.notify();
}
public synchronized void out()
{ if(!flag)
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(name+"........"+sex);
flag=false;
this.notify();
}
}
class Input implements Runnable
{
private Res r;
Object obj=new Object();
Input(Res r)
{
this.r=r;
}
public void run()
{
int x=0;
while(true)
{
if(x==0)
r.set("mike","man");
else
r.set("lilli","nv");
x=(x+1)%2;
}
}
}
class Output implements Runnable
{
private Res r;
Output(Res r)
{
this.r=r;
}
public void run()
{ while(true)
{
r.out();
}
}
}
《《《《————————》》》》
需求:多生成者和消费者情况
对于多个生产者和消费者。
为什么要定义while判断标记。
原因:让被唤醒的线程再一次判断标记。
为什么定义notifyAll,
因为需要唤醒对方线程。因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。
public class ProducerConsumerDemo {
/**多生产者消费者的情况
* 当出现多个线程时,要用while标识和notifyAll
* @param args
*/
public static void main(String[] args) {
Resource r=new Resource();
Produce pro=new Produce(r);
Consumer con=new Consumer(r);
Thread t1=new Thread(pro);
Thread t2=new Thread(pro);
Thread t3=new Thread(con);
Thread t4=new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resource
{
private String name;
private int count=1;
private boolean flag=false;
public synchronized void set(String name)
{ while(flag)//用了while出现了全部等待
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.name=name+"------"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag=true;
this.notifyAll();
}
public synchronized void out()
{ while(!flag)
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"...消费者.."+this.name);
flag=false;
this.notifyAll();
}
}
class Produce implements Runnable
{
private Resource res;
Produce(Resource res)
{
this.res=res;
}
public void run()
{
while(true)
{
res.set("商品++");
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res=res;
}
public void run()
{
while(true)
{
res.out();
}
}
}
《《《《——————————》》》》
JDK1.5 中提供了多线程升级解决方案。
将同步Synchronized替换成现实Lock操作。
将Object中的wait,notify notifyAll,替换了Condition对象。
该对象可以Lock锁 进行获取。
该示例中,实现了本方只唤醒对方操作。
Lock:替代了Synchronized
lock
unlock
newCondition()
Condition:替代了Object wait notify notifyAll
await();
signal();
signalAll();
多生产者和消费者
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
// t1 t2
private Lock lock = new ReentrantLock();
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
public void set(String name)throws InterruptedException
{
lock.lock();
try
{
while(flag)
condition_pro.await();//t1,t2
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag = true;
condition_con.signal();
}
finally
{
lock.unlock();//释放锁的动作一定要执行。
}
}
// t3 t4
public void out()throws InterruptedException
{
lock.lock();
try
{
while(!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
flag = false;
condition_pro.signal();
}
finally
{
lock.unlock();
}
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.set("+商品+");
}
catch (InterruptedException e)
{
}
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.out();
}
catch (InterruptedException e)
{
}
}
}
}
Jdk1.5中提供了多线程升级解决方案,将同步synchronized替换成现实lock操作
将object中的wait,notify,notifyAll替换了condition对象
该对象可以lock锁,进行获取。该实例中,实现了本方只唤醒对方操作。
线程的停止:通过stop方法就可以停止线程。但是这个方式过时了。
停止线程:原理就是:让线程运行的代码结束,也就是结束run方法。
怎么结束run方法?一般run方法里肯定定义循环。所以只要结束循环即可。
第一种方式:定义循环的结束标记。因为线程运行代码一般都是循环,只要控制了循环即可。
第二种方式:如果线程处于了冻结状态,是不可能读到标记的,这时就需要通过Thread类中的interrupt方法,将其冻结状态强制清除。让线程恢复具备执行资格的状态,让线程可以读到标记,并结束。即使用interrupt(中断)方法,该方法是结束线程的冻结状态,使线程回到运行状态中来
特殊情况:
当线程处于了冻结状态
就不会读取到标记,那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行消除。
强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束
Thread类提供该方法interrupt();
class StopThread implements Runnable
{
private boolean flag =true;
public void run()
{
while(flag)
{
System.out.println(Thread.currentThread().getName()+"....run");
}
}
public void changeFlag()
{
flag = false;
}
}
class StopThreadDemo
{
public static void main(String[] args)
{
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
int num = 0;
while(true)
{
if(num++ == 60)
{
//st.changeFlag();
//t1.interrupt();
//t2.interrupt();
break;
}
System.out.println(Thread.currentThread().getName()+"......."+num);
}
System.out.println("over");
}
}
---------< java.lang.Thread >----------
interrupt():中断线程。
setPriority(int newPriority):更改线程的优先级。
getPriority():返回线程的优先级。
toString():返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
Thread.yield():暂停当前正在执行的线程对象,并执行其他线程。
setDaemon(true):将该线程标记为守护线程或用户线程。将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java虚拟机退出。该方法必须在启动线程前调用。
join:临时加入一个线程的时候可以使用join方法。
当A线程执行到了B线程的join方式。A线程处于冻结状态,释放了执行权,B开始执行。A什么时候执行呢?只有当B线程运行结束后,A才从冻结状态恢复运行状态执行。
Join方法
申请加入到CPU执行权。
特点:
当A线程执行到了B线程的.join()方法时,A就会等待,等B线程都执行完,A才会执行。
《《《《——————————》》》》
toString()返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
System.out.println(Thread.currentThread().toString()+"...."+x);
默认优先级是5,setPriority(int newPriority)
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);
Thread.yield();暂停当前正在执行的线程对象,并执行其他线程。
《《《《——————————》》》》
class MyThread extends Thread{
public void run(){
try {
Thread.currentThread().sleep(3000);
} catch (InterruptedException e) {
}
System.out.println("MyThread running");
}
}
public class ThreadTest{
public static void main(String argv[]) {
MyThread t = new MyThread();
t.run();
t.start();
System.out.println("Thread Test");
}
}
代码分析过程:
MyThread t = new MyThread();
创建了一个线程。
t.run();
调用MyThread对象的run方法。这是只有一个线程在运行就是主线程。当主线程执行到了run方法中的sleep(3000);时。这是主线程处于冻结状态。程序并没有任何执行。当3秒过后,主线程打印了 MyThread running。 run方法执行结束。
t.start();
开启了t线程。
有两种可能情况。
第一种,主线程在只执行了t.start()后,还具有执行权,继续往下执行,打印了Thread Test。主线程结束。t线程获取执行权,调用自己的run方法。然后执行的sleep(3000);冻结3秒。3秒后,打印MyThread running t线程结束,整个程序结束。
第二种情况:
主线程执行到t.start();开启了t线程,t线程就直接获取到了执行权。就调用自己的run方法。指定到sleep(3000).t线程冻结3秒,这是t线程就是释放了执行权。那么主线程开始执行打印了Thread Test,主线程结束。等到3秒后,t线程打印MyThread running ,然后t线程结束。程序结束。
《《《《——————————》》》》
Lock接口:多线程在JDK1.5版本升级时,推出一个接口Lock接口。
解决线程安全问题使用同步的形式,(同步代码块,要么同步函数)其实最终使用的都是锁机制。
到了后期版本,直接将锁封装成了对象。线程进入同步就是具备了锁,执行完,离开同步,就是释放了锁。
在后期对锁的分析过程中,发现,获取锁,或者释放锁的动作应该是锁这个事物更清楚。所以将这些动作定义在了锁当中,并把锁定义成对象。
所以同步是隐示的锁操作,而Lock对象是显示的锁操作,它的出现就替代了同步。
在之前的版本中使用Object类中wait、notify、notifyAll的方式来完成的。那是因为同步中的锁是任意对象,所以操作锁的等待唤醒的方法都定义在Object类中。
而现在锁是指定对象Lock。所以查找等待唤醒机制方式需要通过Lock接口来完成。而Lock接口中并没有直接操作等待唤醒的方法,而是将这些方式又单独封装到了一个对象中。这个对象就是Condition,将Object中的三个方法进行单独的封装。并提供了功能一致的方法 await()、signal()、signalAll()体现新版本对象的好处。
< java.util.concurrent.locks >Condition接口:await()、signal()、signalAll();
--------------------------------------------------------
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
}
finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
}
finally {
lock.unlock();
}
}
}
———寄语:走自己的路,让别人羡慕嫉妒恨去吧。瞄准自己的方向,勇往直前!加油!濛濛在努力中。。。。。——— 濛濛