您的位置:首页 > 编程语言 > Java开发

【JavaSE笔记】多线程(一)进程&线程&Thread&同步代码块

2017-08-20 15:44 471 查看
本期知识点:
进程

线程

Thread

同步代码块

1.进程:

a.线程依赖于进程而存在

b.进程:

就是正在运行的程序
进程是系统进行资源分配和调用的独立单位,每一个进程都有自己的内存空间和系统资源

c.多进程的意义:

在一段时间内执行多个任务,并且提高CPU的使用率
例如:我们一边玩游戏一边听音乐是同时进行的吗?
不是,单核CPU在某一个时间上只能做一件事情,而在玩游戏或者听歌的时候,CPU在做程序间高效的切换让人觉得是同时进行。

2.线程:

a.概述:

在同一个进程内又可以执行多个任务,而每一个任务,可以i看作是一个线程

线程:是程序的执行单元,执行路径。是程序使用CPU的最基本单位

单线程:程序只有一条执行路径

多线程:程序有多条执行路径


b.多线程的意义:

多线程的存在,不是为了提高程序的执行速度,其实是为了提高应用程序的使用率。
程序的执行其实是在抢CPU的资源、CPU的执行权。 多个程序是在抢资源,如果一个进程的执行路径比较多,就有更高的几率抢到CPU的执行权。
线程执行具有随机性。


c.并行和并发的区别:

并行:逻辑上同时发生,指在某一个时间内同时运行多个程序。
并发:物理上同时发生,指在某一个时间点同时运行多个程序。


d.JVM程序的运行原理:

由java命令启动JVM,JVM启动就相当于启动了一个进程,接着由该进程创建了一个主线程去调用main方法。

(问题)JVM虚拟机的启动是多线程还是单线程?

多线程。
因为,垃圾回收线程也要先启动,否则很容易出现内存溢出。垃圾回收线程+主线程,至少启动了两个线程,所以是多线程。

3.Thread

a.如何实现多线程程序?

java语言是不能直接调用系统功能(用系统功能创建对象)
java语言可以通过C/C++底层已经调用了系统功能
java提供了一个类:Thread 是程序中的执行线程。Java虚拟机允许应用程序并发地运行多个执行线程。

b.多线程程序实现方法一

继承Thread类
i.步骤:

1)自定义类MyThread继承Thread类

2)MyThread类里重写run()

3)创建对象

4)启动线程

(问题)为什么要重写run()?

因为不是类中所有代码都要被线程执行。为了区分哪些代码能够被线程执行,java提供了Thread类中的run()方法用来包含那些要被线程执行的代码

(问题)run()和start()的区别?

run():仅仅是封装被线程执行的代码,直接调用是普通方法。

start():首先启动了线程,然后再由JVM去调用该线程的run()方法。

public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100 ; i++) {
System.out.println(i);
}
}
}
public class MyThreadDemo {
public static void main(String[] args) {
//创建线程对象
MyThread my = new MyThread();
//启动线程
my.start();
//		my.start();//IllegalThreadStateException
//		指示线程没有处于请求操作所要求的适当状态时抛出的异常。
//		相当于my线程被调用了两次,而不是两个线程启动
MyThread my2 = new MyThread();
my2.start();

}
}


c.常用方法:

如何获取线程的名称?

public final String getName()返回该线程的名称。

如何设置线程的名称?

public final void setName(String name)改变线程名称,使之与参数 name 相同

public static Thread currentThread()返回对当前正在执行的线程对象的引用。

public class MyThread extends Thread {
public MyThread() {
// TODO Auto-generated constructor stub
}

public MyThread(String name) {
super(name);
}

@Override
public void run() {
for (int i = 0; i < 100 ; i++) {
System.out.println(getName()+":"+i);
}
}
}
/*
设置名字的源码:
public final synchronized void setName(String name) {
checkAccess();
if (name == null) {
throw new NullPointerException("name cannot be null");
}

this.name = name;
if (threadStatus != 0) {
setNativeName(name);
}
}
*/
public class MyThreadDemo {
public static void main(String[] args) {
//		无参构造+setXxx();
//		//创建线程对象
//		MyThread my1 = new MyThread();
//		MyThread my2 = new MyThread();
//		//设置名称
//		my1.setName("Yang");
//		my2.setName("Fan");
//		有参构造
MyThread my1 = new MyThread("Ash");
MyThread my2 = new MyThread("Glaz");
//		启动线程
my1.start();
my2.start();

//		获取main方法所在线程对象的名称?
//		public static Thread currentThread()返回对当前正在执行的线程对象的引用。
System.out.println(Thread.currentThread().getName());
}
}
/*为什么名称是:Thread-1:60 带有编号?
Thread的无参构造 源码:
private volatile String name;

public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}

private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {
//省略代码
this.name = name;
//省略代码
}

private static int threadInitNumber;//0,1
private static synchronized int nextThreadNum() {
return threadInitNumber++;//return 0,1
}

public final String getName() {
return name;
}

*/


