多线程的学习

     声明:观看尚硅谷李贺飞的java juc的视频笔记。

1、内存可见性问题:
     多个线程在操作共享数据时,彼此不可见。

     volatile使得多线程中彼此的操作可见。跟同步来说,它是一种轻量级的同步策略。

     但是volatile不具备互斥性,不能保证变量的原子性。

     以下例子:
 

public class TestVolatile {
   public static void main(String[] args) {
	     MyThread mt = new MyThread();
	     new Thread(mt).start();
	     
	     while(true) {
	    	 if(mt.isFlag()) {
	    		 System.out.println("****");
	    		 break;
	    	 }
	     }
   }
}

class MyThread implements Runnable{
    private boolean flag=false;  
	public boolean isFlag() {
		return flag;
	}

	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	@Override
	public void run() {
		    try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
      		flag = true;
      		System.out.println("flag:"+isFlag());
	}	
}
通过以上代码,我测试了好几次,当子线程中的睡眠时间去掉的话,出现正常执行的概率要大点,如果是睡眠的话,几乎100%都是无限循环。

如果加了volatile,就不会出现这个问题。


  2、原子变量:

        1)volatile保证内存可见性

         2)CAS算法保证数据的原子性(硬件对于并发操作共享数据的支持)

               内存值 v

               预估值 A

               更新值 B

               当V=A时,V=B,否则不做处理。

     例子:i++的操作有三步:读-操作-写回

    

public class TestAutomic {
   public static void main(String[] args) {
	   MyThread1 mt = new MyThread1();
	   for(int i=0;i<10;i++) {
		   new Thread(mt).start();
	   }
   }
}

class MyThread1 implements Runnable{
        public int i;
	@Override
	public void run() {
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(i++);
	}
	
}


这里其实不管i是不是用volatile修饰,都会出现线程的问题,因为volatile是保证内存可见性的问题,并不保证原子操作。
使用原子类型可以解决这个问题:

public class TestAutomic {
   public static void main(String[] args) {
	   MyThread1 mt = new MyThread1();
	   for(int i=0;i<10;i++) {
		   new Thread(mt).start();
	   }
   }
}

class MyThread1 implements Runnable{
    public AtomicInteger i = new AtomicInteger(0);
	@Override
	public void run() {
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(i.getAndIncrement());
	}
	
}

3、ConcurrentHashMap的使用:

     采用锁分段机制:每个段都是独立的锁。16个段,每个段一个表,表存链表数据。
     

4、对几个集合类的使用

public class TestCopyAndWrite {
   public static void main(String[] args) {
	  MyThread3 mt = new MyThread3();
	  for(int i=0;i<10;i++) {
		  new Thread(mt).start();
	  }
   }
}

class MyThread3 implements Runnable{
    private static List<String> list = Collections.synchronizedList(new ArrayList<>());
    
	static {
		list.add("AA");
	}
    @Override
	public void run() {
		Iterator<String> it = list.iterator();
		while(it.hasNext()) {
			System.out.println(it.next());
			list.add("BB");
		}
	}
	
}

运行上述的代码会出现:java.util.ConcurrentModificationException的异常。

 private static List<String> list = Collections.synchronizedList(new ArrayList<>());

的一行改成:
private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
便可以解决这个异常。

注意:如果是比较多写入操作的list,不建议使用CopyOnwriteArrayList,因为每次写都要复制一份,效率比较低。

          CopyOnwriteArrayList比较适合迭代比较多的情况。

5、CountDownLatch的使用(闭锁)

public class TestCountdown {
   //开始4个人,来一个减一,为0时,继续执行。
   private static  CountDownLatch latch = new CountDownLatch(4);
   public static void main(String[] args) throws InterruptedException {
	  //四个人相约爬山,全部人齐了才开始
	  TestCountdown tc = new TestCountdown();
	  //因为到达目的地是并发的过程,这里开启四个线程  
	  //我的线程
	  new Thread(new Runnable() {
		@Override
		public void run() {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			tc.meArrive();
			latch.countDown();
		}
	  }).start();
	  
	  //严的线程
	  new Thread(new Runnable() {
			@Override
			public void run() {
				tc.yanArrive();
				latch.countDown();
			}
		  }).start();
	  //俊金的线程
	  new Thread(new Runnable() {
			@Override
			public void run() {
				tc.junjinArrive();
				latch.countDown();
			}
		  }).start();
	  //仲源的线程
	  new Thread(new Runnable() {
			@Override
			public void run() {
				tc.zhongyanArrive();
				latch.countDown();
			}
		  }).start();
	  //人数不齐,继续等
	  latch.await();
	  //开始爬山
	  tc.climb();
   }
   
