进程与线程

在学习Java多线程之前,先简单复习一下进程与线程的知识。

进程:进程是系统进行资源分配和调度的基本单位,可以将进程理解为一个正在执行的程序,比如一款游戏。

线程:线程是程序执行的最小单位,一个进程可由一个或多个线程组成,在一款运行的游戏中通常会有界面

   更新线程、游戏逻辑线程等,线程切换的开销远小于进程切换的开销。

                   图1

在图1中,蓝色框表示进程,黄色框表示线程。进程拥有代码、数据等资源,这些资源是共享的,3个线程都可

以访问,同时每个线程又拥有私有的栈空间。

Java线程状态图

线程的五种状态:

  1)新建状态(New):线程对象实例化后就进入了新建状态。

  2)就绪状态(Runnable):线程对象实例化后,其他线程调用了该对象的start()方法,虚拟机便会启

     动该线程,处于就绪状态的线程随时可能被调度执行。

  3)运行状态(Running):线程获得了时间片,开始执行。只能从就绪状态进入运行状态。

  4)阻塞状态(Blocked):线程因为某个原因暂停执行,并让出CPU的使用权后便进入了阻塞状态。

    等待阻塞:调用运行线程的wait()方法,虚拟机会把该线程放入等待池。

    同步阻塞:运行线程获取对象的同步锁时,该锁已被其他线程获得,虚拟机会把该线程放入锁定池。

    其他线程:调用运行线程的sleep()方法或join()方法,或线程发出I/O请求时,进入阻塞状态。

  5)结束状态(Dead):线程正常执行完或异常退出时,进入了结束状态。

Java线程实现

Java语言提供了两种实现线程的方式:

1)通过继承Thread类实现线程

public class ThreadTest {
public static void main(String[] args){
Thread thread = new MyThread(); //创建线程
thread.start();  //启动线程
}
}
//继承Thread类
class MyThread extends Thread{
@Override
public void run() {
int count = 7;
while(count>0){
System.out.println(count);
try {
Thread.sleep(1000);  
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
}
}
}

2)通过实现Runnable接口实现线程

public class ThreadTest {
public static void main(String[] args){
Runnable runnable = new MyThread();
     //将Runnable对象传递给Thread构造器
Thread thread = new Thread(runnable);
thread.start();
}
}
//实现了Runnable接口
class MyThread implements Runnable{
@Override
public void run() {
int count = 7;
while(count>0){
System.out.println(count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
}
}
}

两种方式都覆写了run()方法,run()方法内定义了线程的执行内容,我们只能通过线程的start()方法来

启动线程,且start()方法只能调用一次,当线程进入执行状态时,虚拟机会回调线程的run()方法。直

接调用线程的run()方法,并不会启动线程,只会像普通方法一样去执行。

其实,Thread类本身也实现了Runnable接口。这两种方式都可以实现线程,但Java语言只支持单继

承,如果扩展了Thread类就无法再扩展其他类,远没有实现接口灵活。

线程常用方法

1)Thread类

  Thread():用于构造一个新的Thread。

  Thread(Runnable target):用于构造一个新的Thread,该线程使用了指定target的run方法。

  Thread(ThreadGroup group,Runnable target):用于在指定的线程组中构造一个新的Thread,该

  线程使用了指定target的run方法。

  currentThread():获得当前运行线程的对象引用。

  interrupt():将当前线程置为中断状态。

  sleep(long millis):使当前运行的线程进入睡眠状态,睡眠时间至少为指定毫秒数。

  join():等待这个线程结束,即在一个线程中调用other.join(),将等待other线程结束后才继续本线程。

  yield():当前执行的线程让出CPU的使用权,从运行状态进入就绪状态,让其他就绪线程执行。

2)Object类

  wait():让当前线程进入等待阻塞状态,直到其他线程调用了此对象的notify()或notifyAll()方法后,当

  前线程才被唤醒进入就绪状态。

  notify():唤醒在此对象监控器上等待的单个线程。

  notifyAll():唤醒在此对象监控器上等待的所以线程。

注:wait()、notify()、notifyAll()都依赖于同步锁,而同步锁是对象持有的,且每个对象只有一个,所以

    这些方法定义在Object类中,而不是Thread类中。

3)yield()、sleep()、wait()比较

   wait():让线程从运行状态进入等待阻塞状态,并且会释放它所持有的同步锁。

   yield():让线程从运行状态进入就绪状态,不会释放它锁持有的同步锁。

   sleep():让线程从运行状态进入阻塞状态,不会释放它锁持有的同步锁。

线程同步

先看一个多线程模拟卖票的例子,总票数7张,两个线程同时卖票:

public class ThreadTest{
public static void main(String[] args){
Runnable r = new MyThread();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
class MyThread implements Runnable{
private int tickets = 7; //票数
@Override
public void run(){
while(tickets>0){
System.out.println("tickets:"+tickets);
tickets--;
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}

运行结果:

运行结果不符合我们的预期,因为两个线程使用共享变量tickets,存在着由于交叉操作而破坏数据的可能性,

这种潜在的干扰被称作临界区,通过同步对临界区的访问可以避免这种干扰。

在Java语言中,每个对象都有与之关联的同步锁,并且可以通过使用synchronized方法或语句来获取或释放

这个锁。在多线程协作时,如果涉及到对共享对象的访问,在访问对象之前,线程必须获取到该对象的同步

锁,获取到同步锁后可以阻止其他线程获得这个锁,直到持有锁的线程释放掉锁为止。

public class ThreadTest{
public static void main(String[] args){
Runnable r = new MyThread();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
class MyThread implements Runnable{
private int tickets = 7;
@Override
public void run(){
while(tickets>0){
synchronized(this){ //获取当前对象的同步锁
if(tickets>0){
System.out.println("tickets:"+tickets);
tickets--;
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
}
}

运行结果:

synchronized用法:

1)synchronized方法:如果一个线程要在某个对象上调用synchronized方法,那么它必须先获取这个对象的

锁,然后执行方法体,最后释放这个对象上的锁,而与此同时,在同一个对象上调用synchronized方法的其他

线程将阻塞,直到这个对象的锁被释放为止。

public synchronized void show(){
System.out.println("hello world");
}

2)synchronized静态方法:静态方法也可以被声明为synchronized的,每个类都有与之相关联的Class对象,

而静态同步方法获取的就是它所属类的Class对象上的锁,两个线程不能同时执行同一个类的静态同步方法,如

果静态数据是在线程之间共享的,那么对它的访问就必须利用静态同步方法来进行保护。

3)synchronized语句:静态语句可以使我们获取任何对象上的锁而不仅仅是当前对象上的锁,也能够让我们定

义比方法还要小的同步代码区,这样可以让线程持有锁的时间尽可能短,从而提高性能。

private final Object lock = new Object();
public void show(){
synchronized(lock){
System.out.println("hello world");
}
}

  

   

Java 多线程入门的更多相关文章

  1. java多线程入门学习(一)

    java多线程入门学习(一) 一.java多线程之前 进程:每一个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销.一个进程包括1--n个线程.     线程:同一类线程共享代码 ...

  2. Java多线程学习(一)Java多线程入门

    转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79640870 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...

  3. (转载)Java多线程入门理解

    转载出处http://blog.csdn.net/evankaka 写在前面的话:此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更 ...

  4. java多线程入门

    一.认识多任务.多进程.单线程.多线程 要认识多线程就要从操作系统的原理说起.   以前古老的DOS操作系统(V 6.22)是单任务的,还没有线程的概念,系统在每次只能做一件事情.比如你在copy东西 ...

  5. Java多线程入门知识点梳理

    前言 在多核时代,高并发时代,对系统并行处理能力有很高要求.多线程就是这个时代最好的产物.通过使用多线程可以增强系统并行处理能力,提高CPU资源的有效利用:从而提高系统的处理能力.常见应用场景如:多窗 ...

  6. Java多线程入门及实战

    基本概念: 1: 程序 2 进程 3 线程 4 进程和线程的区别 5 进程和程序的区别 Java实现多线程的方法: 1 继承Thread 2 实现Runable 3 实现callable 4 线程池的 ...

  7. Java多线程入门中几个常用的方法

    一.currentThread()方法 currentThread方法就是返回当前被调用的线程. 该方法为一个本地方法,原码如下: /** * Returns a reference to the c ...

  8. Java多线程入门Ⅱ

    线程的让步 线程让出自己占用的CPU资源 线程让出资源,不指定让给谁 线程让出资源,指定让给谁 方法1: public static void yield(); 线程实现交替打印 import jav ...

  9. Java多线程学习(四)等待/通知(wait/notify)机制

    转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79690279 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...

随机推荐

  1. Java多线程之线程的同步

    Java多线程之线程的同步 实际开发中我们也经常提到说线程安全问题,那么什么是线程安全问题呢? 线程不安全就是说在多线程编程中出现了错误情况,由于系统的线程调度具有一定的随机性,当使用多个线程来访问同 ...

  2. linkin大话面向对象--闭包和回调

      先来理解2个概念:闭包和回调   什么是闭包? 闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建他的作用域.通过这个定义,可以看出内部类是面向对象的闭包,因为他不仅包含了外部类对象的信 ...

  3. mavean的依赖传递和排除依赖

    三个mavean项目 A  .B. C 如果B依赖A(A先执行clean package命令) 那么B得pom.xml文件里面就写 <dependency> <groupId> ...

  4. android view控件的显示和隐藏动画效果

    // 显示动画 mShowAction = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO ...

  5. $.ajax()实现简单计算器

    1.html页面  a.html <!DOCTYPE html> <html lang="en"> <head> <meta charse ...

  6. 编译原理-NFA构造DFA

    本题摘自北邮的编译原理与技术. 首先,根据此图构造状态转换表 表中第一列第一行表示从第一个符号B通过任意个空转换能到达的节点,Ia表示由此行的状态数组({B,5,1}可以看作0状态)经过一个a可以到达 ...

  7. 新awk整理

    总感觉上一篇awk的总结几乎是照着man翻译过来的,惨不忍睹 无意间在互联网上有找到了宝贵的资料 感觉整理的很好,想着照着这个来重新写下,对照新的man更新下吧,只是总是在改变的 一.awk简介二.a ...

  8. awk之NR==FNR问题

    NR,表示awk开始执行程序后所读取的数据行数. FNR,与NR功用类似,不同的是awk每打开一个新文件,FNR便从0重新累计. 下面看两个例子: 1,对于单个文件NR 和FNR 的 输出结果一样的 ...

  9. COGNOS安装与发布报表步骤

    1. 安装 1.1安装前准备 安装COGNOS需要先安装好iis和sql Server(因为我们用的数据库系统就是SQLServer). 1.2安装过程 1)    找到BI Server文件 2)  ...

  10. MySQL用户授权与权限

    MySQL权限如下表 权限名字 权限说明 Context CREATE 允许创建新的数据库和表 Databases, tables, or indexes DROP 允许删除现有数据库.表和视图 Da ...