多线程

多线程

一、线程和进程

1)进程:是一个在执行中的程序。每一个进程都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。

2)线程:就是进程中的一个独立的控制单元。线程在控制着进程的执行。

3) 理解:一个进程中,至少有一个线程。在JVM 启动的时候会有一个进程叫java.exe该进程中,至少有一个线程,在负责 java 程序的执行,而且这个线程,运行的代码存在于 main 方法中,该线程称之为主线程。其实,JVM 中更细节的说明了虚拟机不止一个线程,包含了一个主线程和一个负责控制垃圾回收机制的线程。通过API的查找,java已经提供了对线程这类事物的描述,就是 Thread 类。

4)多线程的随机性:因为多个线程都获取 cpu 的执行权。 cpu 执行到谁,谁就运行。但是要明确的是:在某一时刻,只能有一个程序在运行。(多核除外。)CPU 在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象的把多线程的运行形容为在互相抢夺 CPU 的资源(执行权)我们可以形象的把多线程的运行形容为在互相抢夺 CPU 的资源(执行权)这就是多线程的特性,谁抢到谁执行。至于执行多长时间。CPU 说的算。

5)覆盖 run 方法的说明:Thread 类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是 run 方法。也就是说 Thread 类中 run 方法,用于存储线程需要运行的代码。所以说自定义线程,就是把代码放在run方法中。

6)线程都有自己的默认名称。Thread-编号,该编号从0开始。

currentThread:获取当前线程对象,这是一个静态的线程可以直接调用。

Ps:static Thread currentThread()

getName():获取线程的名称。

setName():设置线程名称,setName或者通过构造函数。

二、线程的生命周期

1)运行过程:

被创建 start()-->运行-- sleep(time)   --> 冻结(:放弃执行资格)

 运行-- sleep时间到  <-- 冻结(:放弃执行资格)

   运行-- wait()               --> 冻结(:放弃执行资格)

   运行-- notify()             <-- 冻结(:放弃执行资格)

2)消亡:stop() 就是run方法中的结束。

3) 说明:没有资格执行的状态冻结状态当wait状态的时候,线程冻结了,没办法自动重启,这时候可以使用notify方法唤醒线程。

4)临时阻塞:具备运行资格,但是没有执行权。

线程的图解:


三、创建线程的两种方式

总结两种方式的区别:

继承 Thread :线程代码存放在 Thread 子类 的 run 方法中。

实现 Runnable:线程代码存放在接口的子类的 run 方法中。

方式一:继承 Thread 类。

步骤:

1、定义类继承 Thread 。

2、复写 Thread 类中的 run 方法。目的:将自定义的代码存储在 run 方法中,让线程运行。

3、调用线程的 start 方法。该方法有两个作用,启动线程,调用run方法。

简单的继承 Demo:

package study.part1.day009.thread;
public class ThreadDemo2 {
	public static void main(String[] args) {
		Test t1 = new Test("t1");
		Test t2 = new Test("t2");
		t1.start();
		t2.start();
		/**
		直接调用 run 方法,没有线程的随机性。
		t1.run();
		t2.run();
		*/
		for (int i = 0; i < 60; i++) {
			System.out.println("main----->"+i);
		}	
	}
}
class Test extends Thread{
	//private String name;
	//不需要再自定义传入线程名称,因为父类已经干好了。所以写一个super就够了
	Test(String name){
		//this.name = name;
		super(name);
	}
	/**
	 * 复写 run 方法
	 */
	public void run(){
		for (int i = 0; i < 100; i++) {
			System.out.println(Thread.currentThread().getName()+"---"+this.getName()+"--Test Run:"+i);
		}
	}
}

方法:实现 Runnable 接口

步骤:

1、定义类实现 Runnable 接口。

2、覆盖 Runnable 接口中的 Run 方法。将线程要运行的方法存放在该 run 方法中。

3、通过 Thread 类建立线程对象。

4、将 Runnable 接口的子类对象作为实际参数传递给 Thread 类的构造函数。

Ps:为什么要将 Runnable接口的子类对象传递给 Thread 的构造函数。因为,自定义的 run 方法所属的对象是Runnable 接口的子类对象,所以要让线程去执行指定对象的 run 方法。就必须明确该 run 方法所属的对象。

5、调用 Thread 的 start 方法开启线程,并调用 Runnable接口子类的 run 方法。 

