线程的相关概念:
- 多线程:是指在一个程序(进程)中,运行时产生了不止一个线程。
- 并行:是指多个cpu实例或者多台机器同时进行一段逻辑处理,是同时运行。
- 并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时进行。并发往往在场景中有公共的资源,那么针对这个资源的调用会产生一个瓶颈,我们会用TPS或者QPS来反应这个系统的处理能力。(TPS/QPS:每秒钟Request/事务的 数量)。
- 线程安全:经常用来描述一段逻辑处理的代码。指在并发的情况下,该代码经过多线程使用,线程的调用顺序不影响任何结果。如不加事务处理的转账逻辑。
- 线程同步:保证共享资源的多线程访问成为线程安全,来保证结果的准确行。如在多个线程调用的方法上加入synchronization关键字处理,在保证结果的同时,提高性能才是一个优秀的程序。在程序线程安全和程序性能的优先级上,线程安全较大。
一、如果每个线程的执行代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据。
例子:售票。
public class ThreadShareBuyTicketDemo {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"窗口一").start();
new Thread(buyTicket,"窗口二").start();
}
}
class BuyTicket implements Runnable{
private int count = 100;
@Override
public void run() {
while(true){
synchronized (this) {
if(count > 0 ){
try {
Thread.sleep(100);
count--;
System.out.println(Thread.currentThread().getName()+"余票数:"+count);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
}
}
}
};
}
//加锁的写法
public class SalTickets {
public static void main(String[] args) {
/**
* 如果每个线程的执行代码相同,那么可以共用一个Runnable对象,这个Runnable有那个共享数据
*/
Tickets tickets = new Tickets();
new Thread(tickets,"窗口一").start();
new Thread(tickets,"窗口二").start();
}
}
class Tickets implements Runnable{
Lock lock = new ReentrantLock();
private int tickeCount = 100;
@Override
public void run() {
while(true){
sals();
}
}
public void sals(){
lock.lock();
try {
Thread.sleep(100);
if(tickeCount > 0){
tickeCount --;
System.out.println("窗口"+Thread.currentThread().getName()+"还剩票数:"+ tickeCount );
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
二、如果每个线程执行代码不同,这时候需要不同的Runnable对象(把对线传到Runnable中)。
例子一:设计4个线程,其中两个线程每次对j+1,另外两个线程每次对j-1。
public class ThreadMultiShareDemo {
public static void main(String[] args) {
final ThreadMulti threadMulti = new ThreadMulti();
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<100;i++){
threadMulti.increment();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<100;i++){
threadMulti.decrement();
}
}
}).start();
}
}
class ThreadMulti{
private int j=10;
public synchronized void increment(){
j++;
System.out.println("j++ 了:"+j);
}
public synchronized void decrement(){
j--;
System.out.println("j-- 了:"+j);
}
}
注:这里increment方法和decrement方法加synchronized关键字是因为当两个线程同时操作同一个变量时,就算是简单的j++操作时,在系统底层也是通过多条机器语句来实现,所以在执行j++过程也是要耗费时间,这时就有可能在执行j++的时候,另外一个线程H就会对j进行操作,因此另外一个线程H可能操作的可能就不是最新的值了。因此要提供线程同步。
例子二:消费者和生产者模式
一个生产者::一个消费者
public class ProductAndCustomer {
public static void main(String[] args) {
UserAction action = new UserAction();
new Thread(new product(action)).start();
new Thread(new product(action)).start();
}
static class product implements Runnable{
public UserAction action;
public product(UserAction action){
this.action = action;
}
@Override
public void run() {
while(true){
action.productAction(10);
}
}
}
static class customer implements Runnable{
public UserAction action;
public customer(UserAction action){
this.action = action;
}
@Override
public void run() {
while(true){
action.customerAction(10);
}
}
}
}
class UserAction{
//现有商品
private int currentGoods = 0;
//最大库存
private int maxInventory = 30;
//消费
public synchronized void customerAction(int num){
try {
if(num>currentGoods){
System.out.println("通知消费者:"+Thread.currentThread().getName()+",现有库存不足,正在生产中");
this.wait();
}
currentGoods -= num;
System.out.println("用户:"+Thread.currentThread().getName()+"消费了商品:"+num+",库存还有:"+currentGoods);
this.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产
public synchronized void productAction(int num){
try {
if((num+currentGoods) > maxInventory){
currentGoods = 30;
System.out.println("通知生产者:"+Thread.currentThread().getName()+"库存已满,库存还有:"+currentGoods);
this.wait();
}
currentGoods += num;
System.out.println("生产者:"+Thread.currentThread().getName()+"生产了商品:"+num+",库存还有:"+currentGoods);
this.notify();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
多个生产者::多个消费者。当有多个生产者和多个消费者的时候,运行的结果会出现负值,这意味着,我们线程出现了不同步,这个时候我们需要吧if判断改为while,和this.nitifyAll();
原因是要线程wait();的时候,需要while来保证线程可运行,一旦线程被唤醒,直接运行。
package com.XC.saleticket;
public class ProductAndCustomer {
public static void main(String[] args) {
UserAction action = new UserAction();
new Thread(new product(action)).start();
new Thread(new product(action)).start();
new Thread(new customer(action)).start();
new Thread(new customer(action)).start();
}
static class product implements Runnable{
public UserAction action;
public product(UserAction action){
this.action = action;
}
@Override
public void run() {
while(true){
action.productAction(10);
}
}
}
static class customer implements Runnable{
public UserAction action;
public customer(UserAction action){
this.action = action;
}
@Override
public void run() {
while(true){
action.customerAction(10);
}
}
}
}
class UserAction{
//现有商品
private int currentGoods = 0;
//最大库存
private int maxInventory = 30;
//消费
public synchronized void customerAction(int num){
try {
while(num>currentGoods){
System.out.println("通知消费者:"+Thread.currentThread().getName()+",现有库存不足,正在生产中");
this.wait();
}
currentGoods -= num;
System.out.println("用户:"+Thread.currentThread().getName()+"消费了商品:"+num+",库存还有:"+currentGoods);
this.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产
public synchronized void productAction(int num){
try {
while((num+currentGoods) > maxInventory){
currentGoods = 30;
System.out.println("通知生产者:"+Thread.currentThread().getName()+"库存已满,库存还有:"+currentGoods);
this.wait();
}
currentGoods += num;
System.out.println("生产者:"+Thread.currentThread().getName()+"生产了商品:"+num+",库存还有:"+currentGoods);
this.notifyAll();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
总结:在多线程中尽量使用while语句,当使用Lock的时候,lock.unlock();一定要写在finally里面。this.wait();this.notify();this.notifyAll();要写在synchronized里面。