java多线程及线程安全详解

为什么要使用多线程:

单线程只能干一件事  而多线程可以同时干好多事(将任务放到线程里执行  效率高)

而所谓同时干并不是真正意义上的同时   只是(这里就叫CPU)cpu在每个线程中随机切换来执行 线程中要干的活

多线程编写:

1)第一种:(线程类)

class Stu1 extends Thread{

  //重写 run方法 

}

调用:Stu1 su = new Stu1();

su.start()//内部会自动调用run方法    把run方法放到线程上调用

2)第二种:普通任务类(由于第一种类只能是单继承 就没法实现继承其他父类并且继承线程类 所以第一种方法扩展性比较差)

直接创建线程对象    线程内干的事  放到一个自定义对象里面实现   (用到修饰设计模式)

底层代码

class Thread{

private Runnable r

public void Thread(Runnable r){ //利用有参构造将自定义对象传进来

this.r = r;

}

public void start(){

  r.run();

}

}

class Stu implements Runnable{}  通过实现接口   线程类底层start调用的run方法 实际就是我们自定义类中的run方法

Thread th = new  Thread(new Stu())  //直接创建线程对象

th.start();

我们用第二种方法创建多线程如下:

public static void main(String[] args) {
//		不同线程干同一件事
		SaleWindow sw = new SaleWindow();
		Thread t1 = new Thread(sw);
		Thread t2 = new Thread(sw);
		t1.setName("窗口A");
		t2.setName("窗口B");
		t1.start();
		t2.start();
	}
public class SaleWindow implements Runnable {
	private int ticketCount = 10;

	@Override
	public void run() {
		// TODO Auto-generated method stub
//		多个窗口卖票
		for(int i = 0;i<10;i++){
			if(ticketCount>0){
				//字符串拼接信息   变量+""  就可以拼接成字符串
				System.out.println(Thread.currentThread().getName()+"卖出"+ticketCount+"张票");
				ticketCount--;
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
//					e.printStackTrace();
				}
			}
		}
	}

}

最后结果图:

从运行结果来看:同一张票卖给了二个人  这是在现实生活中不允许的 这样就会产生线程安全问题。

而产生线程安全问题的有三个要素必须同时满足才会产生线程安全:

1、必须有共享资源
2、必须是多线程环境
3、每个线程都适用了共享资源

而上面的例子:票是共享资源、又是多线程环境、线程执行任务的时候又使用了共享资源 所以会产生线程安全

怎么解决线程安全?

解决线程安全其核心:就是将某一个线程中的任务给锁(同步锁)起来,这个时候JVM就不得不等到本任务里的代码执行完以后在去执行另外一个线程里的任务。

二种方法:

1、同步代码块:

public class SaleWindow implements Runnable {
	private int ticketCount = 10;

	@Override
	public void run() {
		// TODO Auto-generated method stub
		// 多个窗口卖票
		for (int i = 0; i < 10; i++) {//可以随意设置锁
			synchronized (this) {
				if (ticketCount > 0) {
					// 字符串拼接信息 变量+"" 就可以拼接成字符串
					System.out.println(Thread.currentThread().getName() + "卖出"
							+ ticketCount + "张票");
					ticketCount--;
					try {
						Thread.sleep(500);//每隔500毫秒 线程休眠 随后自己自动会唤醒(目的是为了调节线程速度)
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						// e.printStackTrace();
					}
				}
			}
		}
	}

}

2、同步方法:

public class SaleWindow implements Runnable {
	private int ticketCount = 10;
     //默认固定的锁对象this        //将产生线程安全的代码封装到方法里并设置成同步方法
	public synchronized void syncB() {
		if (ticketCount > 0) {
			// 字符串拼接信息 变量+"" 就可以拼接成字符串
			System.out.println(Thread.currentThread().getName() + "卖出"
					+ ticketCount + "张票");
			ticketCount--;
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				// e.printStackTrace();
			}
		}
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		// 多个窗口卖票
		for (int i = 0; i < 10; i++) {
			syncB();
		}
	}

}

