Java多线程

Java多线程复习

线程创建

  1. 实现runnable类(推荐)

    public class TreadLearningIR implements Runnable {
        private String name;
        public TreadLearningIR(String name){
            this.name=name;
        }
        @Override
        public void run() {
            for(int i=0;i<5;i++){
                System.out.println(name+"运行: "+i);
            }
        }
        public static void main(String[] args) {
            new Thread(new TreadLearningIR("A")).start();
            new Thread(new TreadLearningIR("B")).start();
        }
    }
    
  2. 继承Thread类

    public class ThreadLearningET extends Thread {
        private String name;
        public ThreadLearningET(String name){
            this.name=name;
        }
        @Override
        public void run() {
            for(int i=0;i<5;i++){
                System.out.println(name+"运行: "+i);
    
            }
        }
        public static void main(String[] args) {
            ThreadLearningET threadLearning1 = new ThreadLearningET("A");
            ThreadLearningET threadLearning2 = new ThreadLearningET("B");
            threadLearning1.start();
            threadLearning2.start();
        }
    }
    

锁 synchronize

synchronize

synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;

线程安全问题

线程安全出现的原因:多个线程抢占统一资源,当一个线程抢到CPU之后还没执行完,CPU又被抢走,造成数据被重复调用。

  1. 模拟线程出现问题:

    public class Demo12 {
    	public static void main(String[] args) {
    		//任务对象
    		Ticket ticket = new Ticket();
    		//将任务与线程绑定
    		Thread t0 = new Thread(ticket);
    		Thread t1 = new Thread(ticket);
    		Thread t2 = new Thread(ticket);
    		Thread t3 = new Thread(ticket);
    		//开启线程
    		t0.start();
    		t1.start();
    		t2.start();
    		t3.start();
    	}
    }
    class Ticket implements Runnable{
         int sum = 20;
         boolean flag = true;
    	public void run() {
    		while (flag) {
    			//让当前的线程睡100毫秒
    			//作用:让他暂时让出cpu 
    			try {
    				Thread.sleep(100);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			if (sum > 0) {
    				System.out.println("剩余 票数:"+ --sum);
    			}else {
    				flag = ! flag;
    			}
    		}
    	}
    }
    //输出结果  多个值被同时抢到
    剩余 票数:19
    剩余 票数:17
    剩余 票数:18
    剩余 票数:19
    剩余 票数:16
    剩余 票数:15
    剩余 票数:14
    剩余 票数:13
    剩余 票数:12
    剩余 票数:11
    剩余 票数:10
    剩余 票数:10
    剩余 票数:8
    剩余 票数:9
    剩余 票数:7
    剩余 票数:7
    剩余 票数:6
    剩余 票数:6
    剩余 票数:5
    剩余 票数:5
    剩余 票数:4
    剩余 票数:4
    剩余 票数:3
    剩余 票数:3
    剩余 票数:1
    剩余 票数:2
    剩余 票数:0
    剩余 票数:0
    
  2. 加锁解决线程安全问题

    public class Demo13 {
    	public static void main(String[] args) {
    		//任务对象
    		Ticket1 ticket = new Ticket1();
    		//将任务与线程绑定
    		Thread t0 = new Thread(ticket);
    		Thread t1 = new Thread(ticket);
    		Thread t2 = new Thread(ticket);
    		Thread t3 = new Thread(ticket);
    		//开启线程
    		t0.start();
    		t1.start();
    		t2.start();
    		t3.start();
    	}
    }
    //任务类
    class Ticket1 implements Runnable{
         int sum = 20;
         boolean flag = true;
         //让Object类型的对象临时充当锁
         Object object = new Object();
    	public void run() {
    		while (flag) {
    			//让当前的线程睡100毫秒
    			//作用:让他暂时让出cpu 
    			try {
    				Thread.sleep(100);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			/*
    			 * 锁的条件:
    			 * 1.锁必须是对象      普通的对象/this/字节码文件
    			 * 2.要被所有的线程共享
    			 * 注意:字节码文件的使用范围太大,一般不建议使用.
    			 */
    			synchronized (this) {//线程互斥
    				if (sum > 0) {
    					System.out.println("剩余 票数:"+ --sum);
    				}else {
    					flag = ! flag;
    				}
    			}
    		}
    	}
    }
    //运行结果
    剩余 票数:19
    剩余 票数:18
    剩余 票数:17
    剩余 票数:16
    剩余 票数:15
    剩余 票数:14
    剩余 票数:13
    剩余 票数:12
    剩余 票数:11
    剩余 票数:10
    剩余 票数:9
    剩余 票数:8
    剩余 票数:7
    剩余 票数:6
    剩余 票数:5
    剩余 票数:4
    剩余 票数:3
    剩余 票数:2
    剩余 票数:1
    剩余 票数:0
       
    
创建锁的条件
  1. 锁必须是对象 普通的对象/this/字节码文件
  2. 要被所有的线程共享
同步代码块\同步函数
  • 同步代码块儿使用更加的灵活,只给需要同步的部分代码同步即可,而同步函数是给这个函数内的所有代码同步.由于处于同步的代码越少越好,所以最好使用同步代码块儿
class Bank {
	int money;
	//使用同步代码块儿
	public void addMoney(int money) {
		synchronized (this) {
		    this.money += money;
		    System.out.println(this.money);
		}
	}
}
class Bank {
	int money;
	//使用同步函数
	//非静态的同步函数
	//在synchronized后面默认有一个this
	public synchronized void addMoney(int money) {
	    this.money += money;
	    System.out.println(this.money);
	}
	//静态的同步函数
	//在synchronized后面默认有一个当前类的字节码文件-----Bank.class
	public synchronized static void addMoney(int money) {
	    this.money += money;
	    System.out.println(this.money);
	}
}

线程之间通信

  • 普通线程通信控制(对共享数据加锁)

    //数据类
    class Des{
    	String name;
    	String sex;
    }
    //输入任务
    class Input implements Runnable{
    	Des des;
    	public Input(Des des) {
    		this.des = des;
    	}
    	public void run() {
    		int i=0;
    		while (true) {
    			/*
    			 * 需要给输入任务和输出任务同时加一把锁,保证两个任务之间是同步的
    			 * 给两个任务加一把锁:可以是des或者Object.class
    			 * 分析:
    			 * 不建议使用Object.class:由于Object的使用范围太大,可能造成不必要的错误.
    			 * 使用des最合适,因为他只被当前的两个任务共享.
    			 * 
    			 *注意:对于当前的情况只给一个线程加锁,无法实现两个线程的同步.
    			 */
    			synchronized (des) {
    				if (i==0) {
    					des.name = "万表哥";
    					des.sex = "男";
    				}else {
    					des.name = "蔡表妹";
    					des.sex = "女";
    				}
    				
    				i=(i+1)%2;
    			}
    		}
    	}
    }
    //输出任务
    class Output implements Runnable{
    	Des des;
    	public Output(Des des) {
    		this.des = des;
    	}
    	public void run() {
    		while (true) {
    			synchronized (des) {
    				System.out.println("姓名:"+des.name+"   性别:"+des.sex);
    			}
    		}
    	}
    }
    
  • 进程间三态控制

    • wait():让当前的线程变成等待的状态,失去抢占CPU的资格,等待被唤醒

    • notify():让当前的线程从等待状态唤醒,相当于从池子中取出线程.(唤醒的是同一把锁下的任意一个线程

    • notifyAll():唤醒的是同一把锁下的所有线程

      //数据类
      class Des1{
      	String name;
      	String sex;
      	boolean flag = false;//用于唤醒和等待之间的切换
      }
      
      //输入任务
      class Input1 implements Runnable{
      	Des1 des;
      	public Input1(Des1 des) {
      		this.des = des;
      	}
      	public void run() {
      		int i=0;
      		while (true) {
      			/*
      			 * 需要给输入任务和输出任务同时加一把锁,保证两个任务之间是同步的
      			 * 给两个任务加一把锁:可以是dies或者Object.class
      			 * 分析:
      			 * 不建议使用Object.class:由于Object的使用范围太大,可能造成不必要的错误.
      			 * 使用des最合适,因为他只被当前的两个任务共享.
      			 * 
      			 *注意:对于当前的情况只给一个线程加锁,无法实现两个线程的同步.
      			 */
      			synchronized (des) {
      				if (des.flag == true) {//如果是true,让当前的线程处于等待状态
      					try {
      						des.wait();//当执行这行代码的时候,这里对应的是哪个线程,就操作的是哪个线程
      					} catch (InterruptedException e) {
      						e.printStackTrace();
      					}
      				}
      				if (i==0) {
      					des.name = "万表哥";
      					des.sex = "男";
      				}else {
      					des.name = "蔡表妹";
      					des.sex = "女";
      				}
      				
      				i=(i+1)%2;
      				
      				des.flag = ! des.flag;
      				des.notify();//唤醒的是同一把锁下的线程,因为现在只有一个输入线程,一个输出线程.所以这里唤醒的是输出线程
      				//当线程池中没有被当前的锁标记的线程可唤醒时,我们成为空唤醒,空唤醒不影响程序的执行.
      			}
      		}
      	}
      }
      
      //输出任务
      class Output1 implements Runnable{
      	Des1 des;
      	public Output1(Des1 des) {
      		this.des = des;
      	}
      	public void run() {
      		while (true) {
      			
      			synchronized (des) {
      				if (des.flag == false) {//让输出线程等待
      					try {
      						des.wait();
      					} catch (InterruptedException e) {
      						e.printStackTrace();
      					}
      				}
      				System.out.println("姓名:"+des.name+"   性别:"+des.sex);
      				
      				des.flag = ! des.flag;
      				des.notify();//唤醒输入线程
      			}
      		}
      	}
      }
      

生产者\消费者设计模式

单生产者、单消费者

public class Demo9 {
	public static void main(String[] args) {
		//准备数据
		Product product = new Product();
		//准备任务
		Producer producer = new Producer(product);
		Consumer consumer = new Consumer(product);
		//准备线程
		Thread proThread = new Thread(producer);
		Thread conThread = new Thread(consumer);
		//开启线程
		proThread.start();
		conThread.start();	
	}
}

//创建产品
class Product{
	String name;//产品的名字
	double price;//产品的价格
	int count;//生产的产品数量
	
	//标识
	boolean flag = false;
	
	//准备生产
	public synchronized void setProduce(String name,double price){
		if (flag == true) {
			try {
				wait();//让生产线程等待
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		this.name = name;
		this.price = price;
		System.out.println(Thread.currentThread().getName()+"   生产了:"+this.name+"   产品的数量:"+this.count+"   价格:"+this.price);
		
		count++;
		flag = ! flag;
		notify();//唤醒消费线程
	}
	//准备消费
	public  synchronized void getConsume() {
		if (flag == false) {
			try {
				wait();//让消费线程等待
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName()+"   消费了:"+this.name+"   产品的数量:"+this.count+"   价格:"+this.price);
		//唤醒生产线程
		flag = ! flag;
		notify();
	}
}
//创建生产任务
class Producer implements Runnable{
	Product product;
	public Producer(Product product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.setProduce("bingbing", 10);
		}
		
	}
}
//创建消费任务
class Consumer implements Runnable{
	Product product;
	public Consumer(Product product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.getConsume();
		}
	}
}

多生产者、多消费者

public class Demo10 {
	public static void main(String[] args) {
		//准备数据
		Product1 product = new Product1();
		//准备任务
		Producer1 producer = new Producer1(product);
		Consumer1 consumer = new Consumer1(product);
		//准备线程
		Thread proThread1 = new Thread(producer);
		Thread proThread2 = new Thread(producer);
		Thread conThread1 = new Thread(consumer);
		Thread conThread2 = new Thread(consumer);
		//开启线程
		proThread1.start();
		conThread1.start();	
		proThread2.start();
		conThread2.start();	
	}
}

//创建产品
class Product1{
	String name;//产品的名字
	double price;//产品的价格
	int count;//生产的产品数量
	
	//标识
	boolean flag = false;
	
	//准备生产
	public synchronized void setProduce(String name,double price){
		while (flag == true) {
			try {
				wait();//让生产线程等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		this.name = name;
		this.price = price;
		System.out.println(Thread.currentThread().getName()+"   生产了:"+this.name+"   产品的数量:"+this.count+"   价格:"+this.price);
		
		count++;
		flag = ! flag;
		//notify();//唤醒消费线程
		notifyAll();
	}
	//准备消费
	public  synchronized void getConsume() {
		while (flag == false) {
			try {
				wait();//让消费线程等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName()+"   消费了:"+this.name+"   产品的数量:"+this.count+"   价格:"+this.price);
		//唤醒生产线程
		flag = ! flag;
		//notify();
		notifyAll();
	}
}
//创建生产任务
class Producer1 implements Runnable{
	Product1 product;
	public Producer1(Product1 product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.setProduce("bingbing", 10);
		}
	}
}
//创建消费任务
class Consumer1 implements Runnable{
	Product1 product;
	public Consumer1(Product1 product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.getConsume();
		}
	}
}

Lock

​ 比synchronize效率更高的锁机制

  • 使用场景

    当进行多生产者多消费者的功能时,使用Lock,其他的都使用synchronized

public class Demo11 {
	public static void main(String[] args) {
		//准备数据
		Product2 product = new Product2();
		//准备任务
		Producer2 producer = new Producer2(product);
		Consumer2 consumer = new Consumer2(product);
		//准备线程
		Thread proThread1 = new Thread(producer);
		Thread proThread2 = new Thread(producer);
		Thread conThread1 = new Thread(consumer);
		Thread conThread2 = new Thread(consumer);
		//开启线程
		proThread1.start();
		conThread1.start();	
		proThread2.start();
		conThread2.start();	
	}
}

//创建产品
class Product2{
	String name;//产品的名字
	double price;//产品的价格
	int count;//生产的产品数量
	
	//标识
	boolean flag = false;
	
	//创建锁对象
	Lock lock = new ReentrantLock();
	//用于生产任务的Condition
	Condition proCon = lock.newCondition();
	//用于消费任务的Condition
	Condition conCon = lock.newCondition();
	
	//准备生产
	public  void setProduce(String name,double price){
		try {
			lock.lock();//获取锁
			while (flag == true) {
				try {
					proCon.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			this.name = name;
			this.price = price;
			System.out.println(Thread.currentThread().getName()+"   生产了:"+this.name+"   产品的数量:"+this.count+"   价格:"+this.price);
			
			count++;
			flag = ! flag;
			conCon.signal();
		}finally {
			lock.unlock();//释放锁
		}
		
	}
	//准备消费
	public   void getConsume() {
		try {
			lock.lock();
			while (flag == false) {
				try {
					conCon.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName()+"   消费了:"+this.name+"   产品的数量:"+this.count+"   价格:"+this.price);
			//唤醒生产线程
			flag = ! flag;
			proCon.signal();
		}finally {
			lock.unlock();
		}
	}
}
//创建生产任务
class Producer2 implements Runnable{
	Product2 product;
	public Producer2(Product2 product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.setProduce("bingbing", 10);
		}
		
	}
}
//创建消费任务
class Consumer2 implements Runnable{
	Product2 product;
	public Consumer2(Product2 product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.getConsume();
		}
	}
}

守护线程

​ 相当于后台线程.依赖于前台线程.正常情况下,当前台线程结束的时候,不管守护线程有没有结束,都会立刻结束.典型的守护线程:垃圾回收线程

public class Demo15 {
	public static void main(String[] args) {
		MyTest1 myTest1 = new MyTest1();
		Thread t0 = new Thread(myTest1);
		/*
		 * 当程序调用setDaemon方法时,并且将参数设置成true.当前线程就变成了守护线层.
		 * 注意:这个方法一定要在start方法之前调用
		 */
		t0.setDaemon(true);
		t0.start();
		int i=0;
		while (true) {
			if (++i == 10) {
				System.out.println(Thread.currentThread().getName()+"   主线程");
				break;
			}
		}
	}
}
class MyTest1 implements Runnable{
	public void run() {
		while (true) {
			System.out.println(Thread.currentThread().getName()+"   子线程");
		}
	}
}

join

/*
 * join()方法:
 * 原理:线程一旦调用了join方法,他的优先级会高于主线程.主线程会等当前的线程执行完后再去执行.
 * 注意点:优先级只比main线程的高.对其他的线程没有影响.
 * 
 */
public class Demo16 {
	public static void main(String[] args) {
		MyTest2 myTest1 = new MyTest2();
		Thread t0 = new Thread(myTest1);
		Thread t1 = new Thread(myTest1);
		t0.start();
		t1.start();
		/*
		 * 当线程开始工作后,让t0调用join方法,让他的优先级高于main线程
		 * 注意:join方法必须在线程开始工作后,执行.
		 */
		try {
			t0.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName()+"   主线程"+i);
		}
	}
}

class MyTest2 implements Runnable{
    public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName()+"   子线程"+i);
		}
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值