全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

深入讲解java线程与synchronized关键字

我们将会从以下的几点理解java线程的一些概念:

  1. 线程的基本概念和优劣之处
  2. 创建一个线程的两种方式
  3. 线程的属性
  4. 线程的状态
  5. synchronized可修饰的方法
  6. synchronized的重要特性

一、线程的基本概念

在计算机中有进程和线程这么两个概念,进程中可以有多个线程,它们是从属关系,进程往往更像是资源的占有者,线程才是程序的执行者,多个线程之间共享着进程中的资源。一个cpu同时只能运行一个线程,每个线程都有一个时间片,时间片用完了就会被阻塞并让出CPU的控制权,交给下一个线程使用。这样在计算机中就可以实现多任务的假象,其实CPU在不断的切换线程,好像多个任务在同时运行。

使用线程的优势毋庸置疑,可以增加CPU的执行效率,一旦某个线程需要等待某种资源(例如:等待打印机),就可以将它阻塞释放CPU让CPU执行别的线程,而不需要让CPU和此线程一起等待某种资源从而提高系统效率,另外一点就是可以用这种假象增加用户体验度。但是,CPU在切换不同线程之间所要花费的代价也是不可忽视的,在较为复杂的程序中这种劣势可能九流一毛,但是如果在简单的程序中就会显得尤为突出。

二、创建一个线程

接下来我们看看如何在java中创建一个线程来实现多个线程同时运行。第一种方式,java 中有一个类Thread,我们只要继承这个类并重写他的run方法,调用start方法就可以启动一个新的线程了。(没见过的同学可能不能理解以下代码,下面我会解释)

 /*声明自己的一个线程类*/
public class Test_thread extends Thread {
 //重写Thread类中的run方法
 public void run(){
 System.out.println("i am the thread");
 }
}

public class Test_Class {
 public static void main(String[] args){
 Test_thread thread = new Test_thread();
 thread.start();
 }
}

输出结果:i am the thread

首先我们先了解一下,一个线程被创建之后,怎么才能启动运行,我们调用thread.start();方法启动一个线程,首先就会执行我们重写的run方法(如果没有重写就会调用Thread类的run方法,什么也不做,这也是我们重写run方法的原因),也就是说run方法是一个线程的开始。有个疑问,为什么调用start方法,却执行了run方法了?其实调用start方法就是为线程的启动做准备操作,分配线程私有的堆栈资源,然后执行run方法。

下面我们看创建一个线程的第二种方式,实现接口Runnable,并重写其中的run方法。

 public class Test_thread implements Runnable {
 public void run(){
  System.out.println("i am the thread");
 }
}

public class Test_Class {
 public static void main(String[] args){
  Test_thread thread = new Test_thread();
  thread.start();//编译错误
 }
}

我们会发现虽然重写了run方法,但是在调用start方法的时候却编译错误,我们进入到Runnable接口的源代码中可以看到,只有一个抽象方法run,所以没有启动线程的start方法,所以我们还是要借助Tread类。

 public class Test_Class {
 public static void main(String[] args){
  Thread thread = new Thread(new Test_thread());
  thread.start();
 }
}

因为Thread类中有start方法,所以可以使用Thread的一个构造函数传入一个实现了Runnable接口的类型,构建一个Thread类。

三、线程的属性和状态

在一个多线程的程序中我们使用线程的一些属性来区别和辨认它们:

private long tid;
private volatile char name[];
public static native Thread currentThread()
private boolean  daemon = false;
private volatile int threadStatus = 0;
public final static int NORM_PRIORITY = 5;

每个线程会有一个tid指定此线程的在所有线程中的排序,这个值是递增的,还有一个name指定该线程的名字,也可以使用setName设置一个优雅的线程名字,Thread类还提供了一个方法返回当前正在占用CPU的线程对象。每个线程在某个时刻都是有状态的,属性threadStatus记录了当前对象线程的状态是什么,默认情况下,所有的线程的优先级都被置为5,如果有特殊需要可以修改线程的优先级,使得某个线程可以优先得到运行。下面介绍线程的几种不同的状态。

  1. New:线程刚刚被创建,还没有被启动。
  2. Runnable:线程处于可运行的状态,但是不一定处于正在运行的状态(后面解释区别)
  3. Blocked:线程处于被阻塞的状态,一般是请求某资源不成功于是让出CPU阻塞自己(具体的区别后面说)
  4. Waiting:线程处于等待状态,一般是等待服务
  5. Terminated:线程终止,也就是线程被kill,释放其占用的资源

