多线程我所知道的

声明:学基础,在校学生,本文所有内容来自书本和视频,然后通过自己的理解和筛选编写而来,如有理解不到位的写得不到位的地方,欢迎评论指错!!!(仅做学习交流)
笔者:Fhvk
微信:WindowsC-

什么是多线程

  • 线程是程序的执行路径,一个进程中可以包括多条线程
  • 什么是进程,如下图:
    在这里插入图片描述
  • 多线程并发执行可以提高程序的执行效率,可以同时完成多项目工作,例如使用电脑管家同时进行电脑清理和电脑加速,这就属于多线程;
  • 多线程的应用场景
    1、比如qq和多个人多天
    2、比如果百度网盘同时下载多个文件
    3、服务器处理多个请求等

多线程的原理:多线程说是说同时执行,但其实底层CPU执行还是一条一条执行,只是CPU执行速度非常之快。达到了多线程的效果,但现在电脑都已经达到双核和多核同时执行了。

多线程的并行和并发

  • 并行:就是两个任务同时执行,就是任务1 在执行,任务2也在执行,前提是多核CPU
  • 并发:是指两个任务同时请求执行,而CPU只能执行接收一个任务,就把这两个任务轮流执行,由于执行速度快,时间间隔短,使用户感觉同时执行;

Java程序运行原理和JVM的启动是多线程的吗

  • Java程序运行原理:Java命令会启动JVM,等同于启动一个程序,也就是启动了一个进程,该进程会自动启动一个"主线程",然后主线程去调用某个类的main方法;
  • JVM启动是多线程的吗?很明显是,finalize()方法是在垃圾回收的方法,由JVM自动调用。
/**
 * 证明JVM是多线程的
 * @author fhvk.game
 *  */
public class Demo1_Thread {
	public static void main(String[] agrs) {
		for(int i = 0; i < 1000000; i++) {
			new Demo();  
			/*当垃圾达到了一定的数量,
			 会启动垃圾回收线程会自动调用finalize方法进行垃圾回收*/
		}
		//然后main本来就是一条线程,主线程
		for(int j = 0; j < 10000; j++) {
			System.out.println("你好我是主线程");
		}
		
	}
}
class Demo {
	//重写finalize只输出
	public void finalize() {
		System.out.println("垃圾被清理");
	}
}
输出结果:
你好我是主线程
你好我是主线程
你好我是主线程
垃圾被清理
垃圾被清理
你好我是主线程
你好我是主线程
垃圾被清理
垃圾被清理

多线程2种实现方式

  • 继承Thread
    1、定义类继承Thread;
    2、重写run()方法
    3、将要执行的语句写在run()方法中
    4、创建Thread子类实例对象
    5、通过start()方法开启线程
public class Demo1_Thread {
	public static void main(String[] agrs) {
		Demo d = new Demo();
		d.start();
	}
}
class Demo extends Thread{
	public void run() {
		for(int i = 0; i < 1000; i++) {
				Syetm.out.println("我是Thread"):
		}
	}
}
  • 实现Runnable接口
    1、定义类实现Runnable接口
    2、重写run()方法
    3、执行语句放run()方法中
    4、创建Runnable实现类对象
    5、创建Thread对象
    6、将Runnable实现类对象传到Thread构造中
    7、通过Thread对象start()方法启动线程
public class Demo_Runnable {
	public static void main(String[] agrs) {
		RunnableDemo rd = new RunnableDemo();
		Thread t = new Thread(rd);
		t.start(); 
	}
}
class RunnableDemo implements Runnable {
	public void run() {
		System.out.println("我是Runnable");
	}
}
  • 实现Runnable的原理

看Thread的构造方法,传递了Runnable接口的引用,通过其中init()方法找到传递的target给成员变量target赋值,查看Thread中run()方法,发现run方法中有判断,如果target不为null就会调用Runnable接口子类对象的run()方法

  • 两种多线程实现方式的区别

查看源代码的区别
1、Thread:子类继承Thread,由于重写了run()方法当调用start()方法时,直接找子类run()方法
2、Runnable:通过Thread构造将Runnable引用传给Thread成员变量target(其中是通过init()方法传的),然后run()方法会有判断不为null就调用了成员变量(Runnable引用)的run()方法
3、使用Thread类操作多线程的时候无法达到资源共享的目的,而使用Runnale接口实现的多线程操作可以实现资源共享;

  • 两种方式的好处和坏处