   public void meArrive() {
	   System.out.println("我到了。。。");
   }
   
   public void yanArrive() {
	   System.out.println("严到了。。。");
   }
   
   public void junjinArrive() {
	   System.out.println("俊金到了。。。");
   }
   
   public void zhongyanArrive() {
	   System.out.println("仲源到了。。。");
   }
   
   public void climb() {
	   System.out.println("开始爬山。。。");
   }
}
5、多线程的第三种方式:

实现Callable接口,有返回值和抛出异常。需求FutureTask的支持。用于接受返回接口。FutureTask也可用于闭锁。

例子:

public class TestCallable {    
        public static void main(String[] args) throws InterruptedException, ExecutionException {
        	   MyThread4 mt = new MyThread4();              
               FutureTask<Integer> ft = new FutureTask<>(mt);
               new Thread(ft).start();
               System.out.println(Thread.currentThread().getName()+':');
               //打印返回结果
               Integer in = ft.get();
               System.out.println(in);
		}
}
class MyThread4 implements Callable<Integer>{
	@Override
	public Integer call() throws Exception {
		int temp = 0;
        for(int i=0;i<100;i++) {
        	temp += i;
        }
        System.out.println(Thread.currentThread().getName()+':');
		return temp;
	}	
}
5、解决多线程安全问题的方法:

1)同步代码快

2)同步方法

3)同步锁(jdk 5.0后)(锁的释放问题,一旦没放,会出问题,所以建议放在finally中)

例子:
public class TestLock {
   public static void main(String[] args) {
	   MyThread5 mt = new MyThread5();
	   new Thread(mt,"窗口1").start();
	   new Thread(mt,"窗口2").start();
	   new Thread(mt,"窗口3").start();
  }
}
class MyThread5 implements Runnable{
    //卖一百张票
    private int tickets=100;
    //使用同步锁
    private Lock lock = new ReentrantLock();
	@Override
	public void run() {
		lock.lock();//加锁
	    try {
	    	while(tickets>0) {	 		
	    			try {
						Thread.sleep(200);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"余票为:"+--tickets);  		
		}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			lock.unlock();
		}
	}	
}

6、生产者和消费者案例:

1)这部分代码有个问题是,当产品缺货或者产品已满时,都会继续执行相应的操作,比如产品缺货了,还在一直卖,所以

打印多个缺货。这个是不太合理的,缺货后,应该等着,有货了才执行卖的操作。

public class TestCosumerProduct {
    public static void main(String[] args) {
		Clerk clerk  = new Clerk();
		Productor pd = new Productor(clerk);
		Consumer  cs = new Consumer(clerk);		
		new Thread(pd,"生产者").start();
		new Thread(cs,"消费者").start();
	}
}
//店员
class Clerk{
	private int product=0;	
	//进货
	public synchronized void get() {
		if(product>=10) {
			System.out.println("产品已满!");
		}else {
			System.out.println(Thread.currentThread().getName()+" : "+ ++product);
		}
	}
	//出货
	public synchronized void sale() {
		if(product<=0) {
			System.out.println("缺货!");
		}else {
			System.out.println(Thread.currentThread().getName()+" : "+ --product);
		}
	}
}

