多线程

117 阅读6分钟

并发与并行

并发:两个或者多个事件在同一时间段内发生(交替执行)
并行:两个g或者多个事件在同一时刻发生(同时发生)
进程:一个应用程序 点击一个应用程序进入到内存中占用一些内存运行,这些进入内存的程序叫做进程
线程:进程中的一个执行单元。线程属于进程,是进程中的一个执行单元,负责程序的执行
多线程的好处:1.效率高;
             2.多个线程之间互不影响(在不同的占空间中)

java为抢占式调度

创建多线程程序的第一种方法

1.实现一个继承Thread的子类,重写Run方法
2.创建Thread子类的实例,即创建线程对象
3.调用线程对象的start()方法来启动线程

eg1:

 class MyThread extends Thread{
	@Override 
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println("myThread->"+i);
		}
	}
}

public class HelloWorld {
	public static void main(String[] args) {
		Thread th=new MyThread();
		th.start();
		for(int i=0;i<10;i++) {
			System.out.println("Main->"+i);
		}
	}
}

输出:

Main->0
Main->1
Main->2
myThread->0
myThread->1
myThread->2
myThread->3
myThread->4
myThread->5
myThread->6
myThread->7
myThread->8
myThread->9
Main->3
Main->4
Main->5
Main->6
Main->7
Main->8
Main->9

两个线程随机抢夺cpu.谁抢到谁执行,因此结果随机。

获取当前线程的名称:

      System.out.println(Thread.currentThread().getName());

设置线程的名称:

    th.setName("xiaozhnag");
    
    或者
    构造函数方式

sleep: 让当前执行的线程以毫秒为单位暂停

                    try {
				Thread.sleep((long)1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

创建多线程程序的第二种方法:实现runable接口

步骤:

1.创建一个Runnable接口的实现类
2.在实现类中重写run方法,设置线程任务
3.创建一个Runnable接口的实现类对象
4.创建Thread对象,构造方法中传递Runnable接口的实现类对象
5,调用Thread类中的start方法,开启新的线程执行run方法

eg1:

class HisThread implements Runnable{
	@Override
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println(i);
			try {
				Thread.sleep((long)1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
public class HelloWorld {
	public static void main(String[] args) {
		HisThread ru=new HisThread();
		new Thread(ru).start();
	}
}

使用Runnable方式的好处:

1.避免了单继承的局限性
    一个类只能继承一个类,实现了Runnable接口,还可以继承其他类,实现了其他接口
2.增强了程序的扩展性,降低了程序的耦合
    实现了Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)
    只需改变传入Thread的参数,就能改变执行的任务

匿名内部类实现线程的创建

第一种:

public class HelloWorld {
	public static void main(String[] args) {
		new Thread() {
			@Override
			public void run() {
				for(int i=0;i<10;i++) {
					System.out.println("way1->"+i);
				}
			}
		}.start();
	}
}

第二种

public class HelloWorld {
	public static void main(String[] args) {
		new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i=0;i<10;i++) {
					System.out.println("way2->"+i);
				}
			}
		}).start();
	}
}

线程安全

  • 单线程程序不会出现线程安全问题

  • 多线程没有访问共享数据不会出现线程安全问题

  • 多线程访问了共享数据会产生线程安全问题

eg.买票问题

class ticketRunnable implements Runnable{
	private int ticket=100;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			if(ticket>0) {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"窗口卖第"+ticket+"张票..");
				ticket--;
			}
			else {
				break;
			}
		}
	}
}
public class SellTicket {
	public static void main(String[] args) {
		ticketRunnable run=new ticketRunnable();
		Thread t1=new Thread(run);
		Thread t2=new Thread(run);
		Thread t3=new Thread(run);
		t1.start();
		t2.start();
		t3.start();
	}

}

输出:

Thread-1窗口卖第100张票..
Thread-2窗口卖第100张票..
Thread-0窗口卖第100张票..
Thread-0窗口卖第97张票..
Thread-1窗口卖第97张票..
Thread-2窗口卖第97张票..
Thread-0窗口卖第94张票..
Thread-1窗口卖第94张票..
Thread-2窗口卖第92张票..
Thread-0窗口卖第91张票..
Thread-1窗口卖第91张票..
Thread-2窗口卖第89张票..
.....
Thread-0窗口卖第1张票..
Thread-1窗口卖第1张票..
Thread-2窗口卖第-1张票..

出现了线程安全问题,卖出了不存在的票和重复的票

解决线程安全问题的方式

方式一:使用同步代码块

格式:

synchronized (锁对象) {
    可能出现线程安全问题的代码块(访问了共享数据的代码)
}
class ticketRunnable implements Runnable{
	private int ticket=100;
	
	Object ob=new Object();
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			synchronized (ob) {
				if(ticket>0) {
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"窗口卖第"+ticket+"张票..");
					ticket--;
				}
			}
		}
	}
}
public class SellTicket {
	public static void main(String[] args) {
		ticketRunnable run=new ticketRunnable();
		Thread t1=new Thread(run);
		Thread t2=new Thread(run);
		Thread t3=new Thread(run);
		t1.start();
		t2.start();
		t3.start();
	}

}
方式2:使用同步方法

格式:

修饰符 synchronized 返回值类型 方法名(参数列表){
    访问数据的代码;
}

eg.

class ticketRunnable implements Runnable{
	private int ticket=100;
	Object ob=new Object();
	public synchronized void SellTicket() {
		if(ticket>0) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"窗口卖第"+ticket+"张票..");
			ticket--;
		}
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			SellTicket();
		}
	}
}
public class SellTicket {
	public static void main(String[] args) {
		ticketRunnable run=new ticketRunnable();
		Thread t1=new Thread(run);
		Thread t2=new Thread(run);
		Thread t3=new Thread(run);
		t1.start();
		t2.start();
		t3.start();
	}
}
方式3:使用Lock锁

方法:

lock();
unlock();

eg.

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class ticketRunnable implements Runnable{
	private int ticket=100;
	Object ob=new Object();
	Lock l=new ReentrantLock();//锁对象
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			l.lock();//上锁
			if(ticket>0) {
				try {
					System.out.println(Thread.currentThread().getName()+"窗口卖第"+ticket+"张票..");
					ticket--;
				} catch (Exception e) {
					e.printStackTrace();
				}finally{
					l.unlock();//解锁
				}
				
			}else {
				break;
			}
		}
		
	}
}
public class SellTicket {
	public static void main(String[] args) {
		ticketRunnable run=new ticketRunnable();
		Thread t1=new Thread(run);
		Thread t2=new Thread(run);
		Thread t3=new Thread(run);
		t1.start();
		t2.start();
		t3.start();
	}
}

线程之间的通信(等待/唤醒)

生产者消费者例子:

public class SellTicket {
	static Object obj=new Object();
	
	public static void main(String[] args) {
		
		//创建顾客线程
		new Thread() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				synchronized (obj) {
					System.out.println("顾客买包子");
					try {
						obj.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					System.out.println("顾客吃包子");
				}
			}
		}.start();
		
		//创建老板线程
		new Thread() {
			public void run() {
				System.out.println("老板做包子,需要5s");
				synchronized (obj) {
					try {
						sleep(1000);
						System.out.println("老板做好,唤醒顾客");
						obj.notify();//唤醒顾客
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			};
		}.start();
	}
}

等待唤醒机制

...

线程池

使用步骤:

1.使用线程池的工厂类Excutors里提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
2.创建一个类,实现Runnable接口,重写run方法,设置线程任务。
3.调用ExecutorService中的submit方法,传递线程任务,开启线程,执行run方法
4.调用ExecutorService中的shutdown方法,关闭线程池。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Runimp implements Runnable{
	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+"创建了一个线程");
	}
}
public class ThreadPool {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ExecutorService es=Executors.newFixedThreadPool(2);
		es.submit(new Runimp());
		es.submit(new Runimp());
		es.submit(new Runimp());
		es.shutdown();
	}
}