d.线程调度的两种模型

i.分时调度模型:

所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片

ii.抢占式调度模型(Java采用的是该调度方式)

优先让优先级高的线程使用CPU,如果线程优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些。

iii.如何获取默认优先级?

public final int getPriority()返回线程的优先级。

注意:线程默认优先级是5

iv.如何设置优先级?

public final void setPriority(int newPriority)更改线程的优先级。

注意:

1)会有异常java.lang.IllegalArgumentException

抛出的异常表明向方法传递了一个不合法或不正确的参数。

2)java.lang.Thread

public static final int MAX_PRIORITY 线程可以具有的最高优先级。 10

public static final int MIN_PRIORITY 线程可以具有的最低优先级。1

public static final int NORM_PRIORITY 分配给线程的默认优先级。5

优先级范围:1~10

3)线程优先级仅仅表示线程获取的CPU时间片的几率。要在多次运行时才能看到效果。

public class ThreadPriority extends Thread {
@Override
public void run() {
for (int i = 0; i < 100 ; i++) {
System.out.println(getName()+":"+i);
}
}
}
public class ThreadPriorityDemo {
public static void main(String[] args) {
ThreadPriority tp1 = new ThreadPriority();
ThreadPriority tp2 = new ThreadPriority();
ThreadPriority tp3 = new ThreadPriority();

tp1.setName("Ash");
tp2.setName("Glaz");
tp3.setName("Ying");

//获取默认优先级
//		System.out.println(tp1.getPriority());//5
//		System.out.println(tp2.getPriority());//5
//		System.out.println(tp3.getPriority());//5
//设置优先级
//		tp1.setPriority(1000);
//		IllegalArgumentException
//		抛出的异常表明向方法传递了一个不合法或不正确的参数。
//设置正确的优先级
tp1.setPriority(1);
tp2.setPriority(10);

tp1.start();
tp2.start();
tp3.start();
}
}


e.线程的控制

i.休眠线程

public static void sleep(long millis) throws InterruptedException在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。

注意:父的方法没有异常抛出,子的重写方法不能有异常抛出只能 try...catch(这种情况只能子类中进行捕获异常);

ii.加入线程

public final void join() throws InterruptedException 等待该线程终止。

iii.礼让线程

public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。

让多个线程的执行更和谐但是不能保证一个一次

iv.后台线程

public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。

v.中断线程

public final void stop()

public void interrupt() 中断线程。把线程的状态终止,并抛出SecurityException

f.线程的生命周期

i.新建:创建线程对象
ii.就绪:有执行资格,没有执行权
iii.运行:有执行资格,有执行权
iv.阻塞:由于一些操作让线程处于该状态。没有执行资格,没有执行权。而另一些操作可以激活它,让他处于就绪状态。
v.死亡:线程对象变成垃圾,等待回收



g.多线程程序实现方法二

实现 Runnable接口
i.步骤:

1)自定义一个类MyRunnable(同一个资源)实现Runnable接口

2)然后实现Rannable里面的run方法:耗时的操作

3)在主线程中创建MyRuhnable类的对象

4)创建Thread类对象,将第三步的对象作为参数进行传递来启动线程

ii.有了方式一为什么要出现方式二?(实现接口方式的好处)

例如:有个类已经继承了一个父类,而且父类不想继承Thread类

解决了单继承的局限性。

适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。