四、多线程的安全问题

java 对于多线程的安全问题,提供了专业的解决方式。就是同步代码块。

好处:解决了多线程的安全问题。

弊端:多个线程需要判断锁,较为消耗资源。

1)最常用的线程使用方法:implements,就是用实现的方法写线程Thread

2)运行出现安全问题的原因当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完。另一个线程参与了进来执行,导致了共享

数据的错误。

3)解决方法:在对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其它线程不可以参与执行。java 对于多线程的安全问题,提供了专业的解决方式。就是同步代码块。

4)同步的前提:

1、必须要有两个或者两个以上的线程才需要synchroized锁上。

2、必须是多个线程同时使用一个锁。

5)实现方式:

synchronized(对象){

需要被同步的代码//同步带共享操作的数据,如if的判断tick开始。

}

对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程,即使获取了 CPU 的执行权,也进不去,因为没有获取锁

6)同步代码块的 Demo:

package study.part1.day009.thread;
public class ThreadDemo4 {
	public static void main(String[] args) {
		Ticket2 t = new Ticket2();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}
class Ticket2 implements Runnable{
	private int tick = 100;	
	Object obj = new Object();		
	@Override
	public void run(){
		while(true){
			//synchronized:监视器,锁:确定当前只有一个线程在执行。
			/**
			synchronized(obj){			
				if(tick>0){
						try {
							Thread.sleep(10);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
	System.out.println(Thread.currentThread().getName()+"---sale:"+tick--);
				}
			}*/
			this.show();
		}
	}
	public synchronized void show(){
		if(tick>0){
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"---sale:"+tick--);
		}
	}
}

7)银行金库 Demo:

需求:银行有一个金库。有两个储户分别存300员,每次存一百。存3次。

目的:该程序是否有安全问题:有的

如何找问题:

1、明确那些代码是多线程运行的代码。

2、明确共享数据。

3、明确多线程代码中哪些语句是操作共享数据的。

同步函数用的是哪一个锁呢?函数需要被对象调用。那么函数都有一个属性对象引用。就是this

所以同步函数使用的锁是this 。

package study.part1.day009.thread;
public class ThreadDemo5 {
	public static void main(String[] args) {
		Cust c = new Cust();
		Thread t1 = new Thread(c);
		Thread t2 = new Thread(c);
		t1.start();
		t2.start();
	}
}
class Bank{
	private int sum;
	Object o = new Object();
	public synchronized void add(int n){
		//synchronized(o){//可以写到方法上:同步代码块,2同步函数
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		sum = sum+n;
		System.out.println("sum="+sum);
	}
}
class Cust implements Runnable{
	private Bank b = new Bank();
	@Override
	public void run(){
		for (int i = 0; i < 3; i++) {
			b.add(100);
		}
	}
}

8)线程的死锁,同步中嵌套同步

package study.part1.day009.thread;
public class ThreadDeadLockDemo {
	public static void main(String[] args) {
		TestDemo tdt = new TestDemo(true);
		TestDemo tdf = new TestDemo(false);
		Thread t1 = new Thread(tdt);
		Thread t2 = new Thread(tdf);
		t1.start();t2.start();
	}
}
class TestDemo implements Runnable{
	private boolean flag;
	TestDemo(boolean flag){
		this.flag = flag;
	}
	public void run(){
		if(flag){
			synchronized (MyLock.locka) {
				System.out.println("if locka");
				synchronized (MyLock.lockb) {
					System.out.println("if lockb");
				}
			}
		}else{
			synchronized (MyLock.lockb) {
				System.out.println("else lockb");
				synchronized (MyLock.locka) {
					System.out.println("else locka");
				}
			}
		}
	}
}
class MyLock{
	static Object locka = new Object();
	static Object lockb = new Object();
}
class MyLock2{
	static Object locka = new Object();
	static Object lockb = new Object();
}
class TestDemo2 implements Runnable{
	private boolean flag;
	TestDemo2(boolean flag){
		this.flag = flag;
	}
	@Override
	public void run() {
		if(flag){
			synchronized(MyLock.locka){
				System.out.println("locka");
				synchronized(MyLock.lockb){
					System.out.println("lockb");
				}
			}
		}
		else{
			synchronized(MyLock.lockb){
				System.out.println("lockb");
				synchronized(MyLock.locka){
					System.out.println("locak");
				}
			}
		}
	}
}

