第十一天
线程和进程
进程:是一个正在执行中的程序。
每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。
线程在控制着进程的执行。
一个进程中至少有一个线程。
创建线程方式一:(Thread)
子类覆盖父类中的run方法,将线程运行的代码存放在run中。
建立子类对象的同时线程也被创建。
通过调用start方法开启线程。
class DemoA extends Thread
{
public void run(){}//覆盖run方法
}
class DemoB
{
public static void main(String[] args)
{
Thread t1 =new DemoA();//创建线程对象
Thread t2 =new DemoA();//创建线程对象
ti.start();//启动线程,run方法无法启动线程
t2.start();//启动线程
}
}
创建线程方式二(Runnable)
实现Runnable接口
子类覆盖接口中的run方法。
通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。
Thread类对象调用start方法开启线程。
class DemoA implements Runnable//好处:还可以继承其他类。避免了单继承的局限性
{
public void run(){}//覆盖run方法
}
class DemoB
{
public static void main(String[] args)
{
Thread t1 =new DemoA();//创建线程对象
Thread t2 =new DemoA();//创建线程对象
ti.start();//启动线程,run方法无法启动线程,仅仅对象调用方法而已
t2.start();//启动线程
}
}
线程的四种状态:
sleep方法需要指定睡眠时间,单位是毫秒。
一个特殊的状态:就绪。具备了执行资格,但是还没有获取资源。
多线程的安全为题:
导致安全问题的出现的原因:
多个线程访问出现延迟。//cpu随机切换导致
线程随机性 。
注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
解决线程安全问题:
同步(synchronized)//降低了效率
格式:
synchronized(对象)
{
需要同步的代码;
}
同步可以解决安全问题的根本原因就在那个对象上。
该对象如同锁的功能。
同步的特点
同步的前提:
同步需要两个或者两个以上的线程。
多个线程使用的是同一个锁。
未满足这两个条件,不能称其为同步。
同步的弊端:
当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
同步函数
格式:
在函数上加上synchronized修饰符即可。
同步函数用的this锁
死锁:
同步中嵌套同步。
class Test implements Runnable
{
private boolean flag;
Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
while(true)
{
synchronized(MyLock.locka)//同步里面嵌套同步
{
System.out.println(Thread.currentThread().getName()+"...if locka ");
synchronized(MyLock.lockb)
{
System.out.println(Thread.currentThread().getName()+"..if lockb");
}
}
}
}
else
{
while(true)
{
synchronized(MyLock.lockb)//同步里面嵌套同步
{
System.out.println(Thread.currentThread().getName()+"..else lockb");
synchronized(MyLock.locka)
{
System.out.println(Thread.currentThread().getName()+".....else locka");
}
}
}
}
}
}
class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}
线程间的通信:
当多个线程炒作一个共享资源时候。
银行随机存取问题实例:
等待唤醒机制:
class BankTest
{
public static void main(String[] args)
{
ZhangHu zhanghu =new ZhangHu();
IntCun Int = new IntCun(zhanghu);
OutQu Out = new OutQu(zhanghu);
Thread t1 = new Thread(Int);
Thread t2 = new Thread(Out);
t1.start();
t2.start();
}
}
class ZhangHu //同一个资源。
{
private long money = 0;
private boolean flag=true;
public synchronized void set(long money)//存入线程所运行代码。
{
if(flag)
{
try{this.wait();}catch(Exception e){}
this.money += money;
System.out.println("存款成功,"+"余额为"+this.money);
}
// System.out.println("检测...........");
flag = false;
this.notify();//两个线程以上就将notify()换成notify。或者看下面代码lock锁
}
public synchronized void out(long money)//取出线程所运行代码。
{
if(!flag)
{
if(this.money>money)
{
try{
this.wait();
}
catch(Exception e)
{}
this.money-=money;
System.out.println("取钱成功,"+"余额为"+this.money);
}
else
{
System.out.println("取钱失败,"+"余额为"+this.money);
}
}
flag = true;
this.notify(); //两个以上线程操作此方法就将notify()换成notifyAll()。或者看下面代码lock锁
}
}
class IntCun implements Runnable//存入,转入。
{
private ZhangHu zhanghu;//存入同一个账户。使两个线程操作共享资源
IntCun(ZhangHu zhanghu)
{
this.zhanghu =zhanghu;
}
long money =(long)(Math.random()*2000);
public void run()//存钱的线程方法,里面调用set存入方法
{
while(true)
{
try
{
Thread.sleep((int)(Math.random()*2000));//随机时间存一次钱
//System.out.println("存入钱数*******"+money);
zhanghu.set(money);//随机存款
}
catch (Exception e)
{
}
}
}
}
class OutQu implements Runnable//取出。
{
private ZhangHu zhanghu;//取出的账户,使两个线程操作共享资源
OutQu(ZhangHu zhanghu)
{
this.zhanghu =zhanghu;
}
long money =(long)(Math.random()*2000);//定义随机钱数
public void run()//取钱的线程方法,里面调用out()取出方法。
{
while(true)
{
try
{
Thread.sleep((int)(Math.random()*2000));//随机时间取一次钱
//System.out.println("取出钱数....."+money);
zhanghu.out(money);//随机取款
}
catch (Exception e)
{
}
}
}
}
1.5新特性:
接口 Lock://替换synchronized
将synchronized 替换成了Lock接口。
将隐式锁,升级成了显示锁。
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。
void lock()
获取锁。
void unlock()
释放锁。
接口Condition://替代了 Object 监视器方法的使用。
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,
以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。
其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
void await()
造成当前线程在接到信号或被中断之前一直处于等待状态。
void signal()
唤醒一个等待线程。
void signalAll()
唤醒所有等待线程
替代上面代码:
import java.util.concurrent.locks.*;
class BankTest
{
public static void main(String[] args)
{
ZhangHu zhanghu =new ZhangHu();
IntCun Int = new IntCun(zhanghu);
OutQu Out = new OutQu(zhanghu);
Thread t1 = new Thread(Int);
Thread t2 = new Thread(Int);
Thread t3 = new Thread(Out);
Thread t4 = new Thread(Out);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class ZhangHu //同一个资源。
{
private long money = 0;
private boolean flag=true;
private Lock lock = new ReentrantLock();
private Condition condition_int = lock.newCondition();
private Condition condition_out = lock.newCondition();
public void set(long money)
{
String name =Thread.currentThread().getName();//获取线程名称,线程0和线程1
lock.lock();
try
{
if(flag)
{
try
{
condition_int.await();
}
catch (Exception e)
{
}
this.money += money;
System.out.println(name+"存款成功,"+"余额为"+this.money);
}
// System.out.println("检测...........");
flag = false;
condition_out.signal();//唤醒对方一个线程
}
finally
{
lock.unlock();//释放锁的动作一定要执行。
}
}
/*
格式:try()finally()
lock.lock();
try
{
while()
notFull.await();//要try,不try的话就在函数上抛,让下面调用的处理
notEmpty.signal();
}
finally
{
lock.unlock();
}
*/
public synchronized void out(long money)
{
String name =Thread.currentThread().getName();//获取线程名称,线程2和线程3
lock.lock();
try
{
if(!flag)
{
if(this.money>money)
{
try
{
condition_out.await();
}
catch (Exception e)
{
}
this.money-=money;
System.out.println(name+"取钱成功,"+"余额为"+this.money);
}
else
{
System.out.println(name+"取钱失败,"+"余额为"+this.money);
}
}
flag = true;
condition_int.signal();//唤醒对方一个线程
}
finally
{
lock.unlock();//释放锁的动作一定要执行。
}
}
}
class IntCun implements Runnable//存入,转入。
{
private ZhangHu zhanghu;//存入的账户
IntCun(ZhangHu zhanghu)
{
this.zhanghu =zhanghu;
}
long money =(long)(Math.random()*2000);
public void run()//存钱的线程方法
{
while(true)
{
try
{
Thread.sleep((int)(Math.random()*2000));//随机时间存一次钱
//System.out.println("存入钱数*******"+money);
zhanghu.set(money);//随机存款
}
catch (Exception e)
{
}
}
}
}
class OutQu implements Runnable//取出。
{
private ZhangHu zhanghu;//取出的账户
OutQu(ZhangHu zhanghu)
{
this.zhanghu =zhanghu;
}
long money =(long)(Math.random()*2000);//定义随机钱数
public void run()//取钱的线程方法
{
while(true)
{
try
{
Thread.sleep((int)(Math.random()*2000));//随机时间取一次钱
//System.out.println("取出钱数....."+money);
zhanghu.out(money);//随机取款
}
catch (Exception e)
{
}
}
}
}
停止线程:
stop方法已经过时。
如何停止线程?
只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。
只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:
当线程处于了冻结状态。
就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。
Thread类提供该方法 interrupt();
class StopThread implements Runnable
{
private boolean flag =true;
public void run()
{
while(flag)
{ try
{
wait();//让线程处于冻结状态,另一个线程读不到标记,就挂了。用中断interrupt();来解决。
}
catch (InterruptedException e)
{
flag = false;//让标记为假,让程序停止循环,程序结束
}
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.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");
}
}
守护线程(后台线程):
当所有前台线程结束后,后台线程自动结束。
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();
break;
}
System.out.println(Thread.currentThread().getName()+"......."+num);
}
System.out.println("over");
}
}
join:
当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
join可以用来临时加入线程执行。
优先级:Thread.MAX_PRIORITY,CPU执行权大小
class Demo implements Runnable
{
public void run()
{
for(int x=0; x<70; x++)
{
System.out.println(Thread.currentThread().toString()+"....."+x);
Thread.yield();
}
}
}
class JoinDemo
{
public static void main(String[] args) throws Exception
{
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
//t1.setPriority(Thread.MAX_PRIORITY);
t2.start();
//t1.join();//拿到CPU执行权,它从谁手里拿到执行权,那个被那执行权的就要等它执行完才执行。谁给谁等
for(int x=0; x<80; x++)
{
//System.out.println("main....."+x);
}
System.out.println("over");
}
}
wait和sleep的区别:
wait:释放cpu执行权,释放同步中锁。
sleep:释放cpu执行权,不释放同步中锁。