文档章节

java基础查漏补缺一:多线程

silence88
 silence88
发布于 2017/02/15 22:31
字数 2187
阅读 24
收藏 1
  • 进程与线程的理解。

进程是执行中的程序:程序是一段描述指令的文本,是一个静态的概念,把这段指令运行起来,每次运行就得到了一个进程,进程是动态的概念;操作系统会为进程分配资源。

线程是进程中一段实际执行的代码:它也是一个动态的概念;操作系统调度和分派线程,为线程分配CPU时间片,使其执行。

进程之间可以并发执行:如你可以运行多个记事本程序,运行多个QQ。

线程之间也可以并发执行:如你运行Word程序时,一个线程等待用户输入,一个线程进行拼写检查,一个线程将数据存入临时文件中...

简单来说:二者都可以并发,只是并发的层次不一样。

  • 实现线程的两种方式。

这里以卖票为例。(没有考虑同步问题)

1、实现Thread

public class MainTest {

	public static void main(String[] args) {

		ThreadType thread1 = new ThreadType("卖票窗口1");
		ThreadType thread2 = new ThreadType("卖票窗口2");
		
		thread1.start();
		thread2.start();
		
	}

}

class ThreadType extends Thread{
	
	private static int ticket = 100;
	
	public ThreadType(String name){
		super(name);
	}
	
	@Override
	public void run() {
		while(ticket > 0){
			ticket -- ;
			System.out.println(this.getName()+"执行剩余票数"+ticket);		
		}
	}
}

2、继承Runnable

public class MainTest {

	public static void main(String[] args) {
		
		RunanbleType runnableType = new RunanbleType();

		Thread runnable1 = new Thread(runnableType,"卖票窗口1");
		Thread runnable2 = new Thread(runnableType,"卖票窗口2");
		
		runnable1.start();
		runnable2.start();
		
	}

}

class RunanbleType implements Runnable {
	
	private int ticket = 100;
	
	@Override
	public void run() {
		while(ticket > 0){
			ticket -- ;
			System.out.println(Thread.currentThread().getName()+"执行剩余票数"+ticket);		
		}
	}
}

这里有几点说明:

1、继承Thread可以给线程取名字通过调用父类的构造函数,Thread类有一个name域这段这个就是线程的名称这段,当然也可以调用父类的set方法。当不给线程取名字的时候,线程名默认是以Thread-index命名,获取线程名除了通过super.getname()之外还可以通过Thread.currentThread().getName()获取到。2、实现Runnable接口只需要定义一个实现该该接口类的实例,然后实例化多个Thread对象接收一个你写的类的实例。

3、实际情况都尽量用第二种方式。

  • 开启线程为什么是start()而不是run()

实际上start()方法是Thread的一个方法,而start方法会使得本机开启线程调用run方法,而此时执行的线程代码并是你服写的run方法。

  • 冻结状态与阻塞状态的区别

冻结状态有执行权但是放弃了,例如线程睡眠了。

阻塞状态没有执行权。

  • 同步代码块和同步函数

没有什么区别,尽量只用同步代码块,因为一般情况下某个方法只有某几句代码需要同步(在你不想提出一个单独方法的情况下)。

  • 哪块代码需要同步的分析

找到哪块代码在操作同一资源,这块代码很有可能就是我们要考虑同步的地方

  • 同步函数用的哪个锁

函数需要被对象调用也就是当前对象调用:this,那也就是this是这个锁(当前对象)。

如果同步函数是static,则锁是类名.class(类的字节码文件对象,也就是Class的对象)。可以这样理解:静态进内存时,内存中没有本类对象,但是一定有该类的字节码文件对象,因此锁就是这个类的字节码文件对象,这个锁是唯一的。

  • synchronized(this)与synchronized(xxx.class)的区别

前者是一个类的实例对象,如果有多个线程在操作的时候,如果使用的是this,一定要注意每一个线程的this是否是同一个对象,如果不是这个锁是没有意义的!

后者是一个字节码文件对象,一定是唯一一个的,因此这个锁一定有效。

  • 同步什么情况产生死锁

同步中嵌套同步,各自占用了对方的锁资源。看以下代码示例:

package cn.zxy;

public class LockDemo {

	public static void main(String[] args) throws InterruptedException {

		LockDemoThread lockDemoThread = new LockDemoThread();

		Thread thread1 = new Thread(lockDemoThread,"LockThread1");
		Thread thread2 = new Thread(lockDemoThread,"LockThread2");

		thread1.start();
		Thread.sleep(50);
		lockDemoThread.flag = false;
		thread2.start();
	}

}

class LockDemoThread implements Runnable {

	public boolean flag = true;

	private Object locka = new Object();
	private Object lockb = new Object();

	@Override
	public void run() {

		if (flag) {

			synchronized (locka) {

				System.out.println("if>locka");
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (lockb) {

					System.out.println("if>lockb");
				}
			}
		} else {

			synchronized (lockb) {

				System.out.println("else>locka");
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (locka) {

					System.out.println("else>lockB");
				}
			}
		}
	}
}

这里主线程main有睡眠是为了保证两条线程同时都开启了,并且一个进入if一个进入else,这里的ifelse仅仅只是为了使一个线程类执行两端代码逻辑,以便出现死锁的现象。

运行该代码,通过打出的console:并可推测代码已经死锁了。

if>locka
else>locka

或者通过jdk自带的工具查看结果:

  • wait()、notify()、notifyall()为什么定义在Object类中

public final void notify()

唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。

直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。

此方法只应由作为此对象监视器的所有者的线程来调用。通过以下三种方法之一,线程可以成为此对象监视器的所有者:

  • 通过执行此对象的同步实例方法。
  • 通过执行在此对象上进行同步的 synchronized 语句的正文。
  • 对于 Class 类型的对象,可以通过执行该类的同步静态方法。