9)静态的同步方法

如果同步函数被静态修饰后,使用的是什么锁呢?

通过验证发现不是 this ,因为静态方法中也不可以定义 this 静态进内存时,内存中没有本类对象,但是一定有该类对象的字节码文件对象。

类名.class 该对象的类型是 class

静态的同步方法,使用的锁是该方法所在类的字节码文件对象。也就是类名.class

类名.class 在内存中是唯一的。

静态的同步代码Demo:

package study.part1.day009.thread;
public class ThreadDemo6 {
	public static void main(String[] args) {
		Ticket6 t6 = new Ticket6();
		Thread t1 = new Thread(t6);
		Thread t2 = new Thread(t6);
		t1.start();
		t2.start();
	}
}
class Ticket6 implements Runnable{
	private static int tick = 100;
	boolean flag = true;
	@Override
	public void run() {
		if(flag){
			while(true){
				synchronized (Ticket6.class) {
					if(tick>0){
						try {
							Thread.sleep(10);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println(Thread.currentThread().getName()+"----code:"+tick--);
					}
				}
			}
		}else{
			while(true){
				show();
			}
		}
	}
	public static synchronized void show(){
		if(tick>0){
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"...show:"+tick--);
		}
	}
}

五、线程间的通信之等待唤醒机制

1)线程间的通:其实就是多个线程在同时操作一个资源,但是操作的动作不同。

2)等待唤醒机制:其实就是 wait 方法和 notify 方法的使用。

使用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。

3)为什么这些操作线程的方法要定义在Object类中呢?

因为这些方法在操作同步线程时,都必须要标识他们所操作线程的锁。只有同一个锁上的被等待线程,可以被同一个锁 notify 唤醒,不可以对不同锁中的线程唤醒。也就是说,等待和唤醒必须是同一个锁。而锁,可以是任意对象,所以可以被任意对象调用的方法定义在 Object 中。

等待唤醒机制示例:

package study.part1.day010.threadtx;
public class ThreadTX2 {
	public static void main(String[] args) {
		Res1 r1 = new Res1();
		new Thread(new Input1(r1)).start();
		new Thread(new Output1(r1)).start();
		
	}
}
class Res1{
	private String name;
	private String sex;
	private boolean flag;
	public synchronized void set(String name , String sex){
		if(flag){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
			this.name = name;
			this.sex = sex;
			flag = true;
		this.notify();	
	}
	public synchronized void out(){
		if(!flag){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(name+"---"+sex);
			flag = true;
		this.notify();	
		
	}
}
class Input1 implements Runnable{
	private Res1 res;
	Input1(Res1 res){
		this.res = res;
	}
	@Override
	public void run() {
		int x = 0;
		while(true){
				if(x==0){
					res.set("mike", "man");
				}else{
					res.set("raven", "woman");
				}
				x = (x+1)%2;	//让x变成1;
		}
	}
}
class Output1 implements Runnable{
	private Res1 res;
	Output1(Res1 res){
		this.res = res;
	}
	@Override
	public void run() {
		while(true){
			res.out();
		}
	}
}

等到了运行的结果:

 

六、全部唤醒

对于多个生产者和消费者,就必须用 while + notifyAll(唤醒全部)

1)为什么要用 while 判断标记呢?

原因:让被唤醒的线程再一次判断标记。

2)为什么定义 notifyAll ?

因为唤醒自己的同时还要唤醒对方的线程。只用 notify ,容易出现只唤醒本方线程的情况。导致程序中的所有线程都在等待。这也是比较有用的方法。 

示例:

