多线程知识点

一、什么是线程 (多线程即在同一时间可以做多件事情)

            创建多线程有3种方式,分别是继承线程类、实现Runnable接口和匿名类

      理解进程(Processor)和线程(Thread)的区别

         进程:启动一个LOL.exe就叫一个进程,再启动一个就叫两个进程

         线程:线程是在进程内部同时做的事,比如在LOL里英雄互相击杀就是由多线程来实现的

       如下代码是两对英雄同时攻击的代码(继承线程类)

         思路:设计一个类KillThread继承Thread,并且重写run方法

                启动线程的方法:实例化一个KillThread对象并且调用其start方法

一、Hero
package charactor;
import java.io.Serializable;
public class Hero {
    public String name;
    public float hp;
    public int damage;
    public void attackHero(Hero h){
    	try{
    		Thread.sleep(1000);
    	}catch(InterruptedException e){
    		e.printStackTrace();
    	}
    	h.hp-=damage;
    	System.out.format("%sc正在攻击c%s ,%s 的血变成了 %.0f %n ",name,h.name,h.name,h.hp);
    	if(h.isDead()){
    		System.out.println(h.name+"死啦啦了");
    	}
    }
	public boolean isDead(){
		return 0>=hp?true:false;
	}
}

二、KillThread
package PraticeThread;
import charactor.Hero;
public class KillThread extends Thread{
     private Hero h1;
     private Hero h2;
     public KillThread(Hero h1,Hero h2){
    	 this.h1=h1;
    	 this.h2=h2;
     }
	public  void run(){
		while(!h2.isDead()){
			h1.attackHero(h2);
		}
	}

}

三、TestThread
package PraticeThread;
import charactor.Hero;
public class TestThread {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
        Hero snake=new Hero();
        snake.name="蛇女";snake.hp=700;snake.damage=60;
        
        Hero Tang=new Hero();
        Tang.name="唐三";Tang.hp=800;Tang.damage=70;
        
        Hero Xiao=new Hero();
        Xiao.name="萧炎";Xiao.hp=600;Xiao.damage=90;
        
        Hero tree=new Hero();
        tree.name="树神";tree.hp=1000;tree.damage=60;
        
        KillThread killThread1=new KillThread(snake,Tang);
        killThread1.start();
        KillThread killThread2=new KillThread(Xiao,tree);
        killThread2.start();
        
	}

}


  如下是两对英雄同时攻击的代码(实现Runnable接口)

   思路:创建battle类,实现Runnable接口

            启动的时候,首先创建一个battle对象,然后再根据该battle对象创建一个线程对象并启动

二、battle
package PraticeThread;
import charactor.Hero;
public class battle implements Runnable{
    private Hero h1;
    private Hero h2;
    public battle(Hero h1,Hero h2){
    	this.h1=h1;
    	this.h2=h2;
    }
    public void run(){
    	while(!h2.isDead()){
    		h1.attackHero(h2);
    	}
    }
}

三、TestThread
package PraticeThread;
import charactor.Hero;
public class TestThread {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
        Hero snake=new Hero();
        snake.name="蛇女";snake.hp=700;snake.damage=60;
        
        Hero Tang=new Hero();
        Tang.name="唐三";Tang.hp=800;Tang.damage=70;
        
        Hero Xiao=new Hero();
        Xiao.name="萧炎";Xiao.hp=600;Xiao.damage=90;
        
        Hero tree=new Hero();
        tree.name="树神";tree.hp=1000;tree.damage=60;
        
       battle battle1=new battle(snake,Tang);
       new Thread(battle1).start();
       battle battle2=new battle(Xiao,tree);
       new Thread(battle2).start();
	}

}

如下是两对英雄互相攻击的代码(匿名类)

    思路:使用匿名类,继承Thread,重写run方法,直接再run里面写业务代码

           匿名类的好处是可以很方便的访问外部的局部变量

二、TestThread
package PraticeThread;
import charactor.Hero;
public class TestThread {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
        Hero snake=new Hero();
        snake.name="蛇女";snake.hp=700;snake.damage=60;
        
        Hero Tang=new Hero();
        Tang.name="唐三";Tang.hp=800;Tang.damage=70;
        
        Hero Xiao=new Hero();
        Xiao.name="萧炎";Xiao.hp=600;Xiao.damage=90;
        
        Hero tree=new Hero();
        tree.name="树神";tree.hp=1000;tree.damage=60;
        
        Thread t1=new Thread(){
        	public void run(){
        		while(!Tang.isDead()){
        			snake.attackHero(Tang);
        		}
        	}
        };
        t1.start();
        Thread t2=new Thread(){
        	public void run(){
        		while(!tree.isDead()){
        			Xiao.attackHero(Xiao);
        		}
        	}
        };
        t2.start();
	}

}

二、常见的线程方法

关键字简介用法
sleep当前线程暂停Thread.sleep(1000)---暂停1000毫秒其他线程不影响
join加入到当前线程中

匿名方法举例子

Thread t=new Thread(){方法};

try{t.join();}catch(异常)

在主线程里加入该线程,该线程运行完毕其他线程才会继续

setPriority线程优先级

两个线程t1和t2,优先t1获得CPU资源

t1.setPriority(Thread.MAX_PRIORITY);

t2.setPriority(Thread.MIN_PRIORITY);

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

yield

临时暂停

Thread.yield();

两个线程t1和t2,t1临时暂停就使得t2可以占用

Thread t1=new Thread(){

   public void run(){

      while(!Tang.isDead()){

            Thread.yield();

            snake.attackHero(Tang);

    }

  }

}

setDaemo

守护线程:当一个进程里所有的线程都是守护线程时结束进程

就是说一家公司里生产部和销售部倒闭的话,后勤也就没意义了,这里的后勤就是守护线程

守护线程通常会被用来做日志,性能统计工作

线程名.setDaemo();

 三、同步(concurrency)问题-----多个线程同时修改一个数据的时候可能导致的问题

           解决的思路是:在一个线程访问期间其他线程不可以访问,因此引入synchronizedt同步                                      ,见如下代码

          synchronized表示当前线程独占对象someObject,其他线程试图占有someObject的时候            就会等待知道当前对象释放,someObject又叫同步对象,所有的对象都可以作为同步对象,          为了达到同步效果必须使用同一个同步对象

      释放同步对象的方法:synchronized块自然结束或者有异常抛出

Object someObject=new Object();

synchronized(someObject){
   //此处的代码只有占有了someObject后才能执行
    snake.recover();
}

      解决同步问题------三种办法

        假如现在两个线程,一个recover,另一个hurt,snake是对象

  1、使用synchronized----实例化

final Object someObject=new Object();

synchronized(somrObject){
    snake.recover();
}

  2、使用hero对象作为同步对象----省了实例化

synchronized(snake){
     snake.recover();
}

 3、在方法前加入修饰符synchronized----直接在Hero类中方法recover前加上synchronized

pubic synchronized void recover(){
     hp=hp+1;
}

四、线程安全的类---面试题?

   1、HashMap和HashTable的区别

     共同点:两者都实现了Map接口,都是键值对保存数据的方式

     区别:HashMap可以存放null,HashTable不能存放null

               HashMap不是线程安全的类,HashTable是线程安全的类

2、StringBuffer和StringBuilder的区别

    Buffer是线程安全的,Builder是非线程安全的

   所以说如果是单线程用StringBuilder会更快,如果是多线程就用StringBuffer保证数据的安全性

3、ArrayList和Vector的区别

     Vector是线程安全的类,ArrayList是非线程安全的类

 不过这里可以通过工具Collections把ArrayList转化为线程安全,见如下代码

List<Integer> t1=new ArrayList<>();
List<Integer> t2=Collections.synchronizedList(t1);

