Java 多线程与并发(案例 + 应用)

Posted IT_Holmes

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 多线程与并发(案例 + 应用)相关的知识,希望对你有一定的参考价值。

文章目录

1. 传统 创建线程的两种方式


一种继承Thread,一种是实现Runnable两种方式。

问题:如果匿名类实现了Runnable又覆盖了Thread的run方法,会执行谁的run方法?

public static void main(String[] args) 
	new Thread(new Runnable() 

		public void run() 
			// TODO Auto-generated method stub
			System.out.println("Runnable:执行了runnable");
		
		
	) 
		@Override
		public void run() 
			// TODO Auto-generated method stub
			System.out.println("Thread:执行了run方法");
		
	.start();
	
	//因为后来重写的run()方法覆盖了。因此这里执行的是Thread的run方法。

2. 传统 定时器技术


定时器的使用:

public static void main(String[] args) throws IOException, InterruptedException 

	new Timer().schedule(new TimerTask() 
		@Override
		public void run() 

			System.out.println("炸弹定时器1~~~~");
			new Timer().schedule(new TimerTask() 
				@Override
				public void run() 
					System.out.println("炸弹定时器2~~~");
				
			,2000);

			//如果我们this承接当前类的timerTask就会报错!!
			new Timer().schedule(this,2000);
		
	,2000);


为了解决上面的问题,我们可以通过创建对象的形式来解决:

class MyTimerTask extends TimerTask
	@Override
	public void run() 
		//我们可以直接new一个对象来重复调用就可以了。
		new Timer().schedule(new MyTimerTask(),2000);
	

3. 传统线程 互斥技术


synchronized 同步锁:

  • 同步监听器,就是要上锁的对象!!一般设置为this。

对于同步监听器的理解,就是类似一种锁机制。多个线程调用的时候,经过synchronized同步锁,先是查看同步监听器有没有被上锁抢占。如果有,就等待;如果没有,就抢占。(重点!!!)

也就说同步监听器是多个线程共有的一个东西,自然this就是最好的选择。this就是指的就是当前对象。

如果同步监听器设置为了对象的class,给class字节码对象上了锁,那么就算是多个对象情况下,也会被同步锁阻塞。因为,对象.class文件被上锁了,本身就没法使用class文件调用了。

import com.itholmes.config.User;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scheduling.support.CronTrigger;
import sun.applet.Main;

import java.io.*;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Callable;

public class MainTest 
	public static void main(String[] args) throws IOException, InterruptedException 
		new MainTest().init();
	

	private void init()

		//这里因为Outputer是内部类,不能直接再static方法里面显示,因此我们就可以通过创建一个方法,通过对象来调用。
		Outputer outputer = new Outputer();

		new Thread(new Runnable() 
			@Override
			public void run() 
				while (true)
					try 
						Thread.sleep(10);
					 catch (InterruptedException e) 
						e.printStackTrace();
					
					outputer.output("123456");

					// 这样就不可以了!!!就算有多个线程都是调用,他们都不是用的同一个outpuer对象。
					// 也就是多线程需要加锁的地方就是多个线程同时操作一个对象或者一部分代码的时候的情况。
					//new Outputer().output("123123");
				
			
		).start();

		new Thread(new Runnable() 
			@Override
			public void run() 
				while (true)
					try 
						Thread.sleep(10);
					 catch (InterruptedException e) 
						e.printStackTrace();
					
					outputer.output("2344");
				
			
		).start();

	

	//了解静态内部类 和 普通内部类一些区别:https://blog.csdn.net/qq_37768971/article/details/101166164
	static class Outputer

		//方法synchronized的同步监听器是this,这里的this指向的是当前output对象。
		public synchronized void output(String name)

			int len = name.length();

			for (int i=0;i<len;i++)
				System.out.print(name.charAt(i));
			
			System.out.println();

		

		//同样,方法synchronized的同步监听器是this,然而这里的this指向的是OutPuter.class。
		public static synchronized void output2(String name)

			int len = name.length();

			for (int i=0;i<len;i++)
				System.out.print(name.charAt(i));
			
			System.out.println();

		

	


4. 传统线程 同步通信技术


解决一个问题:

public class ThreadTest 

    public static void main(String[] args) 

        /**
         *  对于同步的多线程:
         *      第一点:一般都是通过一个类,就像下面的business对象一样,我们直接将同步锁 和 wait ,notify等封装到对象方法里面。
         *      这样无论是同步技术实现,还是同步监听器都好设置。
         *
         *      第二点:同步锁要写到类中,不要写到线程的run方法中。
         *
         *      第三点:一定用while,不要用if。
         */
        Business business = new Business();

        new Thread(new Runnable() 
            @Override
            public void run() 
                for (int j = 0;j<50;j++)
                    business.sub(j);
                
            
        ).start();

        for (int j = 0;j<50;j++)
            business.main(j);
        

    



class Business

    private Boolean bShouldSub = true;

    public synchronized void sub(int j)

        //用while,不要用if , while有循环效果!if有可能伪唤醒。
        //if (!bShouldSub)
        while (!bShouldSub)
            try 
                this.wait();
             catch (InterruptedException e) 
                e.printStackTrace();
            
        
        for (int i=0;i<10;i++)
            System.out.println("50循环的第"+j+",子线程第"+i+"次循环。");
        
        bShouldSub = false;
        this.notifyAll();
    
    public synchronized void main(int j)

        //用while,不要用if , while有循环效果!if有可能伪唤醒。
        while (bShouldSub)
            try 
                this.wait();
             catch (InterruptedException e) 
                e.printStackTrace();
            
        

        for (int i = 0; i < 100; i++) 
            System.out.println("100循环的第" + j + ",主线程第" + i + "次循环。");
        
        bShouldSub = true;
        this.notifyAll();
    

重点:

  • 第一点:一般都是通过一个类,就像下面的business对象一样我们直接将同步锁 和 wait ,notify等封装到对象方法里面。这样无论是同步技术实现,还是同步监听器都好设置。
  • 第二点:同步锁要写到类中,不要写到线程的run方法中。
  • 第三点:一定用while,不要用if。用if就有可能伪唤醒。

5. 线程范围内 共享变量 概念和作用


理解下面的data 和 map:

  • 也就是通过map存thread的方式来达到共享变量。
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class ThreadScopeShareData 

    //private static int data = 0; 如果有这种static成员变量就容易出现共享混淆的一个问题。

    //这样我们就可以通过Thread + xxx 的方法来实现,这也就是数据库池经常用到的一种方式。
    private static Map<Thread,Integer> threadData = new HashMap<>();

    public static void main(String[] args) 
        for (int i=0;i<2;i++)
            new Thread(new Runnable() 
                @Override
                public void run() 
                    //让data随机等于一个int类型数据。
                    int data = new Random().nextInt();
                    
                    System.out.println(Thread.currentThread().getName()
                            + "has put data: " + data);

                    threadData.put(Thread.currentThread(),data);
                    new A().get();
                    new B().get();
                
            ).start();
        
    

    static class A 
        public void get()
            Integer data = threadData.get(Thread.currentThread());
            System.out.println("A from" + Thread.currentThread().getName() + " get data: "+data);
        
    

    static class B
        public void get()
            Integer data = threadData.get(Thread.currentThread());
            System.out.println("B from" + Thread.currentThread().getName() + " get data: "+data);
        
    


6. ThreadLocal类及应用技巧


ThreadLocal使用起来,就是多线程使用的时候,每个线程都会操作自己线程对应的那个值。

ThreadLocal原理:

  • 就是key + value 的map,key是各自的线程对象本身。每次get获取就要通过线程对象来获取(注意:并不是将线程对象最为参数传递)。这就是一种容器效果。
  • 由于每一条线程均含有各自私有的ThreadLocalMap容器,这些容器相互独立互不影响,因此不会存在线程安全性问题,从而也无需使用同步机制来保证多条线程访问容器的互斥性。

ThreadLocal.clear()方法可以清空,释放内存。

(remove方法可以拿掉当前线程的内容。)


import java.util.Random;

public class ThreadScopeShareData 

    /**
     * ThreadLocal类,多线程之间是不会相互冲突的。
     * 这里是有泛型的,不仅仅用于integer。
     */
    static ThreadLocal<Integer> x = new ThreadLocal();
    static ThreadLocal<MyThreadScopeData> myThreadScopeData = new ThreadLocal<>();

    public static void main(String[] args) 
        for (int i=0;i<2;i++)

            new Thread(new Runnable() 
                @Override
                public void run() 
                    //让data随机等于一个int类型数据。
                    int data = new Random().nextInt();
                    
                    System.out.println(Thread.currentThread().getName()
                            + "has put data: " + data);

                    //这里的是当前线程内定义的data。
                    x.set(data);

                   MyThreadScopeData.getInstance().setName("name"+data);
                   MyThreadScopeData.getInstance().setAge(data);

                    new A().get();
                    new B().get();
                

            ).start();
        
    

    static class A 
        public void get()
            //这里获取就是当前线程获取的数据。
            Integer data = x.get();
            System.out.println("A from" + Thread.currentThread().getName() + " get data: "+data);

            MyThreadScopeData myData = MyThreadScopeData.getInstance();
            System.out.println("A from " + Thread.currentThread().getName() + "getMyData: " + myData.getName() + ","
            + myData.getAge());
        
    

    static class B
        public void get()
            Integer data = x.get();
            System.out.println("B from" + Thread.currentThread().getName() + " get data: "+data);

            MyThreadScopeData myData = MyThreadScopeData.getInstance();
            System.out.println("B from " + Thread.currentThread().getName() + "getMyData: " + myData.getName() + ","
                    + myData.getAge());
        
    



class MyThreadScopeData

    private MyThreadScopeData()

    private static ThreadLocal<Java多线程与并发——生产者与消费者应用案例

Java多线程与并发库高级应用-传统线程互斥技术

(黑马Java多线程与并发库高级应用)01 传统线程技术回顾

Java多线程与并发库高级应用-传统线程同步通信技术

(黑马Java多线程与并发库高级应用)04 传统线程同步通信技术

Java多线程与并发库高级应用-传统定时器技术回顾