下面具体的说说不同状态下的线程的一些操作,首先看看new,线程从此被创建,只是离运行状态还需要一些准备。Runnable表示线程是可运行,至于是否已经处于运行状态还要看系统给的时间片是否用完,如果用完了就会将此线程放置在可运行线程队列的尾部,等待下次分配时间片,如果时间片没有用完,就是处于运行状态的(这也是为什么叫做Runnable而不是Running的原因)。接下来的两种状态Blocked和waiting都表示线程阻塞,需要让出CPU。只是导致它们处于这种状态的原因不一样,具体的在我们介绍完synchronized关键字之后就会不言而喻了。

四、关键字synchronized

先看一段代码:

 public class Test_thread extends Thread{
 public static int count = 0;
 public void run(){
  try {
   Thread.sleep((int) (Math.random() * 100));
  }catch(InterruptedException e){

  }
  count++;
 }
}

public class Test_Class {
 public static void main(String[] args){
  Test_thread[] thread = new Test_thread[1000];
  for(int a=0;a<1000;a++){
   thread[a] = new Test_thread();
   thread[a].start();
  }

  for(int a=0;a<1000;a++){
   try {
    thread[a].join();
   }catch (InterruptedException e){

   }
  }
  System.out.println(Test_thread.count);
 }
}

按照直觉,创建1000个线程,每个线程随机睡觉并将公共变量增一,main线程等待所有线程执行结束之后,输出公共变量的值。按照直觉答案应该是1000,但是我们运行的结果每次都是不一样的,接近1000但每次都不一样。这是为什么呢?这其实就是简单的模拟了高并发,可能有几个线程睡了相同的时间,同时醒来获取的count值是相同的,这就导致这几个线程对count的操作被覆盖了。

 public class Test_thread extends Thread{
 public static int count = 0;
 public void run(){
  try {
   Thread.sleep((int) (Math.random() * 100));
  }catch(InterruptedException e){

  }
  /*使用关键字*/
  synchronized (Test_thread.class){
   count++;
  }
 }
}

一个简单的关键字就可以轻松解决这样的高并发的问题。至于为什么这么写?在介绍完synchronized关键字之后,想必你就会知道了。

首先我们需要知道,每个对象都有一把内部锁。所以被synchronized关键字修饰的方法,其实是被加了内部对象锁。我们看代码:

  public class Counter{
  private int count;

  /*为实例方法加此关键字*/
  public synchronized int getCount(){
   return count;
  }
 }

为实例方法添加关键字,实际上就是给当前的对象加锁;括号中就是要加锁的对象。

  public class Counter{
  private int count;

  /*为实例方法加此关键字*/
  synchronized(this){
   return count;
  }
 }

对于静态方法,实际上就是为这个类加上锁;

  public class Counter{
  private static int count;

  public static synchronized int getCount(){
   return count;
  }
 }

/*实际上给这个类加上锁*/
 public class Counter{
  private int count;

  synchronized(Counter.class){
   return count;
  }
 }

五、深入理解synchronized的一些特性

第一个性质是:可重入性。被synchronized修饰的方法中的所有操作都是原子操作,但是当我们需要在其中访问另外的一些需要锁的代码时候,可以直接获取别的锁。也就是当前的对象是可以获得多个锁的。

第二个性质是:内存的可见性。在我们的计算机中,其实有很多的操作并不是很"干脆"的,比如你向数据库中存数据时,其实很多时候一些待存入的数据积累在缓存中等到一定数据量的时候才会统一的存入数据库,但是在我们看来这些数据其实应该早就存入数据库了。这就导致了一个内存的不可见性问题。当然我们也是可以使用关键字synchronized来保证每次取出的数据都是最新的。在释放锁的时候会立即将数据协会内存,获得锁的时候会去读取最新的内容。

  public class Counter{
  private int count;

  public synchronized int getCount(){
   return count;
  }
 }
 /*每次获得该对象的锁之后,去获取最新的count数值*/

其实,如果仅仅是要保证内存的不可见性,使用synchronized关键字可能代价有点高,在这种情况下,我们可以使用关键字volatile。

  public class Counter{
  /*使用关键字volatile*/
  private volatile int count;

  public int getCount(){
   return count;
  }
 }
 /*此关键字保证每次使用count的时候数据都是最新的*/

