黑马程序员——Java多线程

黑马程序员——Java多线程--------------------
---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a 
href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! -------------------------------
一、基本概念:
线程:进程中一个独立的控制单元
多线程:一个程序中负责程序执行的单元(执行路径),一个程序有多个执行路径称多线程。
jvm启动至少两个线程在运行,一个main函数线程,一个Java垃圾回收机制线程。


二、线程创建:
创建线程的方式:1.继承Thread类,2实现Runable接口
1.定义继承 Thread 类
2.复写 Thread 类中的run()方法,自定义代码存在run()方法中,让线程运行
3.调用线程star()方法,作用:启动线程,调用run()方法




 class Demo extends Thread
{
private String name;
Demo(String name){


super(name);

}
public void run(){ //自定义线程任务的函数
   for(int i=0;i<10;i++){

System.out.println(name+"....i"=i+".....name="+Thread.currentThread().getName());
}
}
}
class ThreadDemo
{
public static void main(String args[]){
 Demo d=new Demo();//创建好一个线程
 Thread t1=new Thread(d);
 Thread t2=new Thread(d);
        t1.start();
t2.start();
// Demo d1=new Demo("小布");
// Demo d2=new Demo("小强");
 //d1.start();  //开启线程并执行该线程run()方法
// d2.start();  //仅对象调用方法,线程创建了,并没有执行 
       d.run();
 System.out.println("线程完成了"+Thread.currentThread().getName());
}
}
习题:
创建线程,和主线程进行交替
线程有名称,通过set/get
package com.study;


public  class Test {
public static void main(String[] args) {

Thread t1=new Thread("one----");
Thread t2=new Thread("two+++++");
t1.start();
t2.start();
t1.run();
t2.run();

for(int x=0;x<60;x++){
  System.out.println(Thread.currentThread().getName()+"run...."+x);//getName获取当前线程名称
  }
 
}


      
}
 class ThreadTest  extends Thread{
private String name;
     
ThreadTest(String name){
    this.name=name;
     }
      
   public void run(){
  for(int x=0;x<60;x++){
  System.out.print(this.name+"run...."+x);
  }
   }



}
线程状态:
1.新建:start()启动线程
2.运行:run();具备执行资格和执行权
3.冻结:wait()等待,notify()唤醒、sleep(time)睡眠
4.阻塞:线程有CPU执行资格,但是没有CPU执行权
5.消亡:stop


 
 创建线程方式二: 实现Runable接口


1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法,将线程任务代码封装到run方法中。
3.通过 Thread 类创建对象,将  Runnable 接口中的子类对象作为 Thread 的构造参数进行传递,
线程的任务封装在Run()方法中,要让线程对象明确运行的run方法的所属对象。
4.调用Thread类中的Start()方法开启线程


 实现Runable接口好处:
 1.将线程任务从线程子类中分离出来,进行单独封装,按照面向对象的思想,将任务封装成对象。
 2.避免Java单继承的局限性。


package com.study;


public class TickDemo {


/**
*多线程实现多个窗口卖票
*/
public static void main(String[] args) {

Ticket t=new Ticket(); //创建买票对象,使用其run方法

Thread t1=new Thread(t); //创建对个线程,传人t作为Thread 的构造参数进行传递实现对个窗口买票
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start(); //启动线程,调用Tick run方法
t2.start();
t3.start();
t4.start();



}


}
class Ticket implements Runnable{

    private int tick=100;//票数
@Override
public void run() {
while(tick>0){
System.out.println(Thread.currentThread().getName()+"....sale:"+tick--);
}

}

}


三、线程安全问题:
1.多个线程同时操作共享的数据。
2.操作共享数据线程代码有多条。
当一个线程执行操作共享数据时,可能被另外一个线程也在进行访问操作。
(一个在执行修改操作,另一个删除的话就导致线程安全问题)


解决线程安全问题:
同步代码块:
将多条共享数据线程代码块封装起来,有线程访问时,其他线程停止操作不参与运算,当前线程执行完,其他才进行运算。
Java 专业解决方式:同步代码块
synchronized(对象)   --对象锁,标志(火车卫生间)
{
    需要同步的共享代码块
}
对象如同锁,持有锁的线程可以再同步中执行,没有锁的线程即使获得CPU执行权也无进不去。
同步好处:解决线程安全问题
同步的弊端:相对降低效率,同步的线程都会判断同步锁,消耗资源。
同步的前提:1.必须有多个线程(两个或两个以上)并使用同一个对像锁。
            2.保证同步中只有一个线程运行




package com.study;


public class TickDemo {


/**
*多线程实现多个窗口卖票
*/
public static void main(String[] args) {

Ticket t=new Ticket(); //创建买票对象,使用其run方法

Thread t1=new Thread(t); //创建对个线程,传人t,实现对个窗口买票
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();



}


}
class Ticket implements Runnable{

