[Java]多线程复习(更新未完)

知识点小结:(具体看例子)

多线程:

线程是程序中单独创建的控制单元,是并发执行的程序。外部顺序执行的程序叫做主线程

线程是多任务操作系统调用CPU来回切换的程序。

注意:线程开启要用start方法,虚拟机调用底层向操作系统申请一个单独线程。如果你只执行run方法,

那么并没有开启一个线程,仅仅是一个普通类的方法,那么是在主线程中顺序执行的。你开启start是虚

拟机新开启一个线程后自动去调用run方法的。

Windows执行虚拟机,虚拟机执行多线程程序,多线程程序调用底层Windows,最终是Windows调用自己。

线程五种状态:

创建,消亡:stop方法或run方法执行完

临时状态:阻塞。有执行资格但没有执行权。

sleep,wait是冻结状态,放弃执行资格。

notify后,回到临时状态,不一定运行,因为不一定抢到执行权。

因为我们的Thread继承Thread父类,要用super调用父类构造方法赋予名称

卖票:多线程共享同样的资源,定义为静态。ticket--,1比2,3号先打印出来是双核CPU造成的,因为调用

在命令行打印也是一个程序。如果不定义为静态,Ticket仍然继承Thread复写run方法,那么只能有一个

窗口卖票。所以要用另外的方式创建多线程执行的对象。

让Thread调用实现Runnable的类的run方法。

实现方式和继承方式的区别:(面-试-考-点)java单根继承结构,已有继承的类无法再继承Thread

安全问题:多线程执行同一段代码操作同一资源时。这里的卖票例子,到最后一张票时,可能某个线程挂

起在tickets--之前,另一个线程进来,输出后tickets--,这时票已经为0了,等待的线程再执行,出现0

,-1,-2号票等情况

产生的原因:多条语句操作共享数据,一个线程对多条语句只执行了一部分,另一个进程参与进来导致共

享数据错误。

解决:对多条操作共享数据的语句同步起来(同步代码块,锁是一个运行期间唯一对象就行),判断该对

象状态,进入,锁状态,执行,直到该线程执行完,解锁,其他线程才进来。锁,锁旗标,监视器。

同步前提:1.两个或两个以上线程 2.必须是多个线程使用同一个锁(执行同段同步代码)

run方法要是整个放在同步里,成了单线程了。不需要同步的代码根本不需要放在同步里。

同步函数的锁是this

证明:同时写一个同步函数和一个以this为锁的同步代码块,内容是操作同一个资源,两个线程各自跑

注意,如果同步代码块用的不是this这个锁,由于不是同一个锁,那么两个线程就没有实现同步,各运行

各的,操作的是多条语句的同一资源,那么就会有线程安全问题。

静态同步函数的锁是类字节码文件对象

验证:把上面例子同步代码块的锁改成此字节码文件对象就会确保线程安全,而不是就会有线程安全问题

死锁:同步代码块嵌套,用的是同样的锁的时候,两个线程各自需要对方的锁。

几个例子:

1.卖票:已解决线程安全问题(多线程操作多条语句的同一资源)

package cn.xbai.thread;

class Ticket implements Runnable{
	private int tickets=100;

	public void run(){
		while(true){
			synchronized(this){
			if(tickets<=0)//一定要自己尝试,看效果决定知识点理解的对不对,方案能不能解决问题!!
				break;
			//0------1---------2---------3
			//synchronized(this){//放这里也不对!!可能都挂起在上面的判断语句那里!!尤其是这里加了同步,一个线程锁住了,其他线程进不来更容易挂起在这里!!在最后1张票卖完后再进入,出现0,-1,-2!!!
			//自己验证:只同步最下面一句是解决不了问题的,仍然可以都挂起在sleep语句那里,所以要把操作同一资源的多-条-语-句都同步起来,因为可能一个线程只执行部分语句,执行权就被夺走了,引发操作同一资源的线程安全问题。sleep语句只是为了演示这种多语句操作同一资源时执行权被夺走的情况(放大这种被夺走的概率,或者说手动演示就让它被夺走!)
			try {//剩1张票时,都过了上面的判断条件到这里------->我这里出现了更严重的情况:(老师的是判断tickets>0时执行)因为几个线程到了这里都唤醒,tickets变为负数,那么再次进入循环时发现不是0了,永不结束了!!为了便于说明改成<=0!!
				//0------1-------2--------3
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//synchronized(this){
			System.out.println(Thread.currentThread().getName()+"..."+tickets--);
			//}
			}
		}
	}
}

public class TicketDemo {

	public static void main(String[] args){

		Ticket ticket=new Ticket();
		new Thread(ticket).start();
		new Thread(ticket).start();
		new Thread(ticket).start();
		new Thread(ticket).start();
		new Thread(ticket).start();
	}
}

输出结果:(部分示例)

Thread-0...100

Thread-2...99

Thread-4...98

Thread-1...97

Thread-3...96

Thread-0...95

...

Thread-0...25

Thread-2...24

Thread-4...23

Thread-1...22

Thread-3...21

Thread-0...20

Thread-2...19

Thread-4...18

Thread-1...17

Thread-3...16

Thread-0...15

Thread-2...14

Thread-4...13

Thread-1...12

Thread-3...11

Thread-0...10

Thread-2...9

Thread-4...8

Thread-1...7

Thread-3...6

Thread-0...5

Thread-2...4

Thread-4...3

Thread-1...2

Thread-3...1

2.银行存钱:已解决线程安全问题

package cn.xbai.thread;

class Bank{
	private int sum=0;

	public int add(int num){
		return sum+=num;
	}
}

class Cus implements Runnable{

	private Bank bank=new Bank();
	public void run(){
		//循环多次才能看出多线程任务的意义
		for(int i=0;i<3;i++){//i不需要同步,局部变量,每个线程都有一个
			synchronized(this){
			//放慢过程,放大矛盾,这样问题就产生了
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}//我不明白这里怎么会出现输出两个100的情况,下面应该是两条语句:
			System.out.println(bank.add(100));
			}
			/**
100
100         //这里返回值应该是一个独立结果中间值,是sum的副本值,并且应该是先输出后加的,那么应该是第一个线程想输出还没加,第二个线程进来输出并加了,第一个线程直接输出100,然后再加?
200
300
400
500
500
600
700
800
900
1000
1100
1200
1300

			 */
			/**
200
100
300
500
400
600             这一组执行结果会提供原因吗?
700
800
900
1000
1100
1200
1300
1400
1500

			 */
		}
	}
}
public class BankDemo {

	public static void main(String[] args){

		Cus c=new Cus();
		new Thread(c).start();//每个人存300
		new Thread(c).start();
		new Thread(c).start();
		new Thread(c).start();
		new Thread(c).start();
	}
}

运行结果:

100

200

300

400

500

600

700

800

900

1000

1100

1200

1300

1400

1500

3.单例设计模式的懒汉式的线程安全问题:面试题多个考点(为什么会出现线程安全问题,怎么解决其线程安全问题,用什么锁,怎么提高同步后的效率...)!!

package cn.xbai.thread;
//单例设计模式:饿汉式--------->没有线程安全问题:操作共享资源的语句只有一句
/*class Single{
	private static final Single s=new Single();//final代表不可变,static代表只有一份(还有不依赖对象,但这里本来就只有一个对象)
	private Single(){}
	public static Single getInstance(){
		return s;
	}
}*/
//懒汉式:延迟加载
class Single{
	private static Single s=null;//注意这里不能final了,否则无法再赋值
	private Single(){}
	public static Single getInstance(){
		//加上双重判断,提高懒汉式同步的效率,只要创建了对象,外层if不满足,就不用再进入同步代码块(判断锁的过程是低效的)
		if (s == null) {
			synchronized (Single.class) {// 注意静态方法是没有this的,锁是字节码文件对象!!
				if (s == null) {// 注意要判断!不为空不用再创建!
					// -------A---------B
					s = new Single();// s为共享数据,懒汉式在多线程访问时会出现安全隐患:单列模式new出了多个对象!!需要同步!!
				}
			}
		}
		return s;
	}
}
public class SingleDemo {

}

4.死锁:死锁出现的原因,写一个死锁程序,怎样避免死锁,面试考点!!

package cn.xbai.thread;

class DeadPool implements Runnable{

	public boolean flag=true;

	public void run(){
		if(flag==true){
			while(true){
			synchronized(Locks.LOCK_ONE){
				System.out.println("if lock one");
				synchronized(Locks.LOCK_TWO){
					System.out.println("if lock two");
				}
			}
			}
		}else{
			while(true){
			synchronized(Locks.LOCK_TWO){
				System.out.println("else lock two");
				synchronized(Locks.LOCK_ONE){
					System.out.println("else lock one");
				}
			}
			}
		}
	}
}

class Locks{
	public static final Object LOCK_ONE=new Object();
	public static final Object LOCK_TWO=new Object();
}
//同步代码块嵌套,锁不同,两线程各自需要对方占用的锁,造成死锁
public class DeadLockDemo {

	public static void main(String[] args){

		DeadPool deadpool=new DeadPool();
		new Thread(deadpool).start();
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		deadpool.flag=false;
		new Thread(deadpool).start();//只要用同样两个锁,即使两个线程执行的是不同的实现Runnable的对象也会出现这个死锁问题!这和多线程操作同一个资源没有关系!!
	}
}//下翻看下面的执行结果和分析:
/**
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
if lock one
if lock two
else lock two-----------------------结果:else锁住lock two,if锁住lock one,各自再也进不去内层,也都没结束,死锁!
if lock one

 * @author Administrator
 *
 */
时间: 2024-10-25 21:02:05

[Java]多线程复习(更新未完)的相关文章

Java基本概念(未完)

仅简单总结~辅助快速回忆~ 一.JVM 1,Java类加载机制 Java程序由多个类文件组成,按需加载. Java的动态扩展是由运行期动态加载和动态链接实现的.——动态绑定,多态. 加载步骤: 1)装载:查找和导入Class文件. a) 根据一个类的全限定名来获取二进制字节流 b) 将这个字节流代表的静态存储结构转化为方法区的运行时数据结构. c)在Java堆中生成一个代表这个类的Class对象,作为方法区数据的访问入口. 2)链接:把类的二进制数据合并到JRE中. a) 校验:检查Class文

Java开发记事本(未完待续)

右键菜单图 打印图 设置背景颜色.字体样式.字体颜色图 主要 package com.sxt.nodepad.view; import java.awt.BorderLayout; public class NotepadMainFrame extends JFrame implements ActionListener{ /** * 序列号 */ private static final long serialVersionUID = 8585210209467333480L; private

java 集合类复习(未完结)

JAVA常用数据结构及原理分析(面试总结) https://blog.csdn.net/qq_29631809/article/details/72599708 java 中几种常用数据结构   https://blog.csdn.net/u010947402/article/details/51878166 1 java 集合 2 用到的数据结构 3 Collections 的工具 4 集合比较,线性安全 5 堆排序 常见算法  堆实现队列 6 树 二叉树  层次遍历 原文地址:https:/

c++复习(未完待续)

1.使函数不能在定义该函数的文件之外访问的方法: (1)声明函数为static(2)将函数放到无名名字空间中 namespace { void g() { ......... } }

java 堆问题分析(未完)

一 shallow heap 和 retained heap的区别 1 shallow size 是对象本身占据的内存的大小,不包含其引用的对象.对于常规对象(非数组)的shadow size由其成员变量的数量和类型来定,而数组的shallow size则是数组元素大小的总和. 2 retained size Retained Size=当前对象大小 + 当前对象可直接或间接引用到的对象的大小总和.(间接引用的含义:A->B->C,C就是间接引用) ,并且排除被GC Roots直接或者间接引用

Java 多线程下载示例

项目结构: FileDownload.java: package com.wl.download; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.text.DecimalFormat; import org.apache.http.HttpResponse; import org.

40个Java多线程问题详解复习

1.多线程有什么用? 一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡.所谓"知其然知其所以然","会用"只是"知其然","为什么用"才是"知其所以然",只有达到"知其然知其所以然"的程度才可以说是把一个知识点运用自如.OK,下面说说我对这个问题的看法: (1)发挥多核CPU的优势 随着工业的进步,现在的笔记本.台式机乃至商用的应用服务器

你需要了解的多线程知识(JAVA ) 复习

Volatile 关键字 Volatile 是java虚拟机提供的轻量级同步机制(保证可见性,不保证原子性,禁止指令重排) 可见性之前需要了解 JVM(java虚拟机) JMM(java内存模型) javamemory model 不真实存在描述的一种规则规范 定义了程序中各个变量(包括实例字段静态字段和构成数组对象的元素)的访问方式 JMM的同步的规定 1 线程解锁前必须把共享变量的值刷新回主内存 2 线程加锁前必须读取主内存的最新值到自己工作内存 3 加解锁是同一把锁 可见性:举个栗子 比如

Java高级复习㈠ 多线程

1.线程 也称为轻量级进程,每个线程有自己的堆栈.程序计数器.局部变量.与进程相比,进程中的线程之间隔离程度要小 2.Java实现 ①implements Runnable ②extends Thread 一般推荐① 3.sleep().yield() sleep()方法对当前线程操作,指定以毫秒为单位的线程最小休眠时间,除非线程因中断提早恢复执行,否则不会在这段时间前恢复执行.线程可调用另一线程的interrupt()方法,向暂停的线程发出InterruptedException,变相唤醒暂停