「java工作线程」java线程 操作系统线程
今天给各位分享java工作线程的知识,其中也会对java线程 操作系统线程进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!
本文目录一览:
Java线程的概念与原理
一 操作系统中线程和进程的概念
现在的操作系统是多任务操作系统 多线程是实现多任务的一种方式
进程是指一个内存中运行的应用程序 每个进程都有自己独立的一块内存空间 一个进程中可以启动多个线程 比如在Windows系统中 一个运行的exe就是一个进程 线程是指进程中的一个执行流程 一个进程中可以运行多个线程 比如java exe进程中可以运行很多线程 线程总是属于某个进程 进程中的多个线程共享进程的内存 同时 执行是人的感觉 在线程之间实际上轮换执行
二 Java中的线程
在Java中 线程 指两件不同的事情
java lang Thread类的一个实例
线程的执行
使用java lang Thread类或者java lang Runnable接口编写代码来定义 实例化和启动新线程 一个Thread类实例只是一个对象 像Java中的任何其他对象一样 具有变量和方法 生死于堆上 Java中 每个线程都有一个调用栈 即使不在程序中创建任何新的线程 线程也在后台运行着 一个Java应用总是从main()方法开始运行 mian()方法运行在一个线程内 它被称为主线程 一旦创建一个新的线程 就产生一个新的调用栈 线程总体分两类 用户线程和守候线程
当所有用户线程执行完毕的时候 JVM自动关闭 但是守候线程却不独立于JVM 守候线程一般是由操作系统或者用户自己创建的
———————————MultiT java——————————————————————
class MultiThread
{
public static void main(String[] args)
{
MyThread mt=new MyThread();
//mt setDaemon(true);//设定为后台线程 main进程结束时 后台进程也跟着结束
//mt setPriority(Thread MAX_PRIORITY); //设定线程优先级 MAX_PRIORITY为 MIN_PRIORITY为 NORM_PRIORITY为
//设定为最高优先级后 程序运行时 mt线程一直运行 强制终止时 main线程才运行
//设定为最高优先级的线程 无论有无yield(); 线程总一直运行 直到强制终止时 main和mt线程交替运行
mt start();
int index= ;
while(true) //显示结果与教程不同
{
if(index++== )
break;
System out println( main: +Thread currentThread() getName()); //获取线程名字
}
}
}
class MyThread extends Thread
{
public void run()
{
while(true)
{
System out println(getName());
yield(); //允许当前线程停止 转去执行其他线程 静态方法
//mt进程执行时 切换到main进程 main进程执行一段时间后
//切换进程到mt mt执行完获取名字后 返回到main进程
}
}
}
//一个长时间处于等待状态的线程也有可能被线程调度器调度 从而运行
//打破高优先级线程始终获有运行时间的状态
——————————————————————————————————————
——————————MultiThread java———————————————————————
class MultiThread
{
public static void main(String[] args)
{
MyThread mt=new MyThread();
//new Thread(mt) start(); //创建多个同样的线程访问同一个变量index 若MyThread采用继承Thread方式 则无法共享同一个变量
//new Thread(mt) start();
//new Thread(mt) start();
//new Thread(mt) start();
mt getThread() start(); //也可以采用内部类的方式共享访问同一个变量
mt getThread() start();
mt getThread() start();
mt getThread() start();
//mt setDaemon(true);//设定为后台线程 main进程结束时 后台进程也跟着结束
//mt setPriority(Thread MAX_PRIORITY); //设定线程优先级 MAX_PRIORITY为 MIN_PRIORITY为 NORM_PRIORITY为
//设定为最高优先级后 程序运行时 mt线程一直运行 强制终止时 main线程才运行
//设定为最高优先级的线程 无论有无yield(); 线程总一直运行 直到强制终止时 main和mt线程交替运行
//mt start();
int index= ;
while(true) //显示结果与教程不同
{
// if(index++== )
// break;
System out println( main: +Thread currentThread() getName()); //获取线程名字
}
}
}
class MyThread //implements Runnable //extends Thread //使用外部类的方式
//使用内部类完成使用Runnable接口才能完成的两个功能 a 创建多个线程 b 访问同一个变量
{
int index= ;
private class InnerThread extends Thread //不想让外部访问其实现方法 加上private
{
public void run()
{
while(true)
{
System out println(Thread currentThread() getName()+ : +index++);
}
}
}
Thread getThread()
{
return new InnerThread();
}
/*
public void run()
{
while(true)
{
System out println(Thread currentThread() getName()+ : +index++);
//yield(); //允许当前线程停止 转去执行其他线程 静态方法
//mt进程执行时 切换到main进程 main进程执行一段时间后
//切换进程到mt mt执行完获取名字后 返回到main进程
}
}
*/
}
//一个长时间处于等待状态的线程也有可能被线程调度器调度 从而运行
//打破高优先级线程始终获有运行时间的状态
//如果不需要修改Thread类的除了run方法外的其他方法 选用implements Runnable
———————————————————————————————————————
———————————TicketsSystem java———————————————————
//多线程实现火车票的售票系统 用同步块 或著同步方法
class TicketsSystem
{
public static void main(String[] args) //运行结果与教程中不同 不完全顺序 每次运行 顺序都不完全一样
{
SellThread st=new SellThread();//创建四个线程访问同一变量tickets
// 错 SellThread st =new SellThread();//若采用创建四个对象的方式 则每个对象中都有 张票
new Thread(st) start(); //b为false 用的同步方法 | //同步方法与同步块共用中 显示的是只调用了同步块 而同步方法未被调用
//b为true 用的同步块 | //原因 启动第一个线程后 CPU时间片没有到期 线程没有立即运行 接着执行b=true
// | //解决办法 启动第一个线程后 执行一个睡眠时间 让CPU时间片到期
try
{
Thread sleep( );
}
catch(Exception e)
{
e printStackTrace();
}
st b=true;
new Thread(st) start();
//new Thread(st) start();
//new Thread(st) start();
}
}
class SellThread implements Runnable //程序有点小问题 当剩下最后一张票时 四个线程都运行 可能会出现票数为 (系统长时间运行时)
//可加上一个静态方法sleep();它会抛出异常
{
int tickets= ;
//Object obj=new Object();//也可以声明一个Thread对象
Thread th=new Thread();
boolean b=false;
public void run()
{
if(b==false)
{
while(true)
sell();
}
else
{
while(true)
{ //同步方法利用的是this所代表的对象的锁
synchronized(this) //采用同步后 显示正确 此方法两步 声明Thread对象 用synchronized把原方法括起来
{ //这里换th为this
///*
if(tickets )
{
try
{
Thread sleep( );
}
catch(Exception e)
{
e printStackTrace();
}
System out println( th +Thread currentThread() getName()+ sell tickets: +tickets);
tickets ;
}
//*/
}
}
}
}
public synchronized void sell() //每个class也有一个锁 是这个class所对应的class对象的锁(监视器)
{
if(tickets )
{
try
{
Thread sleep( );
}
catch(Exception e)
{
e printStackTrace();
}
System out println( sell +Thread currentThread() getName()+ sell tickets: +tickets);
tickets ;
}
}
}
————————————————————————————————————————
———————————TestWN java————————————————————
class Test
{
public static void main(String[] args)
{
Queue q=new Queue();
Producer p=new Producer(q);
Consumer c=new Consumer(q);
p start();
c start();
}
}
class Producer extends Thread
{
Queue q;
Producer(Queue q)
{
this q=q;
}
public void run()
{
for(int i= ;i ;i++)
{
q put(i);
System out println( Producer put: +i);
}
}
}
class Consumer extends Thread
{
Queue q;
Consumer(Queue q)
{
this q=q;
}
public void run()
{
while(true)
{
System out println( Consumer get: +q get());
}
}
}
class Queue //wait notify 方法必须用在同步方法中 要加上关键字synchronized
{
int value;
boolean bFull=false;
public synchronized void put(int i)
{
if(!bFull)
{
value=i;
bFull=true;
notify();
}
try
{
wait();
}
catch(Exception e)
{
e printStackTrace();
}
}
public synchronized int get()
{
if(!bFull)
{
try
{
wait();
}
catch(Exception e)
{
e printStackTrace();
}
}
bFull=false;
notify();
return value;
}
}
————————————————————————————————————
————————————TestThread java———————————————————————
class TestThread
{
public static void main(String[] args)
{
Thread t =new Thread ();
t start();
int index= ;
while(true)
{
if(index++== )
{
t stopThread();
t interrupt(); //让线程 终止
break;
}
System out println(Thread currentThread() getName());
}
System out println( main() exit );
}
}
class Thread extends Thread
{
private boolean bStop=false;
public synchronized void run()
{
while(!bStop)
{
try
{
wait(); //加入wait后 main线程结束时 程序还未终止 原因是Thread 的线程调用wait方法 进入对象的等待队列中 需要notify方法将它唤醒
}
catch(Exception e)
{
//e printStackTrace();
if(bStop)
return;
}
System out println(getName());
}
}
public void stopThread()
{
bStop=true;
}
}
lishixinzhi/Article/program/Java/gj/201311/27407
java中什么叫做线程?什么叫多线程?多线程的特点是什么?
线程的概念:Thread 每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程。进程也可能是整个程序或者是部分程序的动态执行。
多线程的概念: 多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。
多线程的特点:使用线程可以把占据长时间的程序中的任务放到后台去处理
用户界面可以更加吸引人,这样比如用户点击了一个按钮去触发某些事件的处理,可以弹出一个进度条来显示处理的进度 。
程序的运行速度可能加快 ·在一些等待的任务实现上如用户输入、文件读写和网络收发数据等,线程就比较有用了。
在这种情况下我们可以释放一些珍贵的资源如内存占用等等。
线程定义比较抽象,简单的说就是一个代码执行流。许多执行流可以混合在一起由CPU调度。线程是允许各种任务交互执行的方式。
Java的线程在操作系统的实现模式依系统不同而不同,可能是系统级别的进程或线程,但对于程序员来说并没有影响。
任务交互的一个好处是增加程序响应。如一个界面程序执行一段耗时的数据库查询,使用单独的线程可以让界面依然响应用户的其他输入,而单线程只能等待查询结束再处理。
JVM以及操作系统会优先处理优先级别高的线程,但不代表这些线程一定会先完成。设定优先级只能建议系统更快的处理,而不能强制。
另外,在运行时,并没有按照函数分界,而是按照机器码/汇编码分界。也就是说不保证任何一段代码是被完整而不打断的执行的(除非你已经使用同步手段)。正由于如此,各种线程同步的方法应运而生。
什么叫做Java线程
在计算机中当一个程序运行的时候就会创建至少一个进程,例如当我们运行QQ的时候,系统就会创建进程来处理我们平时的一些操作,当我们打开任务管理器的时候,在进程的列表里面就可以找到QQ.exe的运行程序;
在计算机中处理进程之外还有另一个概念就是线程,线程是存在于进程当中,一个进程可以包含多个线程;当我们的计算机有多核处理器的时候,使用多线程可以加快程序的运算速率;如果一个进程中只有一个线程,当程序遇到一个比较耗时的计算的时候,由于程序是单线程的,那么程序只能等待这个运算结束的时候再继续运行,这样会大大的降低程序的效率;当时用多个线程的时候,在某个线程遇到比较耗时的运算的时候,该线程可以继续自己的运算,但是其他的线程也可以同步进行,这样当耗时的计算结束之后,其他线程也将自己所需要的东西执行完毕,这样就会很大的提高程序执行效率;
在程序运行中对于文件的保存相对于处理器的运算速度来说是很慢的,当我们程序中接收到一个保存文件的信息之后,我们可以创建一个保存文件的线程,在主线程中我们可以继续进行我们的其他运算,这样当文件保存好之后,我们的其他运算也会完成,互不影响;
在Java中我们可以创建一个自己的类继承于Thread类,并且重写run() 方法,当线程启动之后,run()方法里面的操作都在线程中进行处理,而不会影响主线程的信息;
当我们创建好一个自定义线程类之后,我们可以创建这个自定义线程的对象,进行线程的启动;线程须调用start();方法进行启动,这样run()方法里面的内容才会在线程中运行;如果我们不去调用start()方法,那我们只是创建了一个普通的类,即使我们手动调用run()方法,run()方法里面的内容也不会在线程中运行;
在Java中线程主要有初始状态,运行状态,阻塞状态,终止状态等;当我们新创建一个线程对象的时候,此时线程的状态为初始状态;当我们调用start()之后,此时的线程才被激活成为运行状态,之后run()方法里面的信息才会在子线程中运行;我们可以在不同的阶段调用不同的方法将线程设置为不同的状态;比如有时候我们的操作需要等待其他线程中运算结束之后才可以继续进行,这时候我们就可以将线程设置为等待状态,当需要的资源满足条件之后,可以继续运行当前的线程;
JAVA 线程
这是javaeye上非常经典的关于线程的帖子,写的非常通俗易懂的,适合任何读计算机的同学.
线程同步
我们可以在计算机上运行各种计算机软件程序。每一个运行的程序可能包括多个独立运行的线程(Thread)。
线程(Thread)是一份独立运行的程序,有自己专用的运行栈。线程有可能和其他线程共享一些资源,比如,内存,文件,数据库等。
当多个线程同时读写同一份共享资源的时候,可能会引起冲突。这时候,我们需要引入线程“同步”机制,即各位线程之间要有个先来后到,不能一窝蜂挤上去抢作一团。
同步这个词是从英文synchronize(使同时发生)翻译过来的。我也不明白为什么要用这个很容易引起误解的词。既然大家都这么用,咱们也就只好这么将就。
线程同步的真实意思和字面意思恰好相反。线程同步的真实意思,其实是“排队”:几个线程之间要排队,一个一个对共享资源进行操作,而不是同时进行操作。
因此,关于线程同步,需要牢牢记住的第一点是:线程同步就是线程排队。同步就是排队。线程同步的目的就是避免线程“同步”执行。这可真是个无聊的绕口令。
关于线程同步,需要牢牢记住的第二点是 “共享”这两个字。只有共享资源的读写访问才需要同步。如果不是共享资源,那么就根本没有同步的必要。
关于线程同步,需要牢牢记住的第三点是,只有“变量”才需要同步访问。如果共享的资源是固定不变的,那么就相当于“常量”,线程同时读取常量也不需要同步。至少一个线程修改共享资源,这样的情况下,线程之间就需要同步。
关于线程同步,需要牢牢记住的第四点是:多个线程访问共享资源的代码有可能是同一份代码,也有可能是不同的代码;无论是否执行同一份代码,只要这些线程的代码访问同一份可变的共享资源,这些线程之间就需要同步。
为了加深理解,下面举几个例子。
有两个采购员,他们的工作内容是相同的,都是遵循如下的步骤:
(1)到市场上去,寻找并购买有潜力的样品。
(2)回到公司,写报告。
这两个人的工作内容虽然一样,他们都需要购买样品,他们可能买到同样种类的样品,但是他们绝对不会购买到同一件样品,他们之间没有任何共享资源。所以,他们可以各自进行自己的工作,互不干扰。
这两个采购员就相当于两个线程;两个采购员遵循相同的工作步骤,相当于这两个线程执行同一段代码。
下面给这两个采购员增加一个工作步骤。采购员需要根据公司的“布告栏”上面公布的信息,安排自己的工作计划。
这两个采购员有可能同时走到布告栏的前面,同时观看布告栏上的信息。这一点问题都没有。因为布告栏是只读的,这两个采购员谁都不会去修改布告栏上写的信息。
下面增加一个角色。一个办公室行政人员这个时候,也走到了布告栏前面,准备修改布告栏上的信息。
如果行政人员先到达布告栏,并且正在修改布告栏的内容。两个采购员这个时候,恰好也到了。这两个采购员就必须等待行政人员完成修改之后,才能观看修改后的信息。
如果行政人员到达的时候,两个采购员已经在观看布告栏了。那么行政人员需要等待两个采购员把当前信息记录下来之后,才能够写上新的信息。
上述这两种情况,行政人员和采购员对布告栏的访问就需要进行同步。因为其中一个线程(行政人员)修改了共享资源(布告栏)。而且我们可以看到,行政人员的工作流程和采购员的工作流程(执行代码)完全不同,但是由于他们访问了同一份可变共享资源(布告栏),所以他们之间需要同步。
同步锁
前面讲了为什么要线程同步,下面我们就来看如何才能线程同步。
线程同步的基本实现思路还是比较容易理解的。我们可以给共享资源加一把锁,这把锁只有一把钥匙。哪个线程获取了这把钥匙,才有权利访问该共享资源。
生活中,我们也可能会遇到这样的例子。一些超市的外面提供了一些自动储物箱。每个储物箱都有一把锁,一把钥匙。人们可以使用那些带有钥匙的储物箱,把东西放到储物箱里面,把储物箱锁上,然后把钥匙拿走。这样,该储物箱就被锁住了,其他人不能再访问这个储物箱。(当然,真实的储物箱钥匙是可以被人拿走复制的,所以不要把贵重物品放在超市的储物箱里面。于是很多超市都采用了电子密码锁。)
线程同步锁这个模型看起来很直观。但是,还有一个严峻的问题没有解决,这个同步锁应该加在哪里?
当然是加在共享资源上了。反应快的读者一定会抢先回答。
没错,如果可能,我们当然尽量把同步锁加在共享资源上。一些比较完善的共享资源,比如,文件系统,数据库系统等,自身都提供了比较完善的同步锁机制。我们不用另外给这些资源加锁,这些资源自己就有锁。
但是,大部分情况下,我们在代码中访问的共享资源都是比较简单的共享对象。这些对象里面没有地方让我们加锁。
读者可能会提出建议:为什么不在每一个对象内部都增加一个新的区域,专门用来加锁呢?这种设计理论上当然也是可行的。问题在于,线程同步的情况并不是很普遍。如果因为这小概率事件,在所有对象内部都开辟一块锁空间,将会带来极大的空间浪费。得不偿失。
于是,现代的编程语言的设计思路都是把同步锁加在代码段上。确切的说,是把同步锁加在“访问共享资源的代码段”上。这一点一定要记住,同步锁是加在代码段上的。
同步锁加在代码段上,就很好地解决了上述的空间浪费问题。但是却增加了模型的复杂度,也增加了我们的理解难度。
现在我们就来仔细分析“同步锁加在代码段上”的线程同步模型。
首先,我们已经解决了同步锁加在哪里的问题。我们已经确定,同步锁不是加在共享资源上,而是加在访问共享资源的代码段上。
其次,我们要解决的问题是,我们应该在代码段上加什么样的锁。这个问题是重点中的重点。这是我们尤其要注意的问题:访问同一份共享资源的不同代码段,应该加上同一个同步锁;如果加的是不同的同步锁,那么根本就起不到同步的作用,没有任何意义。
这就是说,同步锁本身也一定是多个线程之间的共享对象。
Java语言的synchronized关键字
为了加深理解,举几个代码段同步的例子。
不同语言的同步锁模型都是一样的。只是表达方式有些不同。这里我们以当前最流行的Java语言为例。Java语言里面用synchronized关键字给代码段加锁。整个语法形式表现为
synchronized(同步锁) {
// 访问共享资源,需要同步的代码段
}
这里尤其要注意的就是,同步锁本身一定要是共享的对象。
… f1() {
Object lock1 = new Object(); // 产生一个同步锁
synchronized(lock1){
// 代码段 A
// 访问共享资源 resource1
// 需要同步
}
}
上面这段代码没有任何意义。因为那个同步锁是在函数体内部产生的。每个线程调用这段代码的时候,都会产生一个新的同步锁。那么多个线程之间,使用的是不同的同步锁。根本达不到同步的目的。
同步代码一定要写成如下的形式,才有意义。
public static final Object lock1 = new Object();
… f1() {
synchronized(lock1){ // lock1 是公用同步锁
// 代码段 A
// 访问共享资源 resource1
// 需要同步
}
你不一定要把同步锁声明为static或者public,但是你一定要保证相关的同步代码之间,一定要使用同一个同步锁。
讲到这里,你一定会好奇,这个同步锁到底是个什么东西。为什么随便声明一个Object对象,就可以作为同步锁?
在Java里面,同步锁的概念就是这样的。任何一个Object Reference都可以作为同步锁。我们可以把Object Reference理解为对象在内存分配系统中的内存地址。因此,要保证同步代码段之间使用的是同一个同步锁,我们就要保证这些同步代码段的synchronized关键字使用的是同一个Object Reference,同一个内存地址。这也是为什么我在前面的代码中声明lock1的时候,使用了final关键字,这就是为了保证lock1的Object Reference在整个系统运行过程中都保持不变。
一些求知欲强的读者可能想要继续深入了解synchronzied(同步锁)的实际运行机制。Java虚拟机规范中(你可以在google用“JVM Spec”等关键字进行搜索),有对synchronized关键字的详细解释。synchronized会编译成 monitor enter, … monitor exit之类的指令对。Monitor就是实际上的同步锁。每一个Object Reference在概念上都对应一个monitor。
这些实现细节问题,并不是理解同步锁模型的关键。我们继续看几个例子,加深对同步锁模型的理解。
public static final Object lock1 = new Object();
… f1() {
synchronized(lock1){ // lock1 是公用同步锁
// 代码段 A
// 访问共享资源 resource1
// 需要同步
}
}
… f2() {
synchronized(lock1){ // lock1 是公用同步锁
// 代码段 B
// 访问共享资源 resource1
// 需要同步
}
}
上述的代码中,代码段A和代码段B就是同步的。因为它们使用的是同一个同步锁lock1。
如果有10个线程同时执行代码段A,同时还有20个线程同时执行代码段B,那么这30个线程之间都是要进行同步的。
这30个线程都要竞争一个同步锁lock1。同一时刻,只有一个线程能够获得lock1的所有权,只有一个线程可以执行代码段A或者代码段B。其他竞争失败的线程只能暂停运行,进入到该同步锁的就绪(Ready)队列。
每一个同步锁下面都挂了几个线程队列,包括就绪(Ready)队列,待召(Waiting)队列等。比如,lock1对应的就绪队列就可以叫做lock1 - ready queue。每个队列里面都可能有多个暂停运行的线程。
注意,竞争同步锁失败的线程进入的是该同步锁的就绪(Ready)队列,而不是后面要讲述的待召队列(Waiting Queue,也可以翻译为等待队列)。就绪队列里面的线程总是时刻准备着竞争同步锁,时刻准备着运行。而待召队列里面的线程则只能一直等待,直到等到某个信号的通知之后,才能够转移到就绪队列中,准备运行。
成功获取同步锁的线程,执行完同步代码段之后,会释放同步锁。该同步锁的就绪队列中的其他线程就继续下一轮同步锁的竞争。成功者就可以继续运行,失败者还是要乖乖地待在就绪队列中。
因此,线程同步是非常耗费资源的一种操作。我们要尽量控制线程同步的代码段范围。同步的代码段范围越小越好。我们用一个名词“同步粒度”来表示同步代码段的范围。
同步粒度
在Java语言里面,我们可以直接把synchronized关键字直接加在函数的定义上。
比如。
… synchronized … f1() {
// f1 代码段
}
这段代码就等价于
… f1() {
synchronized(this){ // 同步锁就是对象本身
// f1 代码段
}
}
同样的原则适用于静态(static)函数
比如。
… static synchronized … f1() {
// f1 代码段
}
这段代码就等价于
…static … f1() {
synchronized(Class.forName(…)){ // 同步锁是类定义本身
// f1 代码段
}
}
但是,我们要尽量避免这种直接把synchronized加在函数定义上的偷懒做法。因为我们要控制同步粒度。同步的代码段越小越好。synchronized控制的范围越小越好。
我们不仅要在缩小同步代码段的长度上下功夫,我们同时还要注意细分同步锁。
比如,下面的代码
public static final Object lock1 = new Object();
… f1() {
synchronized(lock1){ // lock1 是公用同步锁
// 代码段 A
// 访问共享资源 resource1
// 需要同步
}
}
… f2() {
synchronized(lock1){ // lock1 是公用同步锁
// 代码段 B
// 访问共享资源 resource1
// 需要同步
}
}
… f3() {
synchronized(lock1){ // lock1 是公用同步锁
// 代码段 C
// 访问共享资源 resource2
// 需要同步
}
}
… f4() {
synchronized(lock1){ // lock1 是公用同步锁
// 代码段 D
// 访问共享资源 resource2
// 需要同步
}
}
上述的4段同步代码,使用同一个同步锁lock1。所有调用4段代码中任何一段代码的线程,都需要竞争同一个同步锁lock1。
我们仔细分析一下,发现这是没有必要的。
因为f1()的代码段A和f2()的代码段B访问的共享资源是resource1,f3()的代码段C和f4()的代码段D访问的共享资源是resource2,它们没有必要都竞争同一个同步锁lock1。我们可以增加一个同步锁lock2。f3()和f4()的代码可以修改为:
public static final Object lock2 = new Object();
… f3() {
synchronized(lock2){ // lock2 是公用同步锁
// 代码段 C
// 访问共享资源 resource2
// 需要同步
}
}
… f4() {
synchronized(lock2){ // lock2 是公用同步锁
// 代码段 D
// 访问共享资源 resource2
// 需要同步
}
}
这样,f1()和f2()就会竞争lock1,而f3()和f4()就会竞争lock2。这样,分开来分别竞争两个锁,就可以大大较少同步锁竞争的概率,从而减少系统的开销。
信号量
同步锁模型只是最简单的同步模型。同一时刻,只有一个线程能够运行同步代码。
有的时候,我们希望处理更加复杂的同步模型,比如生产者/消费者模型、读写同步模型等。这种情况下,同步锁模型就不够用了。我们需要一个新的模型。这就是我们要讲述的信号量模型。
信号量模型的工作方式如下:线程在运行的过程中,可以主动停下来,等待某个信号量的通知;这时候,该线程就进入到该信号量的待召(Waiting)队列当中;等到通知之后,再继续运行。
很多语言里面,同步锁都由专门的对象表示,对象名通常叫Monitor。
同样,在很多语言中,信号量通常也有专门的对象名来表示,比如,Mutex,Semphore。
信号量模型要比同步锁模型复杂许多。一些系统中,信号量甚至可以跨进程进行同步。另外一些信号量甚至还有计数功能,能够控制同时运行的线程数。
我们没有必要考虑那么复杂的模型。所有那些复杂的模型,都是最基本的模型衍生出来的。只要掌握了最基本的信号量模型——“等待/通知”模型,复杂模型也就迎刃而解了。
我们还是以Java语言为例。Java语言里面的同步锁和信号量概念都非常模糊,没有专门的对象名词来表示同步锁和信号量,只有两个同步锁相关的关键字——volatile和synchronized。
这种模糊虽然导致概念不清,但同时也避免了Monitor、Mutex、Semphore等名词带来的种种误解。我们不必执着于名词之争,可以专注于理解实际的运行原理。
在Java语言里面,任何一个Object Reference都可以作为同步锁。同样的道理,任何一个Object Reference也可以作为信号量。
Object对象的wait()方法就是等待通知,Object对象的notify()方法就是发出通知。
具体调用方法为
(1)等待某个信号量的通知
public static final Object signal = new Object();
… f1() {
synchronized(singal) { // 首先我们要获取这个信号量。这个信号量同时也是一个同步锁
// 只有成功获取了signal这个信号量兼同步锁之后,我们才可能进入这段代码
signal.wait(); // 这里要放弃信号量。本线程要进入signal信号量的待召(Waiting)队列
// 可怜。辛辛苦苦争取到手的信号量,就这么被放弃了
// 等到通知之后,从待召(Waiting)队列转到就绪(Ready)队列里面
// 转到了就绪队列中,离CPU核心近了一步,就有机会继续执行下面的代码了。
// 仍然需要把signal同步锁竞争到手,才能够真正继续执行下面的代码。命苦啊。
…
}
}
需要注意的是,上述代码中的signal.wait()的意思。signal.wait()很容易导致误解。signal.wait()的意思并不是说,signal开始wait,而是说,运行这段代码的当前线程开始wait这个signal对象,即进入signal对象的待召(Waiting)队列。
(2)发出某个信号量的通知
… f2() {
synchronized(singal) { // 首先,我们同样要获取这个信号量。同时也是一个同步锁。
// 只有成功获取了signal这个信号量兼同步锁之后,我们才可能进入这段代码
signal.notify(); // 这里,我们通知signal的待召队列中的某个线程。
// 如果某个线程等到了这个通知,那个线程就会转到就绪队列中
// 但是本线程仍然继续拥有signal这个同步锁,本线程仍然继续执行
// 嘿嘿,虽然本线程好心通知其他线程,
// 但是,本线程可没有那么高风亮节,放弃到手的同步锁
// 本线程继续执行下面的代码
…
}
}
需要注意的是,signal.notify()的意思。signal.notify()并不是通知signal这个对象本身。而是通知正在等待signal信号量的其他线程。
以上就是Object的wait()和notify()的基本用法。
实际上,wait()还可以定义等待时间,当线程在某信号量的待召队列中,等到足够长的时间,就会等无可等,无需再等,自己就从待召队列转移到就绪队列中了。
另外,还有一个notifyAll()方法,表示通知待召队列里面的所有线程。
这些细节问题,并不对大局产生影响。
绿色线程
绿色线程(Green Thread)是一个相对于操作系统线程(Native Thread)的概念。
操作系统线程(Native Thread)的意思就是,程序里面的线程会真正映射到操作系统的线程,线程的运行和调度都是由操作系统控制的
绿色线程(Green Thread)的意思是,程序里面的线程不会真正映射到操作系统的线程,而是由语言运行平台自身来调度。
当前版本的Python语言的线程就可以映射到操作系统线程。当前版本的Ruby语言的线程就属于绿色线程,无法映射到操作系统的线程,因此Ruby语言的线程的运行速度比较慢。
难道说,绿色线程要比操作系统线程要慢吗?当然不是这样。事实上,情况可能正好相反。Ruby是一个特殊的例子。线程调度器并不是很成熟。
目前,线程的流行实现模型就是绿色线程。比如,stackless Python,就引入了更加轻量的绿色线程概念。在线程并发编程方面,无论是运行速度还是并发负载上,都优于Python。
另一个更著名的例子就是ErLang(爱立信公司开发的一种开源语言)。
ErLang的绿色线程概念非常彻底。ErLang的线程不叫Thread,而是叫做Process。这很容易和进程混淆起来。这里要注意区分一下。
ErLang Process之间根本就不需要同步。因为ErLang语言的所有变量都是final的,不允许变量的值发生任何变化。因此根本就不需要同步。
final变量的另一个好处就是,对象之间不可能出现交叉引用,不可能构成一种环状的关联,对象之间的关联都是单向的,树状的。因此,内存垃圾回收的算法效率也非常高。这就让ErLang能够达到Soft Real Time(软实时)的效果。这对于一门支持内存垃圾回收的语言来说,可不是一件容易的事情。
java并发(1)线程模型
程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念
主要归咎于两点. 一个是由实现决定的,一个是由需求决定的.
线程由线程ID,程序计数器(PC)[用于指向内存中的程序指令],寄存器集合[由于存放本地变量和临时变量]和堆栈[用于存放方法指令和方法参数等]组成。
以 Unix/Linux 的体系架构为例。
因为操作系统的资源是有限的,如果访问资源的操作过多,必然会消耗过多的资源,而且如果不对这些操作加以区分,很可能造成资源访问的冲突。所以,为了减少有限资源的访问和使用冲突,对不同的操作赋予不同的执行等级(有多大能力做多大的事),用户态(User Mode)和内核态(Kernel Mode)。
运行于用户态的进程可以执行的操作和访问的资源都会受到极大的限制,而运行在内核态的进程则可以执行任何操作并且在资源的使用上没有限制。
并发 :一个时间段内有很多的线程或进程在执行,但何时间点上都只有一个在执行,多个线程或进程争抢时间片轮流执行。
并行 :一个时间段和时间点上都有多个线程或进程在执行。
线程有三种模型, 一对一,多对一,多对多.具体参考 一篇文章读懂Java多线程模型 , 这里只描述一对一的情况.
每个用户线程都映射到一个内核线程,每个线程都成为一个独立的调度单元,由内核调度器独立调度,一个线程的阻塞不会影响到其他线程,从而保障整个进程继续工作.
JVM 没有限定 Java 线程需要使用哪种线程模型来实现, JVM 只是封装了底层操作系统的差异,而不同的操作系统可能使用不同的线程模型,例如 Linux 和 windows 可能使用了一对一模型,solaris 和 unix 某些版本可能使用多对多模型。所以一谈到 Java 语言的多线程模型,需要针对具体 JVM 实现。
Sun JDK 1.2开始,线程模型都是基于操作系统原生线程模型来实现,它的 Window 版和 Linux 版都是使用系统的 1:1 的线程模型实现的。
关于java工作线程和java线程 操作系统线程的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。