一次只能有一个线程拥有对象的监视器

根据notify()的api解释,这些方法在操作同步线程的时候,都必须要标识他们所要操作线程持有的锁,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒,不可以对不同锁中的线程进行唤醒,也就是说等待和唤醒必须操作的是同一把锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

  • 生产者与消费者案例

  • 如何结束线程

虽然jdk的Thread有提供stop方法停止线程,但是不推荐使用,已标记为过时,那么怎么结束线程呢?

让run执行的代码结束掉即结束线程了,也就是说需要我们人为地通过标识使得run方法执行完跳出,大部分情况下我们开启线程都是在run里面写循环结构,也就是只要控制循环结束即可。但这里要注意一个特殊情况:如果线程中的代码处于冻结状态,此时线程相当于暂停了,你无法通过标识判断的方式去停止线程,因此需要用到interrupt方法清除等待状态,让线程重新执行起来。

  •     守护线程

setDaemon方法设置线程为守护线程,这么理解守护线程:如果这个线程依赖某个主线程,如果主线程结束了,则守护线程也随着结束了。例如我们在main函数里面写开启某个线程的操作,正常情况下main虽然已经执行完了,但是main里面开启的线程便不会随着main线程而终止,但如果是守护线程,则这个线程依赖的主线程main结束了,这个线程也随着结束了

  • Thread的join方法

	public static void main(String[] args) throws InterruptedException {

		RunanbleType runnableType = new RunanbleType();
		
		Thread runnable1 = new Thread(runnableType,"买票窗口1");
		Thread runnable2 = new Thread(runnableType,"买票窗口2");
		
		runnable1.start();
		
		runnable1.join();
		
		runnable2.start();
		
	}

当main线程执行到线程1的join方法时,main方法不再抢夺执行权,而是等到线程1执行完之后main才会有夺取cpu的执行权。

  • Thread的yield方法

暂停当前正在执行的线程对象,并执行其他线程。这么理解:线程让步,就是给其他线程一点机会,当然自己也还是有资格继续执行。

© 著作权归作者所有

下一篇: 面试经历
silence88
粉丝 8
博文 71
码字总数 72855
作品 0
深圳
程序员
私信 提问
Android--面试中遇到的问题总结(三)

《Android 开发工程师面试指南 LearningNotes 》,作者是陶程,由梁观全贡献部分。大家可以去知乎关注这两位用心的少年。这份指南包含了大部分Android开发的基础、进阶知识,不仅可以帮助准备...

sealin
2017/02/22
0
0
2019年Java程序员金三银四跳槽季,机会只留给有准备的人!

前言 现在大多数Java程序员都走入了盲点,以为来年跳槽就能涨一点薪,但是你有没有想过跳槽涨薪前提是建立在你要具备跳槽涨薪的价值,希望下面这些你看完之后能对你有所帮助。 跳槽要学习的技...

Java干货分享
02/15
261
1
混了三年工作经验,然而有用吗?

每个程序员的简历都有一些共同的特性,比如,爱好是打篮球,目标是成为架构师。 但是刚毕业的时候,什么都不懂,盲目的投简历,发现都要工作经验。 愿意校招的公司,又往往看重学历,普普通通...

Java-飞鱼
09/11
60
0
Java面试高频题精选300道,一份通往阿里的必备指南(pdf文档)

就目前大环境来看,跳槽成功的难度比往年高很多。一个明显的感受:今年的面试,无论一面还是二面,都很考验Java程序员的技术功底。 最近我整理了一份复习用的面试题及面试高频的考点题及技术...

java知识分子
06/12
43
0
菜鸟网络java岗面经 已拿offer

牛客网上看了很多面经现在回馈一下牛友。 我是一个双非二本java。首先要谢谢我的一个李姓同学。他先去蚂蚁金服。这才告诉我们,双非二本只要技术好大公司也是不会拒绝你的。 还有就是牛客网上...

牛客网
2018/05/10
0
0

没有更多内容

加载失败,请刷新页面

加载更多

java通过ServerSocket与Socket实现通信

首先说一下ServerSocket与Socket. 1.ServerSocket ServerSocket是用来监听客户端Socket连接的类,如果没有连接会一直处于等待状态. ServetSocket有三个构造方法: (1) ServerSocket(int port);...

Blueeeeeee
今天
6
0
用 Sphinx 搭建博客时,如何自定义插件?

之前有不少同学看过我的个人博客(http://python-online.cn),也根据我写的教程完成了自己个人站点的搭建。 点此:使用 Python 30分钟 教你快速搭建一个博客 为防有的同学不清楚 Sphinx ,这...

王炳明
昨天
5
0
黑客之道-40本书籍助你快速入门黑客技术免费下载

场景 黑客是一个中文词语,皆源自英文hacker,随着灰鸽子的出现,灰鸽子成为了很多假借黑客名义控制他人电脑的黑客技术,于是出现了“骇客”与"黑客"分家。2012年电影频道节目中心出品的电影...

badaoliumang
昨天
15
0
很遗憾,没有一篇文章能讲清楚线程的生命周期!

(手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本。 简介 大家都知道线程是有生命周期,但是彤哥可以认真负责地告诉你网上几乎没有一篇文章讲得是完全正确的。 ...

彤哥读源码
昨天
15
0
jquery--DOM操作基础

本文转载于:专业的前端网站➭jquery--DOM操作基础 元素的访问 元素属性操作 获取:attr(name);$("#my").attr("src"); 设置:attr(name,value);$("#myImg").attr("src","images/1.jpg"); ......

前端老手
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部