    private int tick=1000;//票数
    Object obj=new Object();
@Override
public void run() {
while(true){
 synchronized (obj) {  //线程同步对象锁,如线程t1执行是上锁,其它线程2、3、4无法进入线程中
   if(tick>0){
    try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"....sale:"+tick--);
}
 }
}
}

}

案例:银行存钱案例,同步函数更方便
package com.study;


public class BankDemo {


/**
* 银行储户,两个,每个都到银行存钱每次100元,共存了3次
*/
public static void main(String[] args) {

Cus c=new Cus();
  Thread t1=new Thread(c); //创建线程对象
  Thread t2=new Thread(c);
  t1.start();   //启动线程
  t2.start();


}


}
class Bank{
private int sum;//
//private Object obj=new Object();
public synchronized void add(int num){ //同步函数 ,存钱动作

     sum=sum+num;
 try {
 
Thread.sleep(10);    //调用Thread.sleep() 方法 
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println("总存了"+sum);
}
}
class Cus implements Runnable
{
private Bank b=new Bank();


@Override
public void run() {
for(int i=0;i<3;i++){

b.add(100);
}

}
 
}
同步函数:1.函数需要被对象调用,函数都有一个所属对象的引用,就是this
          2.同步函数使用的对象锁是this
          3 当同步函数被static 静态方法修饰,对象锁不再是this
原因:(重要)
静态进内存没有本类对象,但该类有字节码对象,不存在this对象,
类名.class  该对象类型是class,格式synchronized (对象名.class)。


验证:使用两个线程卖票
一个线程同步代码块
一个线程同步函数
都在执行卖票动作




package com.study;


public class TickDemo {


/**
*多线程实现多个窗口卖票
*/
public static void main(String[] args) {

Ticket t=new Ticket(); //创建买票对象,使用其run方法

Thread t1=new Thread(t); //创建对个线程,传人t,实现对个窗口买票
Thread t2=new Thread(t);

t1.start();
//t.flag=false;  //主线程有执行权,瞬间执行完t2结束
try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
//t.flag=false; 
t2.start();





}


}
class Ticket implements Runnable{

    private int tick=100;//票数
     boolean flag=true;  //标记线程
    Object obj=new Object();
@Override
public void run() {
if(flag){
while(true){
synchronized (this) {  //执行同步代码块   (如果static修饰同步函数 对象为Ticket.class)
   if(tick>0){
    try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"....code。。。。:"+tick--);
}
show();
 }
}
}else{
while(true)
show();
}

}
public synchronized void show(){ //执行同步函数,this对象锁
if(tick>0){
    try {
Thread.sleep(10);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName()+"....show。。。:"+tick--);
}
}

}




多线程的单例模式
单例模式:保证一个类在内存中的对象唯一性,一个类就一个对象实例。
步骤
1,私有化构造函数; 
2,创建私有并静态的本类对象; 
3,定义公有并静态的方法,返回该对象。 


1.饿汉模式
类加载的时候就创建实例
class Single{
    private static Single(){} //1.私有构造函数,防止外部创建实例对象
    private static Single s=new Single();//2.创建静态私有本类对象
    
  
    
    public static Single getInstance(){ //3.定义共有静态方法提供外部访问,返回该对象
    
         return s
    }
}


懒汉模式:
优点:实例延迟加载。
用于多线程,有安全问题,同步函数比较低效,使用同步代码块,多重判断提高效率
class Single

  private Single(){}//私有构造函数,防止外部创建实例对象

private static Single s=null;//2.创建对象


public static Single getInstance(){ //3.提供方法
  if(s==null){  //双重判断提高效率
     synchronized(Single.class){  //静态对象锁不再是this,而是 类.class
     if(s==null)
      
 s=new Single();
}
 return s;
}
}
class SingleDemo
{
public static void main(String [] args){

   System.out.print("hell,world");
}
}


多线程同步锁:死锁
1.当两个线程相互调用 Thread.join ()
2.当两个线程使用嵌套的同步块,一个线程占用了另外一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。
通俗讲:同步中嵌套同步,A锁嵌套B、B锁又嵌套A 互相僵持不释放锁 就死锁啦。
package com.itheima.thread.demo;


public class LockDemo {


/**
* @param args
*/
public static void main(String[] args) {
     

Thread t1=new Thread(new TestLock(true)); 
Thread t2=new Thread(new TestLock(false));

t1.start();
t2.start();


}


}
class TestLock implements Runnable{
      private boolean flag; //定义一个标记
      
      TestLock(boolean flag){
     this.flag=flag;
      }
@Override
public void run() {

if(flag){
while(true){
synchronized(MyLock.locka){
System.out.println("这是锁a");
synchronized(MyLock.lockb){
   System.out.print("这是锁b");
}
}
}
}else{
while(true){
synchronized(MyLock.lockb){
System.out.println("这是锁b");
synchronized(MyLock.locka){
   System.out.print("这是锁a");
}
}
 }
}

}


}
class MyLock{
 static  Object locka=new Object();
 static  Object lockb=new Object();
}