结果图:

最后结果每个窗口不能卖同样的票解决了线程安全问题

线程之间的通信

(ps:线程之间的通信和线程安全无关联二者不是一回事)

怎样才能让二个线程之间进行通信?

线程之间是相互独立的互不联系 而真正意义上的通信是通过中间件(同步锁 必须是同一把锁)来达到线程之间通信的目的

案例:二个线程来回交替输出一条数据(意思就是必须按照我说一句你说一句的这个规则走)

class BingB extends Thread {
	public void run(){
		for(int i = 0;i<5;i++){
			System.out.println("冰冰美,如花丑");
		}
	}
}

class RuH extends Thread{
	public void run(){
		for(int i = 0;i<5;i++){
			System.out.println("如花美,冰冰丑");
		}
	}
}

public class Test {
	public static void main(String[] args) {
		BingB bb = new BingB();
		RuH rh = new RuH();
		bb.start();
		rh.start();
	}
}

结果图:此结果不是交替出现的

要想达到交替的目的代码如下:

class MyLock{
	static Object o = new Object();
}

class BingB extends Thread {
	public void run(){
		for(int i = 0;i<5;i++){
			//加锁(意思是必须等到本线程的任务代码执行完以后才去执行别的线程的代码)
			synchronized (MyLock.o) {
				System.out.println("冰冰美,如花丑");
				MyLock.o.notify();//唤醒另一个线程(这里表示如花线程)
				try {
					MyLock.o.wait();//暂时彻底休眠本线程(不会自动唤醒 需要手动唤醒)   同时解锁  阻塞线程 代码就不会往下继续走  jvm会切换到另外一个线程中去
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
				}
			}
		}
	}
}

class RuH extends Thread{
	public void run(){
		for(int i = 0;i<5;i++){
			synchronized (MyLock.o) {
				System.out.println("如花美,冰冰丑");
				MyLock.o.notify();//唤醒另一个线程(这里表示如冰冰程)
				try {
					MyLock.o.wait();//暂时彻底休眠本线程   同时解锁  阻塞线程
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

public class Test {
	public static void main(String[] args) {
		BingB bb = new BingB();
		RuH rh = new RuH();
		bb.start();
		rh.start();
	}
}

多线程状态流程图:

写到这里有关线程的基本讲解完毕,如果内容有我自己理解错误的地方还请各位大神指教,小弟不吝求教!

原文地址:https://www.cnblogs.com/nianzhilian/p/9034018.html

时间: 2024-08-28 07:12:58

java多线程及线程安全详解的相关文章

JAVA多线程Thread VS Runnable详解

要求 必备知识 本文要求基本了解JAVA编程知识. 开发环境 windows 7/EditPlus 演示地址 源文件 进程与线程 进程是程序在处理机中的一次运行.一个进程既包括其所要执行的指令,也包括了执行指令所需的系统资源,不同进程所占用的系统资源相对独立.所以进程是重量级的任务,它们之间的通信和转换都需要操作系统付出较大的开销. 线程是进程中的一个实体,是被系统独立调度和分派的基本单位.线程自己基本上不拥有系统资源,但它可以与同属一个进程的其他线程共享进程所拥有的全部资源.所以线程是轻量级的

Java面试问题——线程全面详解总结

一.多线程是什么?为什么要用多线程? 介绍多线程之前要介绍线程,介绍线程则离不开进程. 首先进程 :是一个正在执行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元: 线程:就是进程中的一个独立控制单元,线程在控制着进程的执行.一个进程中至少有一个进程. 多线程:一个进程中不只有一个线程. 为什么要用多线程: 为了更好的利用cpu的资源,如果只有一个线程,则第二个任务必须等到第一个任务结束后才能进行,如果使用多线程则在主线程执行任务的同时可以执行其他任务,而不需要

java多线程——同步块synchronized详解

Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) 实例方法同步 静态方法同步 实例方法中同步块 静态方法中同步块 Java同步示例 Java 同步关键字(synchronized) Java中的同步块用synchronized标记.同步块在Java中是同步在某个对象上.所有同步在一个对象上的同步块在同时只能被一个线程进入并执行操作.所有其他等待进入该同步块的线程将

Java多线程中join方法详解

join()方法用于让当前执行线程等待join线程执行结束.其实现原理是不停的检查join线程是否存活,如果join线程存活则让当前线程永远等待. join()方法部分实现细节 while(isAlive()) { wait(0) } 其中wait(0)表示永远等待下去. join线程中止后,线程的this.notifyAll()方法会被调用,调用notifyAll()是在JVM里调用的所有在JDK中看不到,大家可以看JVM源码 示例程序: public calss ThreadTest{ pu

Java线程池详解(二)

一.前言 在总结了线程池的一些原理及实现细节之后,产出了一篇文章:Java线程池详解(一),后面的(一)是在本文出现之后加上的,而本文就成了(二).因为在写完第一篇关于java线程池的文章之后,越发觉得还有太多内容需要补充,每次都是修修补补,总觉得还缺点什么.在第一篇中,我着重描述了java线程池的原理以及它的实现,主要的点在于它是如何工作的.而本文的内容将更为上层,重点在于如何应用java线程池,算是对第一篇文章的一点补充,这样对于java线程池的学习和总结稍微完整一些. 使用过java线程池

Java性能分析之线程栈详解(下)

Java性能分析之线程栈详解(下) 转载自:微信公众号"测试那点事儿" 结合jstack结果对线程状态详解 上篇文章详细介绍了线程栈的作用.状态.任何查看理解,本篇文章结合jstack工具来查看线程状态,并列出重点关注目标.Jstack是常用的排查工具,它能输出在某一个时间,Java进程中所有线程的状态,很多时候这些状态信息能给我们的排查工作带来有用的线索. Jstack的输出中,Java线程状态主要是以下几种: 1.BLOCKED 线程在等待monitor锁(synchronized

Java虚拟机之垃圾回收详解一

Java虚拟机之垃圾回收详解一 Java技术和JVM(Java虚拟机) 一.Java技术概述: Java是一门编程语言,是一种计算平台,是SUN公司于1995年首次发布.它是Java程序的技术基础,这些程序包括:实用程序.游戏.商业应用程序.在全世界范围内,Java运行在超过数十亿台个人计算机上,数十亿台设备上,还包括手机和电视设备.Java由一系列的关键组件作为一个整体构建出了Java平台. Java Runtime Edition 当你下载Java,你就得到了Java运行环境(JRE).JR

【java项目实战】Servlet详解以及Servlet编写登陆页面(二)

Servlet是Sun公司提供的一门用于开发动态web网页的技术.Sun公司在API中提供了一个servlet接口,我们如果想使用java程序开发一个动态的web网页,只需要实现servelet接口,并把类部署到web服务器上就可以运行了. 到底什么是Servlet呢? 通俗一点,只要是实现了servlet接口的java程序,均称Servlet.Servlet是由sun公司命名的,Servlet = Server + Applet(Applet表示小应用程序),Servlet是在服务器端运行的小

Java垃圾回收机制(GC)详解

Java垃圾回收机制(GC)详解 简介: 垃圾回收GC(Garbage Collection)是Java语言的核心技术之一,之前我们曾专门探讨过Java 7新增的垃圾回收器G1的新特性,但在JVM的内部运行机制上看,Java的垃圾回收原理与机制并未改变.垃圾收集的目的在于清除不再使用的对象.GC通过确定对象是否被活动对象引用来确定是否收集该对象.GC首先要判断该对象是否是时候可以收集.两种常用的方法是引用计数和对象引用遍历. 垃圾收集的算法分析: Java语言规范没有明确地说明JVM使用哪种垃圾