class Productor implements Runnable{
    private Clerk clerk;    
	public Productor(Clerk clerk) {
		super();
		this.clerk = clerk;
	}
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.get();
		}
	}
}
class Consumer  implements Runnable{
	private Clerk clerk;	
	public Consumer(Clerk clerk) {
		super();
		this.clerk = clerk;
	}
	@Override
	public void run() {
		for(int i = 0;  i < 20; i++) {
			clerk.sale();
		}
	}
}
2)采用等待唤醒机制来处理上述问题:这里确实是解决了上述的问题,但是也有问题:通过3)中演示。
public class TestCosumerProduct {
    public static void main(String[] args) {
		Clerk clerk  = new Clerk();
		Productor pd = new Productor(clerk);
		Consumer  cs = new Consumer(clerk);
		
		new Thread(pd,"生产者").start();
		new Thread(cs,"消费者").start();
	}
}
//店员
class Clerk{
	private int product=0;	
	//进货
	public synchronized void get() {
		if(product>=10) {
			System.out.println("产品已满!");
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}else {
			System.out.println(Thread.currentThread().getName()+" : "+ ++product);
			this.notifyAll();
		}
	}	
	//出货
	public synchronized void sale() {
		if(product<=0) {
			System.out.println("缺货!");
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}else {
			System.out.println(Thread.currentThread().getName()+" : "+ --product);
                        this.notifyAll();
		}
	}
}
class Productor implements Runnable{
    private Clerk clerk;    
	public Productor(Clerk clerk) {
		super();
		this.clerk = clerk;
	}
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.get();
		}
	}
}
class Consumer  implements Runnable{
	private Clerk clerk;
	
	public Consumer(Clerk clerk) {
		super();
		this.clerk = clerk;
	}
	@Override
	public void run() {
		for(int i = 0;  i < 20; i++) {
			clerk.sale();
		}
	}	
}

3)此时会发现循环次数执行完后,代码还是没有停止,因为在产品已满或者缺货中一直等待着。

     解决方式:将else的部分去掉即可(其实也有问题,因为两个的循环次数是一样的,所以没有,如果不一样,也会有)

public class TestCosumerProduct {
    public static void main(String[] args) {
		Clerk clerk  = new Clerk();
		Productor pd = new Productor(clerk);
		Consumer  cs = new Consumer(clerk);
		
		new Thread(pd,"生产者").start();
		new Thread(cs,"消费者").start();
	}
}
//店员
class Clerk{
	private int product=0;
	
	//进货
	public synchronized void get() {
		if(product>=1) {
			System.out.println("产品已满!");
			try {
				Thread.sleep(200);
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}else {
			System.out.println(Thread.currentThread().getName()+" : "+ ++product);
			this.notifyAll();
		}
	}	
	//出货
	public synchronized void sale() {
		if(product<=0) {
			System.out.println("缺货!");
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}else {
			System.out.println(Thread.currentThread().getName()+" : "+ --product);
            this.notifyAll();
		}
	}
}
class Productor implements Runnable{
    private Clerk clerk;
    
	public Productor(Clerk clerk) {
		super();
		this.clerk = clerk;
	}
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.get();
		}
	}
}

class Consumer  implements Runnable{
	private Clerk clerk;
	
	public Consumer(Clerk clerk) {
		super();
		this.clerk = clerk;
	}

	@Override
	public void run() {
		for(int i = 0;  i < 20; i++) {
			clerk.sale();
		}
	}
	
}

4)增加线程,一共有4个线程,wait用while进行循环,如果是if的话,存在虚假唤醒的问题,所以建议wait的方法总是在条件的

循环中比较好。

public class TestCosumerProduct {
    public static void main(String[] args) {
		Clerk clerk  = new Clerk();
		Productor pd = new Productor(clerk);
		Consumer  cs = new Consumer(clerk);
		
		new Thread(pd,"生产者1").start();
		new Thread(cs,"消费者1").start();
		
		new Thread(pd,"生产者2").start();
		new Thread(cs,"消费者2").start();
	}
}