继承Thread的好处:可以直接使用Thread类中的run()方法,代码简单;
继承Thread的坏处:如果类已经有了父类,就不能使用此方法,因为JAVA是单继承
实现Runnable接口的好处:尽管自己定义的类有了父类,也可以使用线程,因为有了父类也可以实现接口;而且还可以多实现,资源共享,代码程数据是独立的 ;
实现的Runnable的坏处:不能直接继承Thread的run()方法,得通过创建Thread类有参构造对象将实现Runnable的类引用传给有参Thread构造,然后调用该Thread类对象start()方法开启线程;

匿名内部类实现多线程两种方式

public class Demo {
	public static void main(String[] agrs) {
		//第一种继承Thread
		new Thread() {
			public void run() {
				语句;
			}
		}.start();
		//第二种实现Runnable
		new Thread(new Runnable() {
			public void run() {
					语句;
			}
		}).start();
	} 
} 

- 获取和设置线程名称(getName&setName)

1、通过getName()方法获取线程名称
2、通过构造和setName()可以设置线程名称
代码如下:

public class Demo {
	public static void main(String[] agrs) {
		///通过构造设置线程名称
		new Thread("线程1") {
			public void run() {
				System.out.println(this.getName());
			}
		}.start();
		//通过setName()设置线程名称
		Thread t = new Thread() {
			public void run() {
				System.out.println();
			}
		};
		t.setName("线程2");
		System.out.println(t.getName());
	}
}

获取当前执行线程的引用(currentThread)

public class Demo6_Thread {
	public static void main(String[] agrs) {
		new Thread() {
			public void run() {
				System.out.println(getName());
			}
		}.start();
		new Thread(new Runnable() {
			public void run() {
				//通过类名点调用currentThread();获取当前执行线程对象的引用;
				System.out.println(Thread.currentThread().getName());
			}
		}).start();
		//获取主线程的引用,也可以设置
		System.out.println(Thread.currentThread().getName());
	}
}

休眠线程(sleep)

  • Thread.sleep(long millis);静态方法,此方法休眠当前执行线程,指定时间继续执行;类名点调用; 1秒 = 1000毫秒;
public class Demo7_Sleep {
	public static void main(String[] agrs) {
		new Thread() {
			public void run() {
				while(true)
					SimpleDateFormat sdf = new SimpleDateFormat(
									"yyyy年MM月dd日 HH:mm:ss");
					System.out.println(sdf.format(new Date()));
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}.start();
	}
}
 

守护线程(setDaemon)

  • setDaemon(),设置一个线程为守护线程,那么该线程不会单独执行,当其它线程非守护线程执行结束后,就会自动退出;
public class Demo9_SetDaemon {
	public static void main(String[] agrs) {
		new Thread() {
			public void run() {
				for(int i = 0; i < 2; i++) {
					System.out.println(getName() + " : 你好");
				}
			}
		}.start();
		Thread t = new Thread(new Runnable() {
			public void run() {
				for(int i = 0; i < 50; i++) {
					System.out.println(Thread.currentThread() + 
												" : 不好" + i);
				}
			}
		});
		t.setDaemon(true);
		t.start();
	}
}

加入线程(join)