package study.part1.day010.threadtx;
public class ThreadTX3 {
	public static void main(String[] args) {
		Resource reso = new Resource();
		new Thread(new Producer(reso)).start();
		new Thread(new Producer(reso)).start();
		new Thread(new Custmer(reso)).start();
		new Thread(new Custmer(reso)).start();
	}
}
class Resource{
	private String name;
	private int count = 1;
	private boolean flag = false;
	public synchronized void set(String name){
		while(flag){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.name = name+"--"+count++;
		System.out.println(Thread.currentThread().getName()+"--生产--"+this.name);
		this.flag = true;
		this.notifyAll();		//全唤醒
	}
	public synchronized void out(){
		while(!flag){
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(Thread.currentThread().getName()+"--消费-----"+this.name);
		this.flag = false;
		this.notifyAll();
	}
}
/**
 * 生产者
 */
class Producer implements Runnable{
	private Resource reso;
	Producer(Resource reso){
		this.reso = reso;
	}
	@Override
	public void run() {
		while(true){
			reso.set("+商品+");
		}
	}
}
class Custmer implements Runnable{
	private Resource reso ;
	Custmer(Resource reso){
		this.reso = reso;
	}
	@Override
	public void run() {
		while(true){
			reso.out();
		}
	}
}

运行结果:

 

七、另一种锁 Lock

线程间的通信另一种锁的方法。在 JDK 1.5 中提供的多线程的升级解决方案:

1)将同步的synchronized替换成实现Lock操作。

2)将Object中的wait,notify,notifyAll,替换成condition对象。该对象可以 Lock 锁进行获取。

3)一个lock上可以多个相关的condition

4) Demo:

package study.part1.day010.threadtx;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadTX5 {
	public static void main(String[] args) {
		Resource3 reso = new Resource3();
		System.out.println("xxx");
		new Thread(new Producer3(reso)).start();
		new Thread(new Producer3(reso)).start();
		new Thread(new Custmer3(reso)).start();
		new Thread(new Custmer3(reso)).start();
		System.out.println("xx");
	}
}
class Resource3{
	private String name;
	private int count = 1;
	private boolean flag = false;
	private Lock lock = new ReentrantLock();
	private Condition condition_pro = lock.newCondition();
	private Condition condition_cus = lock.newCondition();
	public void set(String name){
		lock.lock();
		while(flag){
			try {
				condition_pro.await();
				this.name = name+"--"+count++;
				System.out.println(Thread.currentThread().getName()+"--生产--"+this.name);
				flag = true;
				condition_cus.signal();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}finally{
				lock.unlock();		//解锁的动作一定要执行。
			}
		}
	}
	public void out(){
		lock.lock();
		try {
			while(!flag){
			condition_cus.await();
			System.out.println(Thread.currentThread().getName()+"--消费-----"+this.name);
			flag = false;
			condition_pro.signal();
			}		
		}catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
		
	}
}
/**
 * 生产者
 */
class Producer3 implements Runnable{
	private Resource3 reso;
	Producer3(Resource3 reso){
		this.reso = reso;
	}
	@Override
	public void run() {
		while(true){
			reso.set("+商品+");
		}
	}
}
class Custmer3 implements Runnable{
	private Resource3 reso ;
	Custmer3(Resource3 reso){
		this.reso = reso;
	}
	@Override
	public void run() {
		while(true){
			reso.out();
		}
	}
}

八、停止线程

1) JDK 1.5 后,停止线程的 stop 方法已经过时,那如何停止线程呢?

只有一种 run 方法结束。

2) 开启多线程运行,运行代码通常是循环结构。只要控制循环,就可以让 run 方法结束,也就是线程结束。

3)特殊情况:当线程处于冻结状态。就不会读取标记,那么线程就不会结束。

处理方法:强制中断线程:interrupt

当没有指定的方式让冻结的线程回复到运行的状态时,这时需要对冻结进行清除。强制让线程恢复到运行状态来,实际上就是获取运行资格。这样就可以操作标记,让线程结束。

守护线程,setDaemon(boolean b);

4) 扩展知识之join()特点:

当 A 线程执行到了 B 线程的 join 方法时,A 线程就会等待,等 B 线程都执行完,A 才会执行。join 可以用来临时加入线程执行。

强制中断线程的 Demo:

package study.part1.day010.threadtx;
public class ThreadTX6 {
	public static void main(String[] args) {
		StopThread st = new StopThread();
		Thread t1 = new Thread(st);
		Thread t2 = new Thread(st);
		t1.start();
		t2.start();
		/**
		try {
			t1.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}*/
		//t1.setDaemon(true);
		int num = 0;
		while(true){
			if(num++==60){
				t1.interrupt();
				t2.interrupt();
				st.changeFlag();
				break;
			}
			System.out.println(Thread.currentThread().getName()+"----"+num);
		}
	}
}
class StopThread implements Runnable{
	private boolean flag = true;		//falg	标记
	@Override
	public void run() {
		while(flag){
			System.out.println(Thread.currentThread().getName()+"---run");
			Thread.yield();
		}
	}
	 public void changeFlag()  
	    {  
	        flag = false;  
	    }
}

