java多线程基础(synchronize关键字)
基础知识
线程:进程(process)就是一块包含了某些资源的内存区域。操作系统利用进程把它的工作划分为一些功能单元。
线程:进程中所包含的一个或多个执行单元称为线程(thread)。进程还拥有一个私有的虚拟地址空间,该空间仅能被它所包含的线程访问。
线程和进程的区别如下:
1)一个进程至少有一个线程。线程的划分尺度小于进程,使得多线程程序的并发性高。另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
2)线程在执行过程中与进程的区别在于每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
3)从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用来实现进程的调度和管理以及资源分配。
2 简述线程的状态及其转换
1)New,创建一个线程,但是线程并没有进行任何的操作。
2)Runnable,新线程从New状态,调用start方法转换到Runnable状态。线程调用start方法向线程调度程序(JVM或者是操作系统)注册一个线程,这个时候一切就绪只等CPU的时间。
3)Running,从Runnable状态到Running状态,线程调度根据调度策略的不同调度不同的线程,被调度执行的线程进入Running状态,执行run方法。
4)Dead状态,从Running状态到Runnable,run方法运行完毕后,线程就会被抛弃,线程就进入Dead状态。
5)Block状态,从Running状态到Block状态,如果线程在运行的状态中因为I/O阻塞、调用了线程的sleep方法以及调用对象的wait方法则线程将进入阻塞状态,直到这些阻塞原因被结束,线程进入到Runnable状态。
3 简述线程的两种创建方式以及它们的区别
创建线程的两种方式:
1)使用Thread创建线程。Thread类是线程类,其每一个实例表示一个可以并发运行的线程。我们可以通过继承该类并重写run方法来定义一个具体的线程。其中重写run方法的目的是定义该线程要执行的逻辑。启动线程时调用线程的start()方法而非直接调用run()方法。start()方法会将当前线程纳入线程调度,使当前线程可以开始并发运行。当线程获取时间片段后会自动开始执行run方法中的逻辑。
2)使用Runnable创建线程。实现Runnable接口并重写run方法来定义线程体,然后在创建线程的时候将Runnable的实例传入并启动线程。
两种创建线程方式的区别:
使用Thread创建线程,编写简单,可以直接操纵线程,无需使用Thread.currentThread(),但是不能够再继承其他类。
使用Runnable创建线程可以将线程与线程要执行的任务分离开减少耦合,同时Java是单继承的,定义一个类实现Runnable接口,这样该类还可以继承自其他类。
多线程实现方法
使用Thread创建线并启动线程
java.lang.Thread类是线程类,其每一个实例表示一个可以并发运行的线程。我们可以通过继承该类并重写run方法来定义一个具体的线程。其中
重写run方法的目的是定义该线程要执行的逻辑。启动线程时调用线程的start()方法而非直接调用run()方法。start()方法会将当前线程纳入线程调
度,使当前线程可以开始并发运行。当线程获取时间片段后会自动开始执行run方法中的逻辑。
public class TestThread extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("我是线程");
}
}
}
创建预启动线程
…
Thread thread = new TestThread();//实例化线程
thread.start();//启动线程
…
使用Runnable创建并启动线程
实现Runnable接口并重写run方法来定义线程体,然后在创建线程的时候将Runnable的实例传入并启动线程。
这样做的好处在于可以将线程与线程要执行的任务分离开减少耦合,同时java是单继承的,定义一个类实现Runnable接口这样的做法可以更好的
去实现其他父类或接口。因为接口是多继承关系。
public class TestRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("我是线程");
}
}
}
启动线程的方法:
…
Runnable runnable = new TestRunnable();
Thread thread = new Thread(runnable);//实例化线程并传入线程体
thread.start();//启动线程
…
使用内部类创建线程
通常我们可以通过匿名内部类的方式创建线程,使用该方式可以简化编写代码的复杂度,当一个线程仅需要一个实例时我们通常使用这种方式来
创建。
例如:
继承Thread方式:
Thread thread = new Thread(){ //匿名类方式创建线程
public void run(){
//线程体
}
};
thread.start();//启动线程
Runnable方式:
Runnable runnable = new Runnable(){ //匿名类方式创建线程
public void run(){
}
};
Thread thread = new Thread(runnable);
thread.start();//启动线程
线程的方法
currentThread:方法可以用于获取运行当前代码片段的线程
Thread current = Thread.currentThread();
获取线程信息
Thread提供了 获取线程信息的相关方法:
long getId():返回该线程的标识符
String getName():返回该线程的名称
int getPriority():返回线程的优先级
Thread.state getState():获取线程的状态
boolean isAlive():测试线程是否处于活动状态
boolean isDaemon():测试线程是否为守护线程
boolean isInterrupted():测试线程是否已经中断
线程优先级
线程的切换是由线程调度控制的,我们无法通过代码来干涉,但是我们可以通过提高线程的优先级来最大程度的改善线程获取时间片的几率。
线程的优先级被划分为10级,值分别为1-10,其中1最低,10最高。线程提供了3个常量来表示最低,最高,以及默认优先级:
Thread.MIN_PRIORITY,
Thread.MAX_PRIORITY,
Thread.NORM_PRIORITY
设置优先级的方法为:
void setPriority(int priority)
守护线程
守护线程与普通线程在表现上没有什么区别,我们只需要通过Thread提供的方法来设定即可:
**void setDaemon(boolean )**
当参数为true时该线程为守护线程。
守护线程的特点是,当进程中只剩下守护线程时,所有守护线程强制终止。
GC就是运行在一个守护线程上的。
需要注意的是,设置线程为后台线程要在该线程启动前设置。
Thread daemonThread = new Thread();
daemonThread.setDaemon(true);
daemonThread.start();
sleep方法
Thread的静态方法sleep用于使当前线程进入阻塞状态:
**static void sleep(long ms)**
该方法会使当前线程进入阻塞状态指定毫秒,当指定毫秒阻塞后,当前线程会重新进入Runnable状态,等待分配时间片。
该方法声明抛出一个InterruptException。所以在使用该方法时需要捕获这个异常
**注:**改程序可能会出现"跳秒"现象,因为阻塞一秒后线程并非是立刻回到running状态,而是出于runnable状态,等待获取时间片。那么这段等待
时间就是"误差"。所以以上程序并非严格意义上的每隔一秒钟执行一次输出。
yield方法:
Thread的静态方法yield:
**static void yield()**
该方法用于使当前线程主动让出当次CPU时间片回到Runnable状态,等待分配时间片。
join方法
**void join()**
该方法用于等待当前线程结束。此方法是一个阻塞方法。
该方法声明抛出InterruptException。
线程同步
synchronized关键字
多个线程并发读写同一个临界资源时候会发生"线程并发安全问题“
常见的临界资源:
多线程共享实例变量
多线程共享静态公共变量
若想解决线程安全问题,需要将异步的操作变为同步操作。
所谓异步操作是指多线程并发的操作,相当于各干各的。
所谓同步操作是指有先后顺序的操作,相当于你干完我再干。
同步代码块(synchronized 关键字 ),同步代码块包含两部分:一个作为锁的对象的引用,一个作为由这个锁保护的代码块
这个比较难理解故写了下面代码帮助理解
/**
* 多线程并发安全问题
* 当多个线程同时操作同一资源时,由于
* 线程切换时机不确定,导致出现逻辑混乱。
* 严重时可能导致系统崩溃。
* @author ylg
*
*/
public class SyncDemo1 {
public static void main(String[] args) {
/*
* 当一个方法中的局部内部类想引用该方法
* 的其他局部变量时,这个变量必须被声明
* 为final的
*/
final Table table = new Table();
Thread t1 = new Thread(){
public void run(){
while(true){
int bean = table.getBean();
Thread.yield();//模拟线程切换
System.out.println(
getName()+":"+bean
);
}
}
};
Thread t2 = new Thread(){
public void run(){
while(true){
int bean = table.getBean();
Thread.yield();//模拟线程切换
System.out.println(
getName()+":"+bean
);
}
}
};
t1.start();
t2.start();
}
}
class Table{
//20个豆子
private int beans = 20;
/**
* 当一个方法被synchronized修饰后,该方法
* 成为"同步方法"。多个线程不能同时进入到
* 方法内部。
* @return
*/
public synchronized int getBean(){
if(beans==0){
throw new RuntimeException("没有豆子了!");
}
Thread.yield();//模拟线程切换
return beans--;
}
}
/**
* 有效的缩小同步范围可以保证在
* 安全的前提下提高了并发的效率
* @author ylg
*
*/
public class SyncDemo2 {
public static void main(String[] args) {
final Shop shop = new Shop();
Thread t1 = new Thread(){
public void run(){
shop.buy();
}
};
Thread t2 = new Thread(){
public void run(){
shop.buy();
}
};
t1.start();
t2.start();
}
}
class Shop{
/*
* 在方法上使用synchroinzed,同步监视器对象即当前方法所属对象:this
*/
// public synchronized void buy(){
public void buy(){
try{
Thread t = Thread.currentThread();
System.out.println(t+"正在挑选衣服..");
Thread.sleep(5000);
/*
* 同步块可以缩小同步范围。
* 但是必须保证"同步监视器"即:"上锁对象"是同一个才可以。
* 通常,在一个方法中使用this所谓同步监视器对象即可。
*/
synchronized (this) {
System.out.println(t+"正在试衣服..");
Thread.sleep(5000);
}
System.out.println(t+"结账离开");
}catch(Exception e){
}
}
}
/**
* synchronized也成为"互斥锁"
* 当synchronzed修饰的是两段代码,但是"锁对象"相同时,这两段代码就是互斥的。
* @author ylg
*
*/
public class SyncDemo4 {
public static void main(String[] args) {
final Boo b = new Boo();
Thread t1 = new Thread(){
public void run(){
b.methodA();
}
};
Thread t2 = new Thread(){
public void run(){
b.methodB();
}
};
t1.start();
t2.start();
}
}
class Boo{
public synchronized void methodA(){
Thread t = Thread.currentThread();
System.out.println(t+"正在调用方法A");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
System.out.println(t+"调用方法A完毕");
}
public synchronized void methodB(){
Thread t = Thread.currentThread();
System.out.println(t+"正在调用方法B");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
System.out.println(t+"调用方法B完毕");
}
}```
**wait和notify**
多线程之间需要协调工作。
例如,浏览器的一个显示图片的 displayThread想要执行显示图片的任务,必须等待下载线程downloadThread将该图片下载完毕。如果图片还
没有下载完,displayThread可以暂停,当downloadThread完成了任务后,再通知displayThread“图片准备完毕,可以显示了”,这时,
displayThread继续执行。
以上逻辑简单的说就是:如果条件不满足,则等待。当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于
wait/notify。等待机制与锁机制是密切关联的
java多线程基础(synchronize关键字)的更多相关文章
- [转]Java多线程干货系列—(一)Java多线程基础
Java多线程干货系列—(一)Java多线程基础 字数7618 阅读1875 评论21 喜欢86 前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们 ...
- Java 多线程——基础知识
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- Java多线程基础知识总结
2016-07-18 15:40:51 Java 多线程基础 1. 线程和进程 1.1 进程的概念 进程是表示资源分配的基本单位,又是调度运行的基本单位.例如,用户运行自己的程序,系统就创建一个进程, ...
- Java 多线程基础(五)线程同步
Java 多线程基础(五)线程同步 当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题. 要解决上述多线程并发访问一个资源的安全性问题,Java中提供了同步机制 ...
- Java多线程基础:进程和线程之由来
转载: Java多线程基础:进程和线程之由来 在前面,已经介绍了Java的基础知识,现在我们来讨论一点稍微难一点的问题:Java并发编程.当然,Java并发编程涉及到很多方面的内容,不是一朝一夕就能够 ...
- Java多线程--基础概念
Java多线程--基础概念 必须知道的几个概念 同步和异步 同步方法一旦开始,调用者必须等到方法调用返回后,才能执行后续行为:而异步方法调用,一旦开始,方法调用就立即返回,调用者不用等待就可以继续执行 ...
- Java基础16:Java多线程基础最全总结
Java基础16:Java多线程基础最全总结 Java中的线程 Java之父对线程的定义是: 线程是一个独立执行的调用序列,同一个进程的线程在同一时刻共享一些系统资源(比如文件句柄等)也能访问同一个进 ...
- 1、Java多线程基础:进程和线程之由来
Java多线程基础:进程和线程之由来 在前面,已经介绍了Java的基础知识,现在我们来讨论一点稍微难一点的问题:Java并发编程.当然,Java并发编程涉及到很多方面的内容,不是一朝一夕就能够融会贯通 ...
- Java 多线程基础(一)基本概念
Java 多线程基础(一)基本概念 一.并发与并行 1.并发:指两个或多个事件在同一个时间段内发生. 2.并行:指两个或多个事件在同一时刻发生(同时发生). 在操作系统中,安装了多个程序,并发指的是在 ...
- Java 多线程基础(三) start() 和 run()
Java 多线程基础(三) start() 和 run() 通过之前的学习可以看到,创建多线程过程中,最常用的便是 Thread 类中的 start() 方法和线程类的 run() 方法.两个方法都包 ...
随机推荐
- POJ 2502 Subway-经过预处理的最短路
Description You have just moved from a quiet Waterloo neighbourhood to a big, noisy city. Instead of ...
- QQ日迹Omi实战开发,从0到1
写在前面 相信大家对Omi应该都不陌生了,如果还有不了解的同学先看看这里.了解并使用Omi之后你会发现真的回不去了~~~ 精通Omi框架 先简单说一下吧,Omi就是一个可以快速开发项目的组件化框架,和 ...
- 深入 HTML5 Web Worker 应用实践:多线程编程
深入 HTML5 Web Worker 应用实践:多线程编程 HTML5 中工作线程(Web Worker)简介 至 2008 年 W3C 制定出第一个 HTML5 草案开始,HTML5 承载了越来越 ...
- PM2源码浅析
PM2工作原理 最近在玩一个游戏,<地平线:黎明时分>,最终Boss是一名叫黑底斯的人,所谓为人,也许不对,黑底斯是一段强大的毁灭进程,破坏了盖娅主进程,从而引发的整个大陆机械兽劣化故事. ...
- LuaFramework内存资源管理器ResourceManger详解及切换场景资源清理
1.成员变量 m_BaseDownloadingURL : 获取资源的地方,加载AssetBundle包的时候会用到 m_AssetBundleManifest : 包间依赖关系文件,从这个类中的信息 ...
- python爬虫从入门到放弃(一)之初识爬虫
整理这个文档的初衷是自己开始学习的时候没有找到好的教程和文本资料,自己整理一份这样的资料希望能对小伙伴有帮助 什么是爬虫? 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页 ...
- (转)mysql水平分表和垂直分表和数据库分区
坚信数据库的物理设计在对高级数据库的性能影响上远比其他因素重要.给大家说一下经过专家对Oracle的研究,他们解释了为什么拙劣的物理设计是数据库停机(无论是有计划的还是没计划的)背后的主要原因.但在这 ...
- 【MYSQL】ubuntu13安装mysql(转)
安装的是mysql-5.0.87-linux-i686-glibc23.tar.gz 1.解压tar.gz tar –xzf mysql-5.0.87-linux-i686-glibc23.tar.g ...
- EF 数据库迁移(Migration)
Update-Database -ConnectionStringName "MyConnectionString"
- 如何使用命令行cmd执行java程序
如果你的电脑上没有像idea eclipse这类的IDE,但是因为工作需要必须要执行java代码怎么办呢? 这个时候就需要使用电脑最原始的执行方式 既命令行 1:首先你得安装了jdk与jre (这里就 ...