目录

  • 基础知识
  • 多线程实现方法
  • 线程的方法
  • 线程同步

基础知识

线程:进程(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{@Overridepublic 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{@Overridepublic 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。等待机制与锁机制是密切关联的

转载于:https://www.cnblogs.com/yangliguo/p/7398993.html

java多线程基础(synchronize关键字)相关推荐

  1. JAVA多线程基础篇-关键字synchronized

    1.概述 syncronized是JAVA多线程开发中一个重要的知识点,涉及到多线程开发,多多少少都使用过.那么syncronized底层是如何实现的?为什么加了它就能实现资源串行访问?本文将基于上述 ...

  2. Java多线程干货系列(1):Java多线程基础

    转载自  Java多线程干货系列(1):Java多线程基础 前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本次的学 ...

  3. java多线程基础学习[狂神说java-多线程笔记]

    java多线程基础学习 一.线程简介 1.类比 2.程序进程线程 3.线程的核心概念 二.线程的实现(重点) 调用方法与调用多线程的区别 Thread 类 1.thread使用方法 2. 代码实现 3 ...

  4. 爬梯:Java多线程基础

    学习资源:狂神说 Java多线程基础 1.多线程概述 Process 进程 一个进程可以有多个线程. Thread 线程 线程就是独立的执行路径 在程序运行时,即使没有自己创建线程,后台也会有多个线程 ...

  5. Java多线程基础-6:线程安全问题及解决措施,synchronized关键字与volatile关键字

    线程安全问题是多线程编程中最典型的一类问题之一.如果多线程环境下代码运行的结果是符合我们预期的,即该结果正是在单线程环境中应该出现的结果,则说这个程序是线程安全的. 通俗来说,线程不安全指的就是某一代 ...

  6. Java多线程干货系列—(一)Java多线程基础

    前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们来说极其重要,下面跟我一起开启本次的学习之旅吧. 正文 线程与进程 1 线程:进程中负责程序执行的 ...

  7. 一篇文章弄懂Java多线程基础和Java内存模型

    文章目录 一.多线程的生命周期及五种基本状态 二.Java多线程的创建及启动 1.继承Thread类,重写该类的run()方法 2.通过实现Runnable接口创建线程类 3.通过Callable和F ...

  8. java多线程基础视频_【No996】2020年最新 Java多线程编程核心基础视频课程

    01.课程介绍.mp4 02.多线程编程基础-进程与线程.mp4 03.多线程编程基础-使用多线程-继承Thread类.mp4 04.多线程编程基础-使用多线程-实现Runnable接口.mp4 05 ...

  9. 【Java进阶营】JAVA多线程基础学习二:synchronized

    本篇主要介绍Java多线程中的同步,也就是如何在Java语言中写出线程安全的程序,如何在Java语言中解决非线程安全的相关问题,没错就是使用synchronized. 一.如何解决线程安全问题? 一般 ...

  10. Java多线程基础知识(一)

    Java多线程 一.Java线程模型 实现线程有三种方式:使用内核线程实现.使用用户线程实现和使用用户线程加轻量级进程混合实现.内核线程是直接由操作系统内核支持的线程,通过内核完成线程切换,内核通过操 ...

最新文章

  1. 关于MySQL的酸与MVCC和面试官小战三十回合
  2. python操作系统-Python_操作系统的发展史
  3. NHibernate配置引发的异常
  4. 汇编语言学习工具Dosbox的安装与配置(一)
  5. 常用的adb命令收集
  6. 独行速众行远,BitTitan携手世纪互联蓝云助力用户数据完美迁移
  7. appian php,aha2mao
  8. 美团技术leader:写给工程师的十条精进原则
  9. 关于Initializing Spring root WebApplicationContext解决方法
  10. playframework 2.6 refused to apply inline style because it violates the following Content Security
  11. php redis缓存雪崩,redis雪崩是什么
  12. LocalDateTime日期格式之间的转换
  13. 【快速文档】view标签,小程序中的div
  14. 一、Python复习教程(重点)- 基础
  15. codewars练习(javascript)-2021/3/14
  16. Mac功夫:OS X 的300多个技巧和小窍门
  17. 南下打工潮正在远去!去年千万人返乡,农业的大转折正在到来
  18. 【LeetCode - 702】搜索长度未知的有序数组
  19. Mac苹果电脑如何一键清理磁盘内存空间?
  20. 基于Arduino的水位检测系统

热门文章

  1. linux登录用户who,Linux用户登录查看命令总结 - w,who,last,lastlog
  2. 【渝粤教育】国家开放大学2018年秋季 0708-22T互联网创业基础 参考试题
  3. 【渝粤教育】国家开放大学2018年秋季 2094T法理学 参考试题
  4. Baxter实战 (四)ubuntu14.04安装kinect V2
  5. 控制系统数字仿真与CAD-第二次实验-附完整代码
  6. 知乎上的48条神回复,针针见血
  7. spring+mybatis+log4j 输出SQL
  8. CSS margin合并
  9. [贪心+模拟] zoj 3829 Known Notation
  10. [转]如何撰写学术论文