运行结果:

 

九、JDK1.5后的新特性:关于 StirngBuilder 的线程安全讲解。

StringBuffer 和 StringBuilder

1)StringBuffer:线程安全的可变字符序列 ,是字符串缓冲区,是一个容器。

1、长度是可变的。

2、可以操作多个数据类型。

3、最终通过toString()方法变成字符串。

2) CURD   create update read delete

1、存储。

StringBuffer  append();

StirngBuffer  insert(index,数据):可以将数据插入到指定index位置。

2、删除。

StringBuffer  delete(start,end):删除缓冲区的数据,包含start,不包含end

deleteCharAt(index):删除指定位置的字符。

3、修改。

StringBuffer replace(start,end,str)

void setCharAt(index,char)

  4、将缓冲区中的数据指定到

3)StringBuilder:JDK 1.5之后出现。

4)StringBuffer 和 StringBuilder 的对比。

StirngBuffer: 线程同步的。线程安全。多线程使用

StringBuilder:线程不同步的。线程不安全。单线程使用

5) 升级 StringBuilder 因素:

1、提高效率。

2、简化书写。

3、提高安全性。

6) Demo:

package study.base.day010.ThreadTX;
public class StringBufferDemo1 {
	public static void main(String[] args) {
		SB01();
		SB02();
		SB03();
	}
	public static void SB01(){
		StringBuffer sb = new StringBuffer();
		sb.append("as");
		sb.append(true);
		sb.append('a');
		sb.append(5);
		sb.insert(2, "xxx");
		sc(sb.toString());
	}
	/**
	 * delete
	 */
	public static void SB02(){
		StringBuffer sb = new StringBuffer("adfsadf");
		sb.delete(1, 3);		//删除了‘df’
		sc(sb.toString());
		sb = new StringBuffer("abcdefg");
		sb.deleteCharAt(3);
		sc(sb.toString());
		//清空缓冲区示例:
		//sb.delete(0, sb.length());
	}
	public static void SB03(){
		StringBuffer sb = new StringBuffer("ABCDEFG");
		sb.replace(1, 3, "XXOO");
		sc(sb.toString());
		sc(sb.reverse().toString());
	}
	public static void sc(String str){
		System.out.println(str);
	}	
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
未来社区的建设背景和需求分析指出,随着智能经济、大数据、人工智能、物联网、区块链、云计算等技术的发展,社区服务正朝着数字化、智能化转型。社区服务渠道由分散向统一融合转变,服务内容由通用庞杂向个性化、服务导向转变。未来社区将构建数字化生态,实现数据在线、组织在线、服务在线、产品智能和决策智能,赋能企业创新,同时注重人才培养和科研平台建设。 规划设计方面,未来社区将基于居民需求,打造以服务为中心的社区管理模式。通过统一的服务平台和应用,实现服务内容的整合和优化,提供灵活多样的服务方式,如推送式、订阅式、热点式等。社区将构建数据与应用的良性循环,提高服务效率,同时注重生态优美、绿色低碳、社会和谐,以实现幸福民生和产业发展。 建设运营上,未来社区强调科学规划、以人为本,创新引领、重点突破,统筹推进、整体提升。通过实施院落+社团自治工程,转变政府职能,深化社区自治法制化、信息化,解决社区治理中的重点问题。目标是培养有活力的社会组织,提高社区居民参与度和满意度,实现社区治理服务的制度机制创新。 未来社区的数字化解决方案包括信息发布系统、服务系统和管理系统。信息发布系统涵盖公共服务类和社会化服务类信息,提供政策宣传、家政服务、健康医疗咨询等功能。服务系统功能需求包括办事指南、公共服务、社区工作参与互动等,旨在提高社区服务能力。管理系统功能需求则涉及院落管理、社团管理、社工队伍管理等,以实现社区治理的现代化。 最后,未来社区建设注重整合政府、社会组织、企业等多方资源,以提高社区服务的效率和质量。通过建立社区管理服务综合信息平台,提供社区公共服务、社区社会组织管理服务和社区便民服务,实现管理精简、高效、透明,服务快速、便捷。同时,通过培育和发展社区协会、社团等组织,激发社会化组织活力,为居民提供综合性的咨询和服务,促进社区的和谐发展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值