  • join():当前线程暂停,等到指定线程执行完,当前线程继续执行;
  • join(long millis):当前线程暂停,等待指定线程执行指定时间后,当前线程继续执行;
public class Demo10_JoIn {
	public static void main(String[] agrs) {
		//注意如果匿名内部类想访问他所在方法中的局部变量时,该局部变量必须用final修饰;
		final Thread t1 = new Thread() {
			public void run() {
				for(int i = 0; i < 1000; i++) {
					System.out.println(i + "我是加入线程 : " + getName());
				}
			}
		};
		Thread t2 = new Thread() {
			public void run() {
				for(int i = 0; i < 1000; i++) {
					if(i == 5) {
						try {
							t1.join(10);
							//该线程指定执行时间1秒;过了时间后两条线程交替执行;
							//t1.join(1000);  
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
					System.out.println( i + "我是线和 : " + getName());
				}
			}
		};
		t1.start();
		t2.start();
	}
}

礼让线程

  • yield()方法让出cpu
public class Demo11_YieLd {
	public static void main(String[] agrs) {
		new MyThread().start();
		new MyThread().start();
	}
}
class MyThread extends Thread {
	public void run() {
		for(int i = 0; i < 1000; i++) {
			if(i % 10 == 0) {   //当是10的倍数时
				this.yield();    //让出cpu
			}
			System.out.println(getName() + " : " + i):
		}
	}
}

线程的优先级

  • setPriority(int newPriority)方法:设置线程优先级;
  • newPriority:最大取值10,最小取值1;
  • getPriority()方法获取当前线程优先级
  • main线程优先级是:5
public class Demo12_SetPriority {
	public static void main(String[] agrs) {
		Thread t1 = new Thread() {
			public void run() {
				for(int i = 0; i < 1000; i++) {
						System.out.println(getName() + " : "  + i);
				}
			}
		}.start();
		Thread t2 = new Thread(new Runnable() {
			public void run() {
				for(int i = 0; i < 1000;  i++) {
					System.out.println(Thread.currentThread() + " : " + i);
				}
			}
		}).start();
		t1.setPriority(10);
		t2.setPriority(1);
		t1.start();
		t2.start();
	}
}

判断线程是否启动

  • 在JAVA中可以使用isAlive()方法来测试线程是否已经启动而且仍然在启动;
public class Demo_IsAlive {
	public static void main(String[] agrs) {
		Thread t = new Thread() {
			public void run() {
				for(int i = 0; i < 3; i++) {
					System.out.println("线程运行 i "  + i);
				}
			}
		};
		System.out.println("启动前 : " + t.isAlive());
		t.start();
		System.out.println("启动后 : " + t.isAlive());
		for(int i = 0; i < 3; i++) {
			System.out.println("main运行 " + i);
		}
		System.out.println("启动前 : " + t.isAlive());
	}
}

中继线程

  • interrupt():中断当前线程
public class Demo_Interrupt {
	public static void main(String[] agrs) {
		Thrread t = new Thread() {
			public void run() {
				System.out.println("进入run()方法");
				try{
					Thread.sleep(1000);
					System.out.println("休眠结束");
				}catch(InterrruptedException e) {
					System.out.println("休眠被终止");
					return;
				}
				System.out.println("run()方法正常结束");
			}
		};
		t.start();
		t.interrupt();//线程中断执行
	}
}

案例

/**
 *要求:设计一个线程类,要求产生三个线程对象,并可以分别设置三个线程的休眠时间,如下所示
 *线程a:休眠10秒
 *线程b:休眠20秒
 *线程c:休眠30秒
 * @author MAC
 *
 */
public class Test6 {
	public static void main(String[] agrs) {
		//demo1();
		MyRunnableTest mrt = new MyRunnableTest(10, "线程a");
		Thread t1 = new Thread(mrt,mrt.getNameTest());
		t1.start();
	}

	private static void demo1() {
		MyThread m1 = new MyThread("线程a",10);
		MyThread m2 = new MyThread("线程b",20);
		MyThread m3 = new MyThread("线程c",30);
		m1.start();
		m2.start();
		m3.start();
	}
} 
//继承Thread
class MyThread extends Thread {
	private long time;
	public MyThread(String name,long time) {
		super(name);
		this.time = time;
	}
	public void run()  {
		try {
			Thread.sleep(this.time * 1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(this.currentThread().getName() + " : 休眠了" + this.time + "秒");
		System.out.println(this.currentThread().getName() + " : 开始执行了");
	}
}
class MyRunnableTest implements Runnable {
	private int time;
	private String name;
	public MyRunnableTest(int time,String name) {
		this.time = time;
		this.name = name;
	}
	@Override
	public void run() {
		// TODO Auto-generated method stub
		try {
			Thread.sleep(this.time * 1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " : 休眠了" + this.time + "秒");
		System.out.println(Thread.currentThread().getName() + " : 开始执行了");
	}
	public String getNameTest(){
		return this.name;
	}
}

多线程的同步代码块

  • 同步代码块:使用synchronized关键字加上一个锁对象来定义一块代码,这就叫同步代码块;多个同步代码块如果使用相同锁对象,那么他们就是同步的;
  • 什么情况下使用同步代码块:当多线程并发时,有多块代码同时执行时,我们希望某一块代码执行过程中,CPU不要切换到其他线程工作,这时就需要同步;如果两块代码是同步的,那么在同一时只能执行一块,在一块代码没执行结束之前,不会执行另一块
public class Demo3_Synchronized {
	public static void main(String[] agrs) {
		final PrinterTest pt = new PrinterTest();
		new Thread() {
			public void run() {
				pt.print1();
			}
		}.start();
		new Thread() {
			public void run() {
				pt.print2();
			}
		}.start();
	}
}
class PrinterTest {
	Object o = new Object();
	public void print1() {
		synchronized(o) {     //注意不能是匿名内部类
			for(int i = 0; i < 1000; i++) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.print("李");
				System.out.print("超");
				System.out.print("武");
				System.out.print("\r\n");
			}
		}
	}
	public void print2() {
		synchronized(o) {
			for(int i = 0; i < 1000; i++) {
				System.out.print("陈");
				System.out.print("清");
				System.out.print("香");
				System.out.print("\r\n");
			}
		}
	}
}

多线程的同步方法

  • 使用synchronized修饰的方法,该方法中所有的代码都是同步的;
  • 关于锁:修饰非静态方法,该同步代码块的锁对象是this;修改静态方法,该同步代码块的锁对象是类名.class(字节码对象);
public class Demo4_Synchronized {
	public static void main(String[] agrs) {
		final Printer3 p3 = new Printer3();
		new Thread() {
			public void run() {
				p3.print1();
			}
		}.start();
		new Thread() {
			public void run() {
				p3.print2();
			}
		}.start();
	} 
}
class Printer3 {
	public synchronized void print1() {
		for(int i = 0; i < 1000; i++) {
			System.out.print("李");
			System.out.print("超");
			System.out.print("武");
			System.out.print("帅");
			System.out.print("哥");
			System.out.print("\r\n");
		}
	}
	public  void print2() {
		synchronized(this) {
			for(int i = 0; i < 1000; i++) {
				System.out.print("陈");
				System.out.print("清");
				System.out.print("香");
				System.out.print("好");
				System.out.print("看");
				System.out.print("\r\n");
			}
		}
	}
} 

线程安全问题

  • 多线程并发操作同一数据时,就可能会出现线程安全问题;
  • 使用同步技术可以解决该问题,把操作数据的代码进行同步;不要多个线程一起操作
  • 例如:买票、有100张票,但有四个线程同时买票,如果不同步就会出现,400张票的效果;
public class Test {
	public static void main(String[] agrs) {
			new Ticket().start();
			new Ticket().start();
			new Ticket().start();
			new Ticket().start();
	}
}
class Ticket extends Thread {
	private static int ticket = 100;  //100张票
	public void run() {
		while(true) {
			synchronized(Ticket.class) {
				if(ticket == 0) {
					System.out.println("票买完了");
				}
				System.out.println(getName() + " : "  + ticket--);
			}
		}
	}
}

多线程死锁问题

  • 什么是死锁:就是两个线程都在等待对方执行完毕才能继续执行下去,结果就是两个线程都在无限等待中,这就是死锁;
  • 多线程同步时,如果同步代码嵌套,使用相同锁对象,就可能出现死锁这种情况
  • 代码示例(尽量不要使用同步嵌套)
public class Demo5_DeadLock {
	private static String s1 = "筷子左";
	private static String s2 = "筷子右";
	public static void main(String[] agrs) {
		new Thread() {
			public void run() {
				while(true) {	
					synchronized(s1) {
						System.out.println(getName() + "拿到" + s1 + "等待" + s2);
						synchronized(s2) {
							System.out.println(getName() + "拿到" + s2 + "开吃");
						}
					}
				}
			}
		}.start();
		new Thread() {
			public void run() {
				while(true) {
					
					synchronized(s2) {
						System.out.println(getName() + "拿到" + s2 + "等待" + s1);
						synchronized(s1) {
							System.out.println(getName() + "拿到" + s1 + "开吃");
						}
					}
				}
			}
		}.start();		
	}
}

1、资源共享时需要同步操作

2、程序中过多的使用同步会产生死锁;

Collections.synchronizedxxx

  • API中有一些线程不安全的类,如ArrayList,HashMap,StringBuilder;我们可以通过Collections.synchronizedXXX将线程不安全的线程转换为线程安全的;

单例设计模式

  • 什么是单例设计模式:就是一个类只能有一个对象;
  • 如果保证类在内存只能有一个对象呢?
    1、控制类的创建,不能让其它类创建本类对象(私有构造)
    2、在本类中定义一个本类对象。
    3、提拱静态、公共的访问方法。并将2中创建的本类对象引用返回出去,让其它类都可以拿该引用,那么其它类其实拿的是同一个引用,也就只有一个对象;
public class Demo4_Singleton {
	public static void main(String[] agrs) {
		
	}
}
//饿汉式
class Singleton1 {
	private Singleton1(){};
	private static Singleton1 s = new Singleton1();
	public static Singleton1 getInstance() {
		return s;
	}
}
//懒汉式
class Singleton2 {
	private Singleton2(){};
	private static Singleton2 s;
	public static Singleton2 getInstance() {
		if(s == null) {
			s = new Singleton2();
		}
		return s;
	}
}
//第三种和饿汉式类似
class Singleton3 {
	private Singleton3(){};
	public static final Singleton3 s = new Singleton3();
}
  • 饿汉式和懒汉式的区别

答:饿汉式是空间换时间,懒汉式是时间换空间;
在多线程访问时,饿汉式不会创建多个对象,懒汉式有可能会创建多个对象

Java.long.Runtime(运行时类,运用单例设计模式)

public class Demo_Runtime {
	public static void main(String[] agrs) {
		Runtime r = Runtime.getRuntime(); //获取运行时对象
		r.exec("shutdown -s -t 300");   //该方法在单独的进程中执行指定的字符串命令,这里命令的意思是在指定时间关机
		r.exec("shutdown -a");  //关闭命令
	}
}

两个线程间的通信

  • 什么时候需要通信?
    答:多个线和并发执行时,在默认情况下CPU是随机切换的,如果我们希望它们有规则的执行,就可以使用通信;例有每个线程执行一次打印
  • 怎么通信?
    答:如果希望线程等待,就调用wait()方法 Object中的方法
    如果希望唤醒等待的线程,就调用notify()方法,也是Objetc中的方法
    这两个方法必须在同步代码块中,并且使用同步锁对象来调用;
//等待唤醒机制
public class Demo1_Notify {
	public static void main(String[] agrs) {
		//jdk1.8后内部类访问局部变量,不用加final;已经默认给你隐藏了
		final Printer p = new Printer();
		new Thread() {
			while(true) {
				p.print1();
			}
		}.start();
		new Thread() {
			while(true) {
				p.print2();
			}
		}.start();
	}
}
class Printer {
	private int flag = 1;
	public void print1() {
		synchronized(this) {
			if(flag != 1) {
				try {
					this.wait(); // 该方法使当前线程等待
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.print("黑");
			System.out.print("马");
			System.out.print("程");
			System.out.print("序");
			System.out.print("员");
			System.out.print("\r\n");
			flag = 2;
			this.notify();   //随机唤醒单个等待线程
		} 
	}
	publuc void print2() {
		synchronized(this) {
			if(flag != 2) {
				try {
					this.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.print("传");
			System.out.print("智");
			System.out.print("播");
			System.out.print("客");
			System.out.print("\r\n");
			flag = 1;
			this.notify();
		}
	}
}

生产者和消费者

public class ThreadCaseDemo01 {
	public static void main(String[] agrs) {
		Info info = new Info();
		Producer pro = new Producer(info);
		Consumer con = new Consumer(info);
		new Thread(pro).start();
		new Thread(con).start();
	}
}
class Info {  //定义信息类
	private String name = "李超武";
	private String content = "Java开发工程师";
	private boolean flag = false;
	public synchronized void set(String name,String content) {
		if(!flag) {
			try {
				super.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.name = name;
		try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		this.content = content;
		flag = false;
		super.notifyAll();
	}
	public synchronized void get() {
		if(flag) {
			try {
				super.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(this.name + "-->" + this.content);
		flag = true;
		super.notifyAll();
	}
}

//生产者
class Producer implements Runnable {
	private Info info = null;
	public Producer(Info info) {
		this.info = info;
	}
	public void run() {
		boolean flag = false;
		for(int i = 0; i < 50; i++) {
			synchronized(Producer.class) {
				if(flag) {
					this.info.set("李超武","Java开发工程师");
					flag = false;
				}else {
					this.info.set("陈清香","睡觉");
					flag = true;
				}
			}
		}
	}
}

//消费者
class Consumer implements Runnable {
	private Info info = null;
	public Consumer(Info info) {
		this.info = info;
	}
	public void run() {
		for(int i = 0; i < 50; i++) {
			info.get();
		}
	}
}
本程序总结:
1、在本程序操作中需要以下两点问题
	生产者要不停的生产,但不能生产错误的信息、重复生产等;
	消费者不停取走,但不能重复取走;
2、Object中对线程的支持
	等待:wait();
	唤醒:notify()、notifyAll()
3、本程序只是假设对于同步,等待,唤醒机制的操作,本来运行的问题了解就可以了

三个及三个以上的线程通信

  • 多个线程通信的问题
    notify()方法是随机唤醒第一个等待的线程
    notifyAll()方法是唤醒所有等待的线程,哪个优先级高,哪个线程就有可能先执行;
    注意:要使用while进行判断标记,不能使用if;因为if是在哪等待就在哪唤醒,而while是重新进行判断
//这里我只写核心代码
class Printer {
	private int flag = 1;
	public void print1() {
		synchronized(this) {
			while(flag != 1) {
				this.wait();    //这里要处理异常,由于篇幅没有处理
			} 
			System.out.print("李");
			System.out.print("超");
			System.out.print("武");
			System.out.print("\r\n");
			flag = 2;
			this.notifyAll();
		}
	}
	public void print2() {
		synchronized(this) {
			while(flag != 2) {
				this.wait();
			}
			System.out.print("陈");
			System.out.print("清");
			System.out.print("香");
			System.out.print("\r\n");
			flag = 3;
			this.notifyAll();
		}
	}
	public void print3() {
		synchronized(this) {
			while(flag != 3) {
				this.wait();
			}
			System.out.print("吴");
			System.out.print("非");
			System.out.print("凡");
			System.out.print("\r\n");
			flag = 1;
			this.notifyAll();
		}
	}
}
  • 补充说明(注意)

1、在同步代码块中:你的锁对象是什么,就用该对象调用wait()、notify()、notifyAll();

2、为什么wait()、notify()方法定义在Object?很明显因为锁对象可以是任意对象,而任意对象必须都可以调用wait()等方法,所以定义在Object中,所有类都继承了Object类
3、sleep()方法和wait()方法的区别?
a、sleep()必须传入时间值参数,时间到了自动醒来
wait()可以传入时间值参数(和sleep()一样),也可以不传入参数(就处于一直等待,等待被唤醒)
b、sleep()在同步方法和同步代码块中,不释放锁;就是不释放执行权;sleep开始、休眠过程中、结束后都有执行权、执行的还是当前线程;而wait()方法一旦执行,就是放弃了执行权,别的线程可以执行,被唤醒之后,才有执行权;也就是释放锁;

互斥锁

  • 什么是互斥锁:使用ReentrantLock类的lock()和unlock()方法进行同步
  • 怎么使用?
    答:使用ReentrantLock类的newCondition()方法可以获取Condition对象,需要使线程等待的时候使用Condition的await()方法,要唤醒线和时signal()方法(也有signalAll()方法);不同的线程使用不同Condition对象,这样就可以区分唤醒的时候找的是哪个线程;
  • 类和主要方法介绍

ReentrantLock类:具有与使用synchronized方法相同的功能;而且功能更加强大
ReentrantLock中lock()方法:获取锁
ReentrantLock中unlock()方法:释放锁
ReentrantLock中newCondition()方法:获取一个的监视器;
Condition接口:理解监视器
Condition接口中方法里面都是抽象的子类必须实现,其实在通过newCondition()方法获取对象时,获取的是new ConditionObject();该类重写了Condition接口中所有的抽象方法;
Condition中await()方法:帮助文档中说导致当前线程等到发信息或interrupted,其实在初学者看来就是使用线程等待,和Objetc类中wait()方法一样;
Condition中方法signal()方法:帮助文档说唤醒一个线程;和Object类中notify()方法一样;
它俩区别:singnal()可以指定哪个线程唤醒,而notify()方法随机唤醒正在等待的线程;
其实它目前用不到自己帮助文档和源码;

//核心部分
class Printer {
	private ReentrantLock r = new ReentrantLock(); //创建一个互斥锁对象;
	priavte Condition c1 = r.newCondition();   //获取监视器
	private Condition c2 = r.newCondition();
	private Condition c3 = r.newCondition();
	private int flag = 1;
	public void print1() {
		r.lock();  //获取锁
		if(flag != 1) {
			c1.await();   //监视器1 等待.  此处有异常由于篇幅没有写要注意
		}
		System.out.print("李");
		System.out.print("超");
		System.out.print("武");
		System.out.print("\r\n");
		flag = 2;
		c2.signal(); //唤醒监视器2所在线程
		r.unlock();  //释放锁
	}
	public void print2() {
		r.lock();
		if(flag != 2) {
			c2.await();
		}
		System.out.print("陈");
		System.out.print("清");
		System.out.print("香");
		System.out.print("\r\n");
		flag = 3;
		c3.signal();
		r.unlock();
	}
	public void print3() {
		r.lock();
		if(flag != 3) {
			c3.await();
		}
		System.out.print("吴");
		System.out.print("非");
		System.out.print("凡");
		System.out.print("\r\n");
		flag = 1;
		c1.signal();
		r.unlock();
	}
}

线程组的使用

  • 线程组:java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制;
  • 默认情况下所有线程都是主线程组main
  • 方法:getThreadGroup();方法 通过线程对象调用该方法,获取该线程所在线程组对象
  • 方法:getName();通过线程组对象获取该线程组名称;
  • 我们可以自己创建线程组和设置
    ThreadGroup tg = new ThreadGroup(“线程组”);//创建新线程组
    然后通过Thread构造将线程放到组里去并设置名称
    Thread(tg,线程对象,线和名称);
    然后可以将该组中所有的线程设置为守护线程和优先级;
public class Demo_ThreadGroup {
		public static void main(String[] agrs) {
			//创建新的线程组
			ThreadGroup tg = new ThreadGroup("线程组");
			//创建Runnable子类对象
			MyRunnable mr = new MyRunnable();
			//将t1放到线程中
			Thread t1 = new Thread(tg,mr,"张三");
			//将t2放到线程组中
			Thread t2 = new Thread(tg,mr,"李四");
			//获取t1的线程组名称
			System.out.println(t1.getThreadGroup().getName());
			//将线程组下所有线程设置成守护线程;
			tg.setDaemon(true);
		}
}
class MyRunnable implements Runnable {
	public void run() {
		语句;
	}
}

线程的五种状态

  • 新建、就绪、运行、阻塞、死亡;
    在这里插入图片描述
  • 注意:从这可以知道,调用start()方法不是立刻就启动,而是等cpu进行调度;

线程池

  • 程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能;特别是当程序中要创建大量生存周期很短的线程时,更应该考虑使用线程池。线程池里的每一个线程执行结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用,jdk1.5前,我们必手动实现自己的线程池,从jdk1.5开始,java内置支持线程池;
  • 内置线程池的使用
    JDK1.5新增了一个Executors工厂类来产生线程池,有如下方法
    //1、创建线程池,指定里面几条线程
    public static ExecutorService newFixedThreadPool(int nThreads)
    //2、创建线程池里面就一个条
    public static ExecutorService newSingleThreadExecutor()
    //3、通过返回的ExecutorService对象将线程添加到线程池中,并执行
    Future<?> submit(Runnable task); //重载的方法
    //4、关闭线程池
    void shutdown();
public class Demo_Exeutors {
	public static void main(String[] agrs) {
		//创建线程池指定为两条线程
		ExecutorsService pool = Executors.newFixedThreadPool(2);
		pool.submit(new MyRunnable());  //将线程添加线程池中并执行
		pool.submit(new MyRunnable());
		pool.shutdown();//关闭线程池
	}
}
class MyRunnable Implements{
	public void run() {
		语句;
	}
}

多线程的第三种实现方式(Callable)

//求 100和50的和
public class Demo_Callable {
	public static void main(String[] agrs) throws Exception{
		ExecutorService pool = Executors.newFixedThreadPool(2); //创建线程池
		Future<Integer> t1 = pool.submit(new MyCallable(100));//添加到线程池中并执行并返回Future
		Future<Integer> t2 = pool.submit(new MyCallable(50));
		System.out.println(t1.get());//输出返回值
		System.out.println(t2.get());
		pool.shutdown();//关闭线程
		}
}
class MyCallable implements Callable<Integer> {
	private int num;
	public MyCallable(int num) {
		this.num = num;
	}
	public Integre call() throws Exception{
		int sum = 0;
		for(int i = 1; i < num; i++) {
				sum += i
		}
		return sum;
	}
}

线程的生命周期

在这里插入图片描述

  • 一个新的线程创建之后通过start()方法进入运行状态,在运行状态中可以使用yield()进行礼让,但线程并没有停止,如果一个线程需要暂停的放,可以使用wait()、sleep()、await()、suspend(),如果现在线程不需要再执行,则可以通过stop()方法结束,还有就是等run()方法执行完毕也表示结束;
  • 以下方法被弃用
    1、suspend():暂时挂起线程
    2、resume():恢复挂起的线程
    3、stop():停止线程
    **为什么被弃用?**答:都会产生死锁的问题

如果我想停止一个线程如何做(stop()被弃用)

  • 通过设置标志位

  • 代码实现

public class Demo {
	public static void main(String[] agrs) {
		MyThread mt = new MyThread();
		Thread t = new Thread(mt,"线程");
		t.start();
		Thread.sleep(100);//此处有异常
		mt.stop(); 
	}
}
class MyThread implements Runnable {
	private boolean flag = true;
	public void run() {
		int i = 0;
		while(this.flag) {
			System.out.println(Thread.currentThread().getName() +
										 " 运行,i = "  + (i++));
		}
	}
	public void stop() {
		this.flag = false;
	}
}

简单工厂设计模式

  • 简单工厂设计模式:又叫静态方法模式,它定义一个具体的工厂类负责创建一些类的实例
  • 优点:客户端不需要在负责对象的创建,从而明确了各个类的职责;
  • 缺点:这个工厂负责所有类的创建,如果有新的类增加,某些类的创建方式不同,这时就要不停的修改;不利于维护
  • 案例:
//动物类
public abstract class Animal {
	public void eat();
}
//狗类
public class Dog extends Animal {
	public void eat() {
		System.out.println("狗吃肉");
	}
}
//猫类
public class Catextends Animal {
	public void eat() {
		System.out.println("猫吃鱼");
	}
}
//工厂类
public class AnimalFactory {
	private AnimalFactory(){};
	public static Animal createAnimal(String name) {
		if("Dog".equals(name)) {
			return new Dog();
		}else if("Cat".equals(name)) {
			return new Cat();
		}else {
			return null;
		}
	}
}
//测试类
public class Test {
	public static void mian(String[] agrs) {
		Dog d = (Dog)AnimalFactory.createAnimal("Dog");
		Cat c = (Cat)AinmalFactory.createAnimal("Cat");
		d.eat();
		c.eat();
	}
}

工厂方法设计模式

  • 工厂方法设计模式:工厂方法模式中抽象工厂类负责创建类的接口,具体对象的创建工厂由继承抽象工厂的具体类实现。
  • 优点客户端不需要在负责对象的创建,从而明确了各个类的职责。如果新的类增加了,只需要增加一个具体的类和具体类的工厂类就可以了,不影响已有代码,后期维护容易;增强了系统的扩展性;
  • 缺点需要额外的编写代码,增加了工作量;
  • 案例
//动物抽象类
public abstract class Animal {
	public abstract void eat();
}
//猫类
public class Cat extends Animal {
	public void eat() {
		System.out.println("猫吃鱼");
	}
}
//狗类
public class Dog extends Animal {
	public void eat() {
		System.out.println("狗吃肉");
	}
}
//抽象工厂类
public interface Factory {
	public Animal createAnimal();
}
//猫工厂类
public class CatFactory implements Factory{
	public Animal createAnimal() {
		return new Cat();
	}
}
//狗工厂类
public class DogFactory implements Factory {
	public Animal createAnimal() {
		return new Dog();
	}
}

一些面试题

  1. Thread类中的start()和run()方法有什么关系?

答:start()方法用来启动线程,run()方法内放了要执行的代码,用来被执行,start()低层其实调用run()方法,看源码得知,如果你是直接调用run()方法,就是等于在主线程调用了该方法而已,并没有创建新线程,而通过start()才是启动新线程并在新线程中执行run()方法;

  1. 进程和线程的区别?

答:进程是执行着的应用程序,而线程是进程内部的一个执行序列。一个进程可以有多个线程;线程消失了,进程不会消失,而进程消失了,线程就消失了

  1. 同步代码块和同步方法的区别是什么?

答:同步方法是修饰方法通过synchronized修饰,而同步代码块是在方法内部进行定义;同步方法是同步了整个方法,而同步代码块方法中可以同步某块代码;

  1. 什么是死锁?

答:两个线程都在等待对方执行完毕才能继续执行下去,这就是死锁,结果就是两个线程都处于无限等待中;

上一篇 内部类&常用API

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值