五、死锁-----当业务比较复杂,多线程应用里有可能会发生死锁

         理解:1. 线程1 首先占有对象1,接着试图占有对象2
                    2. 线程2 首先占有对象2,接着试图占有对象1
                    3. 线程1 等待线程2释放对象2; 与此同时,线程2等待线程1释放对象1

                      总结:互相等待无人迈出第一步就只能死等下去咯

       代码理解:同步里面套同步

package PraticeThread;
import charactor.Hero;
public class deadLock {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
         final Hero s=new Hero();
         s.name="美杜莎";
         final Hero t=new Hero();
		 t.name="唐山";
		 Thread a1=new Thread(){
			 public void run(){
				 //占有美杜莎
				 synchronized(s){
					 System.out.println("a1线程已占用美杜莎");
					 try{
						 Thread.sleep(1000);//停顿1000毫秒这样a2线程有足够的时间占有唐山
					 }catch(InterruptedException e){
						 e.printStackTrace();
					 }
					 System.out.println("a1线程试图占有唐山。。。。");
					 synchronized(t){
						 System.out.println("do something");
					 }
				 }
			 }
		 };
		 a1.start();
		 Thread t2=new Thread(){
			 public void run(){
				 //占有唐山
				 synchronized(t){
					 System.out.println("a2已占用唐山");
					 try{Thread.sleep(1000);
					 }catch(InterruptedException e){
						 e.printStackTrace();
					 }
					 System.out.println("a2试图占有美杜莎。。。。");
					 synchronized(s){
						 System.out.println("do something");
					 }
				 }
			 }
		 };
		 t2.start();
	}
}

六、交互-----线程之间有交互通知的需求,例如有两个线程同时处理同一个英雄,一个加血一个减                              血,当减血线程发现血量=1时就停止减血,直到加血线程加了血才继续减血

      总结:利用wait()方法临时释放线程并且等待和notify()方法通知一个其他线程可以继续工作的                    原理,notifyAll()的意思是通知所有等待线程可以工作了

             再补充一个知识点:while(true)的作用是一个无限循环

                                        在内部用break或return退出循环,否则一直循环

一、
package charactor;

public class Hero {
    public String name;
    public float hp;
    public int damage;
    public synchronized void hurt(){
    	if(hp==1){
    		//让占有this减血的线程暂时施放,并等待
    		try{this.wait();
    		}catch(InterruptedException e){
    		e.printStackTrace();
    		}
    	}
    	hp-=1;
    	System.out.printf("%s 减血后的血量是: %.0f%n",name,hp );
    }
    public synchronized void recover(){
    	hp+=1;
    	System.out.printf("%s 加血后的血量: %.0f%n",name,hp);
    	this.notify();
    }
    
}

二、
package PraticeThread;
import charactor.Hero;
public class jiaoHu {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
         final Hero snake=new Hero();
         snake.name="美杜莎";
         snake.hp=66;
         Thread a1=new Thread(){
        	public void run(){
        		while(true){
        			snake.hurt();
        			try{Thread.sleep(10);
        			}catch(InterruptedException e){
        				e.printStackTrace();
        			}
        		}
        	}
         };
         Thread a2=new Thread(){
        	 public void run(){
        		 while(true){
        			 snake.recover();
        			 try{Thread.sleep(100);
        			 }catch(InterruptedException e){
        			    e.printStackTrace();
        			 }	 
        		 }
        	 }
         };
         a1.start();
         a2.start();
	}
}

七、线程池

     设计思路(类似生产者消费者模型)

1准备一个任务容器
2一次性启动10个消费者线程,刚开始所有线程都在wait在上面
3直到外部线程往任务容器里扔了一个任务,就会有一个线程被唤醒notify,这个线程取出任务并且开始执行,执行完毕后继续等待下一次任务的到来
4

 如果短时间内有较多的任务加入,那么就会有多个线程被唤醒notify然后去执行任务。整个过程都不需要创建新的线程而且循环存在的线程

自定义线程池没看懂,就用java自带的线程池吧,如下代码

注释:
  第一个参数10表示初始化10个线程在里面工作
  第二个参数15表示若是10个不够就会自动增加到最多15个
  第三个参数60的意思是结合第四个参数TimeUnit.SECONDS,表示等待60秒,多出来的线程还没有接到活的话 
       就会回收,最少10个
  第五个参数new LinkedBlockingQueue()是用来放任务的集合
package charactor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;;
public class ThreadPoll {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
        ThreadPoolExecutor thredPool=new ThreadPoolExecutor(10,15,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
        thredPool.execute(new Runnable(){
        	public void run(){
        		System.out.println("任务1");
        	}
        });
	}
}

八、Lock对象---与synchronize类似也能达到同步的效果,但是需要手动释放

    --Lock是一个接口,为了使用一个Lock对象,需要用到如下方法:

Lock lock=new ReentrantLock();
package PraticeThread;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;;
public class lock {
	 public static String now(){
     	return new SimpleDateFormat("HH:mm:ss").format(new Date());
     }
	 public static void log(String msg){
		 System.out.printf("%s %s %s %n", now(),Thread.currentThread().getName(),msg);
	 }
	public static void main(String[] args) {
		// TODO Auto-generated method stub
       Lock lock=new ReentrantLock();
       Thread t1=new Thread(){
    	   public void run(){
    		   try{
    			   log("线程启动");
    			   log("正在试图占有对象lock");
    			   lock.lock();
    			   log("占有对象lock中");
    			   log("进行3秒的业务操作");
    			   Thread.sleep(3000);
    		   }catch(InterruptedException e){
    			   e.printStackTrace();
    		   }finally{
    			   log("释放对象lock");
    			   lock.unlock();
    		   }
    		   log("线程结束");
    	   }
       };
        t1.setName("t1");
        t1.start();
	}
}

之前synchronize方式进行线程交互,用到的是wait,notify,notifyAll方法;

Lock也提供了类似解决办法:

 首先通过lock对象得到一个Condition对象,然后调用Condition对象方法:await,signal,signalAll

1---import java.util.concurrent.locks.ReentrantLock;

  ---import java.util.concurrent.locks.Lock;

  ---import java.util.concurrent.locks.Condition;

2---Lock lock =new ReentrantLock();

 ---Condition condition=lock.newCondition();

见如下代码:

package PraticeThread;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.Condition;
public class lockJh {
    public static void ac(String mm,String nn){
          	System.out.printf("%s 变成了 %s%n",mm,nn);
    }
	public static void main(String[] args) {
		// TODO Auto-generated method stub
        Lock lock=new ReentrantLock();
        Condition condition =lock.newCondition();
        Thread t1=new Thread(){
        	public void run(){
        		try{
        			ac("赵晓雪","大尖牛");
        			ac("赵晓雪","屎壳郎");
        			lock.lock();//占有
        			condition.await();//临时暂停等待
        			Thread.sleep(3000);
        		}catch(InterruptedException e){
        			e.printStackTrace();
        		}finally{
        			lock.unlock();//手动释放
        		}
        	}
        };
        t1.setName("tt1");//线程1起名
        Thread t2=new Thread(){
        	public void run(){
        		try{
        		ac("赵晓雪","猴子");
        		ac("赵晓雪","天风元帅");
        		lock.lock();//占有
        		Thread.sleep(2000);
        		condition.signal();//释放其他等待线程
        	}catch(InterruptedException e){
        		e.printStackTrace();
        	}finally{
        		lock.unlock();//唤醒其他线程
        	}
        	}
        };
        t2.setName("tt2");
        t1.start();
        t2.start();
	}
}

九、原子访问---不可中断的操作,比如int i=5,也线程安全的

JDK6以后,新增加了一个包java.util.concurrent.atomic,里面有各种原子类,比如AtomicInteger。

   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值