线程间的通讯:线程间的通讯就是多个线程操作同一个资源,但是操作动作不同
package com.itheima.thread.demo;






public class Res {


/**
* @param 胡田
*/
String name;
String sex;
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 Input implements Runnable{
//Res rs=new Res();
private Res r;
Input(Res r){ //初始化动作传人要操作的内容
  this.r=r;
}
@Override
public void run() {

int x=0;
while(true){
synchronized(r) //对象锁
{
if(x==0){
r.name="小强";
r.sex="男";
}else{
r.name="小红";
r.sex="女";
}
x=(x+1)%2;
}
}
}
}


class Output implements Runnable{
//Res rs=new Res();
private Res r;
Output(Res r){ //初始化传人要操作的内容
  this.r=r;
}
@Override
public void run() {
System.out.println(r.name+"........"+r.sex);

}
}


Lock接口:
Lock接口    
 Lock 替代了synchronized 
 Condition 替代了 Object监视器方法


好处:将同步synchronized 替换成了 Lock
      将object中的wait notify notifyAll 替换成了 Condition对象
      该对象可以Lock锁进行获取。一个锁可以对应多个Condition对象
注意:释放锁的工作一定要执行

示例代码


private Lock lock=new ReentrantLock();
private Condition condition =lock.newCondition();


public void cet(String name ) throws 
{
   lock.lock();
   try
   {
      while(flag)
           contidition.await();
       this.name=name+"--"+count++;


       sop(Thread.currentThread().getName()+"...生产者..."+this.name)
       flag=true;
        condition.signalAll();


    }
    finally
    {
       lock.unlock();
         
   }
}


---多生产者多消费模式-----
注意:if 判断标记与notify()方法对应,只判断一次,会导致不改运行的线程运行起来
while 判断标记跟notifyAll()方法对应,解决获取执行权后是否要运行
package com.itheima.thread.demo;


public class ProductDemo {


/**
* @param args
*/
public static void main(String[] args) {
Resource res=new Resource();

Producter pd=new Producter(res);
Consumer cos=new Consumer(res);

Thread t1=new Thread(pd);
Thread t2=new Thread(cos);

t1.start();
t2.start();


}
}
class Resource{

private String name;
private int count=1;
private static boolean flag=false;
//生产
 public synchronized void set(String name){
 
  while(flag) //不用if,if判断标记与notify()方法对应,只判断一次,会导致不改运行的线程运行起来
  try {
this.wait();
} catch (Exception e) {
// TODO: handle exception
}
  this.name=name+"..."+count++; //烤鸭1、烤鸭2、烤鸭3
  
  System.out.print(Thread.currentThread().getName()+"......生产者"+this.name);
  
  flag=true;
  notifyAll(); //唤醒所有等待的线程
  
  
 
}
//消费
public synchronized  void Out (){
while(!flag)  //标记while 每次notify 都会判断一次
  try {
  wait();
} catch (Exception e) {
// TODO: handle exception
}
 
System.out.print(Thread.currentThread().getName()+"......消费者"+this.name);
flag=false;
notifyAll();
 
}
 
}


class Producter implements Runnable{
     private Resource res;
     Producter(Resource res){
    this.res=res;
     }
     
@Override
public void run() {
while(true){
  res.set("北京烤鸭");
}

}

}


class Consumer implements Runnable{
    private Resource res;
    
    Consumer(Resource res){
    this.res=res;
    }
    
@Override
public void run() {
while(true){
  res.Out();
}

}

}
总结:
等待唤醒机制:
notify:唤醒线程池中的一个线程,本线程唤醒自己没有意义,while判断标记+notify 会导致线程死锁的。。
notifyAll:解决本线程线程一定会唤醒对方线程的问题。
wait():让线程处于冻结等待状态,wait的线程会被存储到线程池中。。。
sleep:线程进入睡眠状态,并不释放锁
notify、wait、notifyAll 要对持有监视器对象(锁)的操作,所以用在同步中synchnized 中


wait和sleep的区别:
1.wait可以指定时间也可以不指定,sleep必须指定睡眠时间,sleep(10)
2.同步时wait线程等待释放执行权,释放锁,交给别的线程执行
   sleep 睡眠状态释放执行权,但并不释放锁。
   
为什么操作线程锁的方法在Object类中?
因为notify、wait、notifyAll 这些方法操作线程时,都必须标识他们所操作的线程所持有的锁
只有在同一个锁上的被等待的线程,才可以被同一个锁的notify线程唤醒,而锁是任意对象、
所以可以任意对象调用的方法定义在Object中


---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a 
href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! -------------------------------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值