//店员
class Clerk{
	private int product=0;	
	//进货
	public synchronized void get() {
		while(product>=1) {
			System.out.println("产品已满!");
		    try {
				Thread.sleep(200);
			} catch (InterruptedException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			try {
				//虚假唤醒
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
			System.out.println(Thread.currentThread().getName()+" : "+ ++product);
			this.notifyAll();
		
	}	
	//出货
	public synchronized void sale() {
		while(product<=0) {
			System.out.println("缺货!");
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
			System.out.println(Thread.currentThread().getName()+" : "+ --product);
            this.notifyAll();
		
	}
}
class Productor implements Runnable{
    private Clerk clerk;
    
	public Productor(Clerk clerk) {
		super();
		this.clerk = clerk;
	}
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.get();
		}
	}
}
class Consumer  implements Runnable{
	private Clerk clerk;	
	public Consumer(Clerk clerk) {
		super();
		this.clerk = clerk;
	}
	@Override
	public void run() {
		for(int i = 0;  i < 20; i++) {
			clerk.sale();
		}
	}
	
}

5)使用同步锁:

public class TestCosumerProduct2 {
    public static void main(String[] args) {
		Clerk2 clerk  = new Clerk2();
		Productor2 pd = new Productor2(clerk);
		Consumer2  cs = new Consumer2(clerk);
		
		new Thread(pd,"生产者1").start();
		new Thread(cs,"消费者2").start();
		
		new Thread(pd,"生产者3").start();
		new Thread(cs,"消费者4").start();
	}
}
//店员
class Clerk2{
	private int product=0;
	//使用同步锁
	private Lock lock = new ReentrantLock();
	private Condition con = lock.newCondition();
	//进货
	public  void get() {
		lock.lock();
		try {
			while(product>=1) {
				System.out.println("产品已满!");
			    try {
					Thread.sleep(200);
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				try {
					//虚假唤醒
					con.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
				System.out.println(Thread.currentThread().getName()+" : "+ ++product);
				con.signalAll();
		} finally {
			lock.unlock();
		}	
	}
	
	//出货
	public synchronized void sale() {
		lock.lock();
		try {
			while(product<=0) {
				System.out.println("缺货!");
				try {
					con.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
				System.out.println(Thread.currentThread().getName()+" : "+ --product);
	            con.signal();
		} finally {
			lock.unlock();
		}	
	}
}

class Productor2 implements Runnable{
    private Clerk2 clerk;
    
	public Productor2(Clerk2 clerk) {
		super();
		this.clerk = clerk;
	}
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			clerk.get();
		}
	}
}
class Consumer2  implements Runnable{
	private Clerk2 clerk;
	
	public Consumer2(Clerk2 clerk) {
		super();
		this.clerk = clerk;
	}
	@Override
	public void run() {
		for(int i = 0;  i < 20; i++) {
			clerk.sale();
		}
	}
	
}
7、交替打印的实现:
public class TestLoopPrint {
   public static void main(String[] args) {
	 Loop loop = new Loop();
	 new Thread(new Runnable() {	
		@Override
		public void run() {
			for (int i = 0; i < 3; i++) {
				loop.loopA(i);
			}
		}
	 },"A").start();
	 
	 new Thread(new Runnable() {	
			@Override
			public void run() {
				for (int i = 0; i < 3; i++) {
					loop.loopB(i);
				}
			}
		 },"B").start();
	 
	 new Thread(new Runnable() {	
			@Override
			public void run() {
				for (int i = 0; i < 3; i++) {
					loop.loopC(i);
				}
			}
		 },"C").start();
  }
}
class Loop{
	private int flag = 1;
	private Lock lock = new ReentrantLock();
	private Condition condition1 = lock.newCondition();
	private Condition condition2 = lock.newCondition();
	private Condition condition3 = lock.newCondition();	
	public void loopA(int totalLoop) {
		try {
			lock.lock();
			//当flag不为1时,等待
			if(flag != 1) {
				try {
					condition1.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			//打印结果
			for(int i=0;i<=3;i++) {
				System.out.println(Thread.currentThread().getName()+"->"+totalLoop);
			}	
			//换新B
			flag = 2;
			condition2.signal();
		} finally {
			lock.unlock();
		}
		
	}	
	public void loopB(int totalLoop) {
		try {
			lock.lock();
			//当flag不为1时,等待
			if(flag != 2) {
				try {
					condition2.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			//打印结果
			for(int i=0;i<=3;i++) {
				System.out.println(Thread.currentThread().getName()+"->"+totalLoop);
			}		
			//换新c
			flag = 3;
			condition3.signal();
		} finally {
			lock.unlock();
		}
		
	}
	public void loopC(int totalLoop) {
		try {
			lock.lock();
			//当flag不为1时,等待
			if(flag != 3) {
				try {
					condition3.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			//打印结果
			for(int i=0;i<=3;i++) {
				System.out.println(Thread.currentThread().getName()+"->"+totalLoop);
			}		
			//换新A
			flag = 1;
			condition1.signal();
		} finally {
			lock.unlock();
		}
		
	}
}

8、读写锁:ReadWritelock

      读写和写写要互斥,读读不用互斥。

public class TestReadWriteLock {  
    public static void main(String[] args) {
    	ReadWriteLockDemo rwld = new ReadWriteLockDemo();
    	for(int i=0;i<100;i++) {
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				rwld.getNumber();
    			}
    		}).start();
    	}	
    	new Thread(new Runnable() {
			@Override
			public void run() {
				rwld.setNumber(12);
			}
		}).start();
	
	}
}
class ReadWriteLockDemo{
	private int number;
    private ReadWriteLock rwl = new ReentrantReadWriteLock();
	public void getNumber() {
		rwl.readLock().lock();
		try {
			System.out.println(Thread.currentThread().getName()+"读"+number);
		} finally {
			rwl.readLock().unlock();
		}
	
	}
	public void setNumber(int number) {
		rwl.writeLock().lock();
		try {
			this.number = number;
			System.out.println(Thread.currentThread().getName()+":写");
		} finally {
		     rwl.writeLock().unlock();
		}		
	}		
}
9、线程8锁:

1)非静态方法的锁,默认为this,静态方法默认为当前的Class实例

2)某一个时刻只能有一个线程持有锁,无论几个方法。

10、线程池:(底层提供一个线程队列)

为什么要用线程池:如果频繁的创建和销毁线程,性能会有所下降。

线程池的体系结构:Executor 负责线程的使用与调度的根接口

                            ExecutorService:子接口:线程池的主要接口    

                                  A ThreadPoolExecutor:线程池的主要实现类

                                  B ScheduledExecutorService:子接口:负责线程的调度

                                           ScheduledThreadPoolExecutor:继承了A实现了B

工具类:Executors

 newFixedThreadPool :创建固定的线程池

 newCachedThreadPool :缓存线程池,数量不固定,可以根据需求自动的更改数量

 newSingleThreadExecutor:创建单个线程,线程池只有一个线程。

 newScheduledThreadPool:创建固定大小的线程,可以延迟和定时执行任务。

例子:

public class TestPool {
	//创建线程池
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ExecutorService es = Executors.newFixedThreadPool(5);
		List<Future<Integer>> list = new ArrayList<>();
		for(int i=0;i<3;i++) {
			Future<Integer> fu = es.submit(new Callable<Integer>() {
				@Override
				public Integer call() throws Exception {
					int sum=0;
					for(int i=0;i<33;i++) {
						sum +=i;
					}
					return sum;
				}
			});
			
			list.add(fu);
		}			
		//关闭线程池
		es.shutdown();
		//获取返回的结果
		for (Future<Integer> future : list) {
			System.out.println(future.get());
		}
	}
}

例子2:

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//创建任务
		MyPoolThread mpt = new MyPoolThread();
		ExecutorService es = Executors.newFixedThreadPool(5);
		//三十个任务,由5个线程处理
		for(int i=0;i<30;i++) {
			es.submit(mpt);//提交三十次
		}	
		//关闭线程池
		es.shutdown();
		//获取返回的结果
	  }
class MyPoolThread implements Runnable{
	@Override
	public void run() {
        for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+" : "+i);
		}		
	}
}
线程调度:newScheduledThreadPool

public class TestScheduled {
  public static void main(String[] args) throws InterruptedException, ExecutionException {
	 //创建调度的线程池
	 ScheduledExecutorService es =  Executors.newScheduledThreadPool(1);
	 for(int i=0;i<5;i++) {
		Future<Integer> fu = es.schedule(new Callable<Integer>() {
			@Override
			public Integer call() throws Exception {
				int temp = new Random().nextInt(100);
				System.out.println(Thread.currentThread().getName()+" : "+temp);
				return temp;
			}
		}, 1, TimeUnit.SECONDS);
		System.out.println(fu.get());//调用了该方法才会有定时的效果。
	 }
	 
	 es.shutdown();
  }



猜你喜欢

转载自blog.csdn.net/chenjianhuideyueding/article/details/79380635