总结

以上就是这篇文章的全部内容了,还是希望大家发现其中错误直接指出,方便我纠正错误,更新知识。java并发系列文章,希望大家多多关注。


# java线程synchronized  # synchronized关键字  # java多线程关键字  # Java的Synchronized关键字学习指南(全面 & 详细)  # 深入分析JAVA Synchronized关键字  # Java synchronized关键字使用方式及特性解析  # Java synchronized关键字和Lock接口实现原理  # Java中synchronized关键字引出的多种锁 问题  # Java多线程并发编程 Synchronized关键字  # 实例解析Java中的synchronized关键字与线程安全问题  # Java中synchronized关键字修饰方法同步的用法详解  # Java多线程编程中synchronized关键字的基础用法讲解  # 详解Java中synchronized关键字的死锁和内存占用问题  # 举例讲解Java中synchronized关键字的用法  # Java多线程之synchronized关键字的使用  # 就会  # 重写  # 多个  # 都是  # 可以使用  # 用完  # 创建一个  # 都有  # 就可以  # 两种  # 中有  # 这就  # 运行状态  # 见性  # 基本概念  # 自己的  # 加锁  # 类中  # 是一个  # 这是 


相关文章: 如何在万网开始建站?分步指南解析  购物网站制作费用多少,开办网上购物网站,需要办理哪些手续?  如何安全更换建站之星模板并保留数据?  如何在七牛云存储上搭建网站并设置自定义域名?  外汇网站制作流程,如何在工商银行网站上做外汇买卖?  网站制作软件免费下载安装,有哪些免费下载的软件网站?  东莞专业制作网站的公司,东莞大学生网的网址是什么?  定制建站方案优化指南:企业官网开发与建站费用解析  制作网站外包平台,自动化接单网站有哪些?  如何在云主机上快速搭建多站点网站?  哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?  *服务器网站为何频现安全漏洞?  网站专业制作公司有哪些,做一个公司网站要多少钱?  如何快速搭建FTP站点实现文件共享?  定制建站哪家更专业可靠?推荐榜单揭晓  济南网站建设制作公司,室内设计网站一般都有哪些功能?  南京网站制作费用,南京远驱官方网站?  零服务器AI建站解决方案:快速部署与云端平台低成本实践  如何在橙子建站上传落地页?操作指南详解  江苏网站制作公司有哪些,江苏书法考级官方网站?  如何挑选高效建站主机与优质域名?  如何在宝塔面板创建新站点?  如何在宝塔面板中创建新站点?  手机网站制作与建设方案,手机网站如何建设?  公司网站制作需要多少钱,找人做公司网站需要多少钱?  如何通过FTP空间快速搭建安全高效网站?  C++如何将C风格字符串(char*)转换为std::string?(代码示例)  微信网站制作公司有哪些,民生银行办理公司开户怎么在微信网页上查询进度?  海南网站制作公司有哪些,海口网是哪家的?  临沂网站制作公司有哪些,临沂第四中学官网?  厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?  如何快速查询网站的真实建站时间?  建站主机如何选?高性价比方案全解析  建站168自助建站系统:快速模板定制与SEO优化指南  如何在阿里云服务器自主搭建网站?  c++怎么编写动态链接库dll_c++ __declspec(dllexport)导出与调用【方法】  如何用5美元大硬盘VPS安全高效搭建个人网站?  建站之星后台密码遗忘或太弱?如何重置与强化?  电影网站制作价格表,那些提供免费电影的网站,他们是怎么盈利的?  TestNG的testng.xml配置文件怎么写  香港服务器网站搭建教程-电商部署、配置优化与安全稳定指南  制作无缝贴图网站有哪些,3dmax无缝贴图怎么调?  如何基于云服务器快速搭建个人网站?  javascript中的try catch异常捕获机制用法分析  建站IDE高效指南:快速搭建+SEO优化+自适应模板全解析  建站DNS解析失败?如何正确配置域名服务器?  专业型网站制作公司有哪些,我设计专业的,谁给推荐几个设计师兼职类的网站?  如何在IIS中新建站点并配置端口与IP地址?  ,网站推广常用方法?  网站制作哪家好,cc、.co、.cm哪个域名更适合做网站? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。