public class MyRunnable implements Runnable {

@Override
public void run() {
for (int i = 0; i < 100 ; i++) {
//由于实现接口的方式不能直接使用Thread类的方法,但是可以间接使用
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
public class MyRunnableDemo {
public static void main(String[] args) {
//创建MyRunnable类对象
MyRunnable mr = new MyRunnable();
//创建Thread类对象,把MyRunnable类对象作为构造参数传递
//		public Thread(Runnable target)
//		Thread t1 = new Thread(mr);
//		Thread t2 = new Thread(mr);
//
//		t1.setName("Ash");
//		t2.setName("Ying");
//		public Thread(Runnable target,String name)

Thread t1 = new Thread(mr, "Ash");
Thread t2 = new Thread(mr, "Ying");

t1.start();
t2.start();

}
}


4.解决线程安全问题的基本思想

a.卖票问题的问题

i.相同的票出现多次

CPU的一次操作必须是原子性的

ii.出现了负数的票

随机性和延迟导致的

//是否是多线程环境 	是
//是否有共享数据		是
//是否有多条语句操作共享数据	是
//满足出问题的条件
public class SellTicket implements Runnable {
//定义100张票
private int ticket = 100;
@Override
public void run() {
while(true){
if(ticket>0){
try {
Thread.sleep(100);//t1进来不休息,t2进来休息,t3进来休息
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票");
}
}
}
}
//实现Runnable接口的方法
public class SellTicketDemo {
public static void main(String[] args) {
//创建资源对象
SellTicket st = new SellTicket();
//创建三个线程对象
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");

t1.start();
t2.start();
t3.start();
}
}


b.首先想为什么出现问题?(也是我们判断是否有问题的标准)

i.是否是多线程环境
ii.是否有共享数据
iii.是否有多条语句操作共享数据

c.如何解决?

i和ii的问题改变不了,只能想办法把C改变。
思想:把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的时候,别人不能来执行。

5.同步代码块:

a.格式:

synchronized (对象){

需要被同步的代码;

}
注意:同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。

public class SellTicket implements Runnable {
//定义100张票
private int ticket = 100;
//创建锁对象
private  Object obj = new Object();

@Override
public void run() {
while(true){
//t1,t2,t3都可以走到这里
//t1抢到执行权t1进
//门(开/关)
synchronized (obj) {//t1进来后门关
if(ticket>0){
try {
Thread.sleep(100);//t1睡眠
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票");
}
}
}
}
}


b.同步的前提

多个线程
多个线程使用的是同一个锁对象

c.同步的优点和缺点:

优点:同步的出现解决了多线程的安全问题。
缺点:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。

d.同步解决线程安全问题

i:同步代码块

这里的锁对象可以是任意对象。

ii:同步方法

把同步加在方法上。

这里的锁对象是this

iii:静态同步方法

把同步加在方法上。

这里的锁对象是当前类的字节码文件对象(反射再讲字节码文件对象)

i.
class Demo{

}
public class SellTicket implements Runnable {
//定义100张票
private int ticket = 100;
//创建锁对象
private Object obj = new Object();
private Demo d = new Demo();
private int x = 0;
//  同步代码块用Obj做锁
//	@Override
//	public void run() {
//		while(true){
//			synchronized (obj) {
//				if(ticket>0){
//					try {
//						Thread.sleep(100);
//					} catch (InterruptedException e) {
//						e.printStackTrace();
//					}
//					System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票");
//				}
//			}
//		}
//	}

//	同步代码块
//	这里的锁对象可以是任意对象。
//	@Override
//	public void run() {
//		while(true){
//			synchronized (d) {
//				if(ticket>0){
//					try {
//						Thread.sleep(100);
//					} catch (InterruptedException e) {
//						e.printStackTrace();
//					}
//					System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票");
//				}
//			}
//		}
//	}
//}
@Override
public void run() {
while(true){
if(x%2==0){
synchronized (d) {
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票");
}
}
}else{
//				synchronized (d) {
//					if(ticket>0){
//						try {
//							Thread.sleep(100);
//						} catch (InterruptedException e) {
//							e.printStackTrace();
//						}
//						System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票");
//					}
//				}
sellTicket();
}
}
}
//如果一个代码一进去就同步 把同步加在方法上
private void sellTicket() {
//		synchronized (d) {
//			if(ticket>0){
//				try {
//					Thread.sleep(100);
//				} catch (InterruptedException e) {
//					e.printStackTrace();
//				}
//				System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票");
//			}
//		}
}
}


ii.
class Demo{

}
public class SellTicket implements Runnable {
//定义100张票
private int ticket = 100;
//创建锁对象
private Object obj = new Object();
private Demo d = new Demo();
private int x = 0;

@Override
public void run() {
while(true){
if(x%2==0){
synchronized (this) {
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票");
}
}
}else{
sellTicket();
}
}
}
//如果一个代码一进去就同步 把同步加在方法上
private  synchronized void sellTicket() {
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票");
}
}
}


iii.
class Demo{

}
public class SellTicket implements Runnable {
//定义100张票
private static int ticket = 100;
//创建锁对象
private Object obj = new Object();
private Demo d = new Demo();
private int x = 0;

@Override
public void run() {
while(true){
if(x%2==0){
synchronized (SellTicket.class) {
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票");
}
}
}else{
sellTicket();
}
}
}
private  static synchronized void sellTicket() {
if(ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(ticket--)+"张票");
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: