0

关于投票
vmware虚拟机上的linux加载u盘的方法(用过没问题,很好)(转)
在使用中需要和宿主系统WindowsXP交换信息。除了配置共享文件夹外,还可以使用U盘。
Step 1. 需要让你的虚拟机Guest OS(Linux)能自动识别U盘。
方法如下:
保持焦点在Linux上,插入U盘,这时宿主操作系统Windows会弹出“找到新设备的提示”,然后一步一步
的点下一步,结束以后,就可以在linux使用fdisk -l /dev/sdb命令查看到/dev/sdb1。
Step 2. 在虚拟机识别出USB之后,用
fdisk -l /dev/sdb
fdisk -l /dev/hdb
fdisk -l
查看,会看到U盘被识别为sdb1
Step 3. 使用mount命令挂载即可,先在/mnt建一个新的文件夹,例如usb
则可通过:
mount -t vfat /dev/sdb1 /mnt/usb
挂载上U盘
 
注意:第一步很重要,否则fdisk -l /dev/sdb看不到/dev/sdb1这个新设备
卸载使用:
umount  /dev/sdb1
系统分类: 嵌入式
用户分类: Vmware
标签: 无标签
来源: 转贴
发表评论 阅读全文(167) | 回复(0)

0

关于投票
Linux 查看磁盘分区、文件系统、使用情况的命令和相关工具介绍(转)

Linux 查看磁盘分区、文件系统、使用情况的命令和相关工具介绍

作者:北南南北
来自:LinuxSir.Org
提要:Linux 磁盘分区表、文件系统的查看、统计的工具很多,有些工具是多功能的,不仅仅是查看磁盘的分区表,而且也能进行磁盘分区的操作;但在本文,我们只讲磁盘分区的查看,以及分区的使用情况的查看;本文只是给新手上路之用;关于分区工具的操作,我将在以后做专门介绍;此文目的:主要是向初学者介绍一下入门必备的一点基础知识,有了基础知识才能进阶;如果把所有的磁盘操作工具都放在一个文档里,大家看了也累;基于这种想法,所以我写文档的时候,就想让新手弟兄一目了解,能轻松点就轻松点;生活、工作和学习无不是如此;操作环境:Fedora core 4.0 i686 & Slackware 10.1
+++++++++++++++++++++++++++++++++++++++
正文
+++++++++++++++++++++++++++++++++++++++


一、df 命令;

df 是来自于coreutils 软件包,系统安装时,就自带的;我们通过这个命令可以查看磁盘的使用情况以及文件系统被挂载的位置;举例:
[root@localhost beinan]# df -lh
Filesystem            容量  已用 可用 已用% 挂载点
/dev/hda8              11G  6.0G  4.4G  58% /
/dev/shm              236M     0  236M   0% /dev/shm
/dev/sda1              56G   22G   35G  39% /mnt/sda1
我们从中可以看到,系统安装在/dev/hda8 ;还有一个56G的磁盘分区/dev/sda1挂载在 /mnt/sda1中;其它的参数请参考 man df
二、fdsik

fdisk 是一款强大的磁盘操作工具,来自util-linux软件包,我们在这里只说他如何查看磁盘分区表及分区结构;参数 -l ,通过-l 参数,能获得机器中所有的硬盘的分区情况;
[root@localhost beinan]# fdisk -l

Disk /dev/hda: 80.0 GB, 80026361856 bytes
255 heads, 63 sectors/track, 9729 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

   Device Boot      Start         End      Blocks   Id  System
/dev/hda1   *           1         765     6144831    7  HPFS/NTFS
/dev/hda2             766        2805    16386300    c  W95 FAT32 (LBA)
/dev/hda3            2806        7751    39728745    5  Extended
/dev/hda5            2806        3825     8193118+  83  Linux
/dev/hda6            3826        5100    10241406   83  Linux
/dev/hda7            5101        5198      787153+  82  Linux swap / Solaris
/dev/hda8            5199        6657    11719386   83  Linux
/dev/hda9            6658        7751     8787523+  83  Linux
在上面Blocks中,表示的是分区的大小,Blocks的单位是byte ,我们可以换算成M,比如第一个分区/dev/hda1的大小如果换算成M,应该是6144831/1024=6000M,也就是6G左右,其实没有这么麻烦,粗略的看一下把小数点向前移动三位,就知道大约的体积有多大了;
System 表示的文件系统,比如/dev/hda1 是NTFS格式的;/dev/hda2 表示是fat32格式的文件系统;. 在此例中,我们要特别注意的是/dev/hda3分区,这是扩展分区;他下面包含着逻辑分区,其实这个分区相当于一个容器;从属于她的有 hda5,hda6,hda7,hda8,hda9 ;我们还注意到一点,怎么没有hda4呢?为什么hda4没有包含在扩展分区?一个磁盘最多有四个主分区; hda1-4算都是主分区;hda4不可能包含在扩展分区里,另外扩展分区也算主分区;在本例中,没有hda4这个分区,当然我们可以把其中的一个分区设置为主分区,只是我当时分区的时候没有这么做而已;再仔细统计一下,我们看一看这个磁盘是不是还有空间?hda1+hda2+hda3=实际已经分区的体积,所以我们可以这样算 hda1+hda2+hda3=6144831+16386300+39728745 = 62259876 (b),换算成M单位,小数点向前移三位,所以目前已经划分好的分区大约占用体积是62259.876(M),其实最精确的计算62259876/1024=60800.67(M);而这个磁盘大小是80.0 GB (80026361856byte),其实实际大小也就是78150.744(M);通过我们一系列的计算,我们可以得出这个硬盘目前还有使用的空间;大约还有18G未分区的空间; fdisk -l 能列出机器中所有磁盘的个数,也能列出所有磁盘分区情况;比如:
[root@localhost beinan]# fdisk -l

Disk /dev/hda: 80.0 GB, 80026361856 bytes
255 heads, 63 sectors/track, 9729 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

   Device Boot      Start         End      Blocks   Id  System
/dev/hda1   *           1         765     6144831    7  HPFS/NTFS
/dev/hda2             766        2805    16386300    c  W95 FAT32 (LBA)
/dev/hda3            2806        7751    39728745    5  Extended
/dev/hda5            2806        3825     8193118+  83  Linux
/dev/hda6            3826        5100    10241406   83  Linux
/dev/hda7            5101        5198      787153+  82  Linux swap / Solaris
/dev/hda8            5199        6657    11719386   83  Linux
/dev/hda9            6658        7751     8787523+  83  Linux

Disk /dev/sda: 60.0 GB, 60011642880 bytes
64 heads, 32 sectors/track, 57231 cylinders
Units = cylinders of 2048 * 512 = 1048576 bytes

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1               1       57231    58604528   83  Linux
通过上面我们可以知道此机器有两块硬盘,我们也可以指定fdisk -l 来查看其中一个硬盘的分区情况;
[root@localhost beinan]# fdisk -l /dev/sda

Disk /dev/sda: 60.0 GB, 60011642880 bytes
64 heads, 32 sectors/track, 57231 cylinders
Units = cylinders of 2048 * 512 = 1048576 bytes

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1               1       57231    58604528   83  Linux

通过上面情况可以知道,在/dev/sda 这个磁盘中,只有一个分区;使用量差不多是百分百了;我们还可以来查看 /dev/hda的
[root@localhost beinan]# fdisk -l /dev/hda
自己试试看?

三、cfdisk 来自于util-linux的软件包;

cfdisk 也是一款不错的分区工具;在有些发行版中,此工具已经从util-linux包中剔除;cfdisk 简单易用是他的特点;和DOS中的fdisk 相似;在本标题中,我们只来解说如何查看机器的中的磁盘分区状况及所用的文件系统等;查看磁盘分区的用法 cfdisk -Ps 磁盘设备名;
比如
[root@localhost beinan]cfdisk -Ps 
[root@localhost beinan]cfdisk -Ps   /dev/hda

[root@localhost beinan]cfdisk -Ps
Partition Table for /dev/hda

               First       Last
# Type       Sector      Sector   Offset    Length   Filesystem Type (ID) Flag
-- ------- ----------- ----------- ------ ----------- -------------------- ----
1 Primary           0    23438834     63    23438835 Linux (83)           Boot
2 Primary    23438835   156296384      0   132857550 Extended (05)        None
5 Logical    23438835   155268224     63   131829390 Linux (83)           None
6 Logical   155268225   156296384     63     1028160 Linux swap (82)      None
我们只用的参数 -Ps,就列出了磁盘的分区情况;目前在Slackware Debian Mandrake 等主流发行版中存在cfdisk ,而fedora 4.0把这个小工具剔除了;有点可惜;这是我在Slackware中操作的;如果您这样操作,就更直观了;
[root@localhost beinan]cfdisk  磁盘设备名  

举例:
[root@localhost beinan]cfdisk  /dev/hda 
您所看到的是如下的模式:
                                        cfdisk 2.12a

                                    Disk Drive: /dev/hda
                              Size: 80026361856 bytes, 80.0 GB
                    Heads: 255   Sectors per Track: 63   Cylinders: 9729

    Name          Flags        Part Type    FS Type            [Label]           Size (MB)
-------------------------------------------------------------------------------------------
    hda1          Boot          Primary     Linux ReiserFS                        12000.69
    hda5                        Logical     Linux ReiserFS                        67496.65
    hda6                        Logical     Linux swap                              526.42



     [Bootable]  [ Delete ]  [  Help  ]  [Maximize]  [ Print  ]  [  Quit  ]
     [  Type  ]  [ Units  ]  [ Write  ]

                        Toggle bootable flag of the current partition
您进入了cfdisk 的操作界面;用键盘移动指针到[Quit]就可以退出;
四、parted 功能不错的分区工具;在Fedora 4.0中有带,可以自己安装上;在此主题中,我们只说如何查看磁盘的分区情况;

调用方法简单,parted 默认是打开的设备是/dev/hda ,也可以自己指定;比如 parted /dev/hda 或/dev/sda 等;退出的方法是 quit
[root@localhost beinan]# parted

使用 /dev/hda
(parted) p
/dev/hda 的磁盘几何结构:0.000-76319.085 兆字节
磁盘标签类型:msdos
Minor    起始点        终止点  类型      文件系统    标志
1          0.031   6000.842  主分区 ntfs        启动
2       6000.842  22003.088  主分区 fat32       lba
3      22003.088  60800.690  扩展分区
5      22003.119  30004.211  逻辑分区 reiserfs
6      30004.242  40005.615  逻辑分区 reiserfs
7      40005.646  40774.350  逻辑分区 linux-swap
8      40774.381  52219.094  逻辑分区 ext3
9      52219.125  60800.690  逻辑分区 reiserfs
我们在partd 的操作面上,用p就可以列出当前磁盘的分区情况,如果想要查看其它磁盘,可以用 select 功能,比如 select /dev/sda ;

五、qtparted ,与parted 相关的软件还有qtparted ,也能查看到磁盘的结构和所用的文件系统,是图形化的;

[beinan@localhost ~]# qtparted 图形化的查看,一目了然;

六、sfdisk 也是一个分区工具,功能也多多;我们在这里只说他的列磁盘分区情况的功能;

[root@localhost beinan]# sfdisk -l 

自己看看吧; sfdisk 有几个很有用的功能;有兴趣的弟兄不妨看看;
七、partx 也简要的说一说,有些系统自带了这个工具,功能也简单,和fdisk 、parted、cfdisk 来说不值一提;不用也罢;

用法: partx 设备名
[root@localhost beinan]# partx /dev/hda
# 1:        63- 12289724 ( 12289662 sectors,   6292 MB)
# 2:  12289725- 45062324 ( 32772600 sectors,  16779 MB)
# 3:  45062325-124519814 ( 79457490 sectors,  40682 MB)
# 4:         0-       -1 (        0 sectors,      0 MB)
# 5:  45062388- 61448624 ( 16386237 sectors,   8389 MB)
# 6:  61448688- 81931499 ( 20482812 sectors,  10487 MB)
# 7:  81931563- 83505869 (  1574307 sectors,    806 MB)
# 8:  83505933-106944704 ( 23438772 sectors,  12000 MB)
# 9: 106944768-124519814 ( 17575047 sectors,   8998 MB)
八、查看目前机器中的所有磁盘及分区情况:
[beinan@localhost ~]$ cat /proc/partitions
major minor  #blocks  name

   3     0   78150744 hda
   3     1    6144831 hda1
   3     2   16386300 hda2
   3     5    8193118 hda5
   3     6   10241406 hda6
   3     7     787153 hda7
   3     8   11719386 hda8
   3     9    8787523 hda9
   8     0   58605120 sda
   8     1   58604528 sda1


九、mac-fdisk 这个工具主要是用在Powerpc版本的Linux中,咱们常用X86版本中没有这个工具;这个只是介绍一下;
相关文档 《在Linux系统中存储设备的两种表示方法》
《合理规划您的硬盘分区》
《系统引导过程及硬盘分区结构论述》
《Linux 查看磁盘分区、文件系统、使用情况的命令和相关工具介绍》
《实例解说 fdisk 使用方法》
《在Fedora core 4.0 加载NTFS和FAT32分区详述》
《Fedora Core 4.0 HAL配置即插即用移动存储(USB及1394)的实践》
系统分类: 嵌入式
用户分类: 无分类
标签: 无标签
来源: 转贴
发表评论 阅读全文(125) | 回复(0)

0

关于投票
linux下C语言多线程编程实例 (转)
学东西,往往实例才是最让人感兴趣的,老是学基础理论,不动手,感觉没有成就感,呵呵。

下面先来一个实例。我们通过创建两个线程来实现对一个数的递加。
或许这个实例没有实际运用的价值,但是稍微改动一下,我们就可以用到其他地方去拉。

下面是我们的代码:
/*thread_example.c :  c multiple thread programming in linux
  *author : falcon
  *E-mail : tunzhj03@st.lzu.edu.cn
  */
#include <pthread.h>
#include <stdio.h>
#include <sys/time.h>
#include <string.h>
#define MAX 10

pthread_t thread[2];
pthread_mutex_t mut;
int number="0", i;

void *thread1()
{
        printf ("thread1 : I'm thread 1\n");

        for (i = 0; i < MAX; i++)
        {
                printf("thread1 : number = %d\n",number);
                pthread_mutex_lock(&mut);
                        number++;
                pthread_mutex_unlock(&mut);
                sleep(2);
        }


        printf("thread1 :主函数在等我完成任务吗?\n");
        pthread_exit(NULL);
}

void *thread2()
{
        printf("thread2 : I'm thread 2\n");

        for (i = 0; i < MAX; i++)
        {
                printf("thread2 : number = %d\n",number);
                pthread_mutex_lock(&mut);
                        number++;
                pthread_mutex_unlock(&mut);
                sleep(3);
        }


        printf("thread2 :主函数在等我完成任务吗?\n");
        pthread_exit(NULL);
}

void thread_create(void)
{
        int temp;
        memset(&thread, 0, sizeof(thread));          //comment1
        /*创建线程*/
        if((temp = pthread_create(&thread[0], NULL, thread1, NULL)) != 0)       //comment2
                printf("线程1创建失败!\n");
        else
                printf("线程1被创建\n");

        if((temp = pthread_create(&thread[1], NULL, thread2, NULL)) != 0)  //comment3
                printf("线程2创建失败");
        else
                printf("线程2被创建\n");
}

void thread_wait(void)
{
        /*等待线程结束*/
        if(thread[0] !=0) {                   //comment4
                pthread_join(thread[0],NULL);
                printf("线程1已经结束\n");
        }
        if(thread[1] !=0) {                //comment5
                pthread_join(thread[1],NULL);
                printf("线程2已经结束\n");
        }
}

int main()
{
        /*用默认属性初始化互斥锁*/
        pthread_mutex_init(&mut,NULL);

        printf("我是主函数哦,我正在创建线程,呵呵\n");
        thread_create();
        printf("我是主函数哦,我正在等待线程完成任务阿,呵呵\n");
        thread_wait();

        return 0;
}



下面我们先来编译、执行一下

引文:

falcon@falcon:~/program/c/code/ftp$ gcc -lpthread -o thread_example thread_example.c
falcon@falcon:~/program/c/code/ftp$ ./thread_example
我是主函数哦,我正在创建线程,呵呵
线程1被创建
线程2被创建
我是主函数哦,我正在等待线程完成任务阿,呵呵
thread1 : I'm thread 1
thread1 : number = 0
thread2 : I'm thread 2
thread2 : number = 1
thread1 : number = 2
thread2 : number = 3
thread1 : number = 4
thread2 : number = 5
thread1 : number = 6
thread1 : number = 7
thread2 : number = 8
thread1 : number = 9
thread2 : number = 10
thread1 :主函数在等我完成任务吗?
线程1已经结束
thread2 :主函数在等我完成任务吗?
线程2已经结束



实例代码里头的注释应该比较清楚了吧,下面我把网路上介绍上面涉及到的几个函数和变量给引用过来。

引文:

线程相关操作

一 pthread_t

pthread_t在头文件/usr/include/bits/pthreadtypes.h中定义:
  typedef unsigned long int pthread_t;
  它是一个线程的标识符。

二 pthread_create

函数pthread_create用来创建一个线程,它的原型为:
  extern int pthread_create __P ((pthread_t *__thread, __const pthread_attr_t *__attr,
  void *(*__start_routine) (void *), void *__arg));
  第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我们的函数thread不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。对线程属性的设定和修改我们将在下一节阐述。当创建线程成功时,函数返回0,若不为0则说明创建线程失败,常见的错误返回代码为EAGAIN和EINVAL。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。

三 pthread_join pthread_exit
  
函数pthread_join用来等待一个线程的结束。函数原型为:
  extern int pthread_join __P ((pthread_t __th, void **__thread_return));
  第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。一个线程的结束有两种途径,一种是象我们上面的例子一样,函数结束了,调用它的线程也就结束了;另一种方式是通过函数pthread_exit来实现。它的函数原型为:
  extern void pthread_exit __P ((void *__retval)) __attribute__ ((__noreturn__));
  唯一的参数是函数的返回代码,只要pthread_join中的第二个参数thread_return不是NULL,这个值将被传递给 thread_return。最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join的线程则返回错误代码ESRCH。
  在这一节里,我们编写了一个最简单的线程,并掌握了最常用的三个函数pthread_create,pthread_join和pthread_exit。下面,我们来了解线程的一些常用属性以及如何设置这些属性。


互斥锁相关

互斥锁用来保证一段时间内只有一个线程在执行一段代码。

一 pthread_mutex_init

函数pthread_mutex_init用来生成一个互斥锁。NULL参数表明使用默认属性。如果需要声明特定属性的互斥锁,须调用函数 pthread_mutexattr_init。函数pthread_mutexattr_setpshared和函数 pthread_mutexattr_settype用来设置互斥锁属性。前一个函数设置属性pshared,它有两个取值, PTHREAD_PROCESS_PRIVATE和PTHREAD_PROCESS_SHARED。前者用来不同进程中的线程同步,后者用于同步本进程的不同线程。在上面的例子中,我们使用的是默认属性PTHREAD_PROCESS_ PRIVATE。后者用来设置互斥锁类型,可选的类型有PTHREAD_MUTEX_NORMAL、PTHREAD_MUTEX_ERRORCHECK、 PTHREAD_MUTEX_RECURSIVE和PTHREAD _MUTEX_DEFAULT。它们分别定义了不同的上所、解锁机制,一般情况下,选用最后一个默认属性。

二 pthread_mutex_lock pthread_mutex_unlock pthread_delay_np

   pthread_mutex_lock声明开始用互斥锁上锁,此后的代码直至调用pthread_mutex_unlock为止,均被上锁,即同一时间只能被一个线程调用执行。当一个线程执行到pthread_mutex_lock处时,如果该锁此时被另一个线程使用,那此线程被阻塞,即程序将等待到另一个线程释放此互斥锁。



注意:

1 需要说明的是,上面的两处sleep不光是为了演示的需要,也是为了让线程睡眠一段时间,让线程释放互斥锁,等待另一个线程使用此锁。下面的参考资料1里头说明了该问题。但是在linux下好像没有pthread_delay_np那个函数(我试了一下,提示没有定义该函数的引用),所以我用了sleep来代替,不过参考资料2中给出另一种方法,好像是通过pthread_cond_timedwait来代替,里头给出了一种实现的办法。

2 请千万要注意里头的注释comment1-5,那是我花了几个小时才找出的问题所在。
如果没有comment1和comment4,comment5,将导致在pthread_join的时候出现段错误,另外,上面的comment2和comment3是根源所在,所以千万要记得写全代码。因为上面的线程可能没有创建成功,导致下面不可能等到那个线程结束,而在用pthread_join的时候出现段错误(访问了未知的内存区)。另外,在使用memset的时候,需要包含string.h头文件哦


参考资料:

1。Linux下的多线程编程
http://linux.chinaunix.net/doc/program/2001-08-11/642.shtml
2。pthread_delay_np(这里头有个关于posix条件变量的例子)
http://bbs.chinaunix.net/archiver/?tid-584593.html
3。pthread_join和段错误(非常感谢这里头的哥们,千万要看哦)
http://www.bczs.net/xml/2005/11/5/4374188.xml
4。posix线程编程指南[学习linux下多线程,不看这个你会后悔的]
http://www.linuxforum.net/forum/showflat.php?Cat=&Board=program&Number=294073&page=0&view=collapsed&sb=5&o=7&fpart=

另一篇

 在进入实战篇以前,我们简单说一下多线程编程的一般原则。

  [安全性]是多线程编程的首要原则,如果两个以上的线程访问同一对象时,一个线程会损坏另一个线程的数据,这就是违反了安全性原则,这样的程序是不能进入实际应用的。

  安全性的保证可以通过设计安全的类和程序员的手工控制。如果多个线程对同一对象访问不会危及安全性,这样的类就是线程安全的类,在JAVA中比如String类就被设计为线程安全的类。而如果不是线程安全的类,那么就需要程序员在访问这些类的实例时手工控制它的安全性。

  [可行性]是多线程编程的另一个重要原则,如果仅仅实现了安全性,程序却在某一点后不能继续执行或者多个线程发生死锁,那么这样的程序也不能作为真正的多线程程序来应用。

  相对而言安全性和可行性是相互抵触的,安全性越高的程序,可性行会越低。要综合平衡。

  [高性能] 多线程的目的本来就是为了增加程序运行的性能,如果一个多线程完成的工作还不如单线程完成得快。那就不要应用多线程了。

  高性能程序主要有以下几个方面的因素:

  数据吞吐率,在一定的时间内所能完成的处理能力。

  响应速度,从发出请求到收到响应的时间。

  容量,指同时处理雅致同任务的数量。

  安全性和可行性是必要条件,如果达到不这两个原则那就不能称为真正的多线程程序。而高性是多线程编程的目的,也可以说是充要条件。否则,为什么采用多线程编程呢?

  

  [生产者与消费者模式]

  首先以一个生产者和消费者模式来进入实战篇的第一节。

  生产者和消费者模式中保护的是谁?

  多线程编程都在保护着某些对象,这些个对象是 "紧俏资源 ",要被最大限度地利用,这也是采用多线程方式的理由。在生产者消费者模式中,我们要保护的是 "仓库 ",在我下面的这个例子中,

  就是桌子(table)。

  我这个例子的模式完全是生产者-消费者模式,但我换了个名字。厨师-食客模式,这个食堂中只有1张桌子,同时最多放10个盘子,现在有4个厨师做菜,每做好一盘就往桌子上放(生产者将产品往仓库中放),而有6个食客不停地吃(消费者消费产品,为了说明问题,他们的食量是无限的)。

  一般而言,厨师200-400ms做出一盘菜,而食客要400-600ms吃完一盘。当桌子上放满了10个盘子后,所有厨师都不能再往桌子上放,而当桌子是没有盘子时,所有的食客都只好等待。

  下面我们来设计这个程序:

  因为我们不知道具体是什么菜,所以叫它food:

  class Food{}

  然后是桌子,因为它要有序地放而且要有序地取(不能两个食客同时争取第三盘菜),所以我们扩展LinkedList,或者你用聚合把一个LinkedList作为属性也能达到同样的目的,例子中我是用

  继承,从构造方法中传入一个可以放置的最大值。

  class Table extends java.util.LinkedList{ int maxSize; public Table(int maxSize){ this.maxSize = maxSize; }}

  现在我们要为它加两个方法,一是厨师往上面放菜的方法,一是食客从桌子上拿菜的方法。

  放菜:因为一张桌子由多个厨师放菜,所以厨师放菜的要被同步,如果桌子上已经有十盘菜了。所有厨师就要等待:

  public synchronized void putFood(Food f){ while(this.size() >= this.maxSize){ try{ this.wait(); }catch(Exception e){} } this.add(f); notifyAll(); }

  拿菜:同上面,如果桌子上一盘菜也没有,所有食客都要等待:

  public synchronized Food getFood(){ while(this.size() <= 0){ try{ this.wait(); }catch(Exception e){} } Food f = (Food)this.removeFirst(); notifyAll(); return f; }

  厨师类:

  由于多个厨师要往一张桌子上放菜,所以他们要操作的桌子应该是同一个对象,我们从构造方法中将桌子对象传进去以便控制在主线程中只产生一张桌子。

  厨师做菜要用一定的时候,我用在make方法中用sleep表示他要消耗和时候,用200加上200的随机数保证时间有200-400ms中。做好后就要往桌子上放。

  这里有一个非常重要的问题一定要注意,就是对什么范围同步的问题,因为产生竞争的是桌子,所以所有putFood是同步的,而我们不能把厨师自己做菜的时间也放在同步中,因为做菜是各自做的。同样食客吃菜的时候也不应该同步,只有从桌子中取菜的时候是竞争的,而具体吃的时候是各自在吃。所以厨师类的代码如下:

  class Chef extends Thread{ Table t; Random r = new Random(12345); public Chef(Table t){ this.t = t; } public void run(){ while(true){ Food f = make(); t.putFood(f); } } private Food make(){ try{ Thread.sleep(200+r.nextInt(200)); }catch(Exception e){} return new Food(); }}

  同理我们产生食客类的代码如下:

  class Eater extends Thread{ Table t; Random r = new Random(54321); public Eater(Table t){ this.t = t; } public void run(){ while(true){ Food f = t.getFood(); eat(f); } } private void eat(Food f){ try{ Thread.sleep(400+r.nextInt(200)); }catch(Exception e){} }}

  完整的程序在这儿:

  package debug;import java.util.regex.*;import java.util.*;class Food{}class Table extends LinkedList{ int maxSize; public Table(int maxSize){ this.maxSize = maxSize; } public synchronized void putFood(Food f){ while(this.size() >= this.maxSize){ try{ this.wait(); }catch(Exception e){} } this.add(f); notifyAll(); } public synchronized Food getFood(){ while(this.size() <= 0){ try{ this.wait(); }catch(Exception e){} } Food f = (Food)this.removeFirst(); notifyAll(); return f; }}class Chef extends Thread{ Table t; String name; Random r = new Random(12345); public Chef(String name,Table t){ this.t = t; this.name = name; } public void run(){ while(true){ Food f = make(); System.out.println(name+ " put a Food: "+f); t.putFood(f); } } private Food make(){ try{ Thread.sleep(200+r.nextInt(200)); }catch(Exception e){} return new Food(); }}class Eater extends Thread{ Table t; String name; Random r = new Random(54321); public Eater(String name,Table t){ this.t = t; this.name = name; } public void run(){ while(true){ Food f = t.getFood(); System.out.println(name+ " get a Food: "+f); eat(f); } } private void eat(Food f){ try{ Thread.sleep(400+r.nextInt(200)); }catch(Exception e){} }}public class Test { public static void main(String[] args) throws Exception{ Table t = new Table(10); new Chef( "Chef1 ",t).start(); new Chef( "Chef2 ",t).start(); new Chef( "Chef3 ",t).start(); new Chef( "Chef4 ",t).start(); new Eater( "Eater1 ",t).start(); new Eater( "Eater2 ",t).start(); new Eater( "Eater3 ",t).start(); new Eater( "Eater4 ",t).start(); new Eater( "Eater5 ",t).start(); new Eater( "Eater6 ",t).start(); }}

  

  这一个例子中,我们主要关注以下几个方面:

  1.同步方法要保护的对象,本例中是保护桌子,不能同时往上放菜或同时取菜。

  假如我们把putFood方法和getFood方法在厨师类和食客类中实现,那么我们应该如此:

  (以putFood为例)

  class Chef extends Thread{ Table t; String name; public Chef(String name,Table t){ this.t = t; this.name = name; } public void run(){ while(true){ Food f = make(); System.out.println(name+ " put a Food: "+f); putFood(f); } } private Food make(){ Random r = new Random(200); try{ Thread.sleep(200+r.nextInt()); }catch(Exception e){} return new Food(); } public void putFood(Food f){//方法本身不能同步,因为它同步的是this.即Chef的实例 synchronized (t) {//要保护的是t while (t.size() >= t.maxSize) { try { t.wait(); } catch (Exception e) {} } t.add(f); t.notifyAll(); } }}

  2.同步的范围,在本例中是放和取两个方法,不能把做菜和吃菜这种各自不相干的工作放在受保护的范围中。

  3.参与者与容积比

  对于生产者和消费者的比例,以及桌子所能放置最多菜的数量三者之间的关系是影响性能的重要因素,如果是过多的生产者在等待,则要增加消费者或减少生产者的数据,反之则增加生产者或减少消费者的数量。

  另外如果桌子有足够的容量可以很大程序提升性能,这种情况下可以同时提高生产者和消费者的数量,但足够大的容时往往你要有足够大的物理内存。

[写在前面]

  随着计算机技术的发展,编程模型也越来越复杂多样化。但多线程编程模型是目前计算机系统架构的最终模型。随着CPU主频的不断攀升,X86架构的硬件已经成为瓶,在这种架构的CPU主频最高为4G。事实上目前3.6G主频的CPU已经接近了顶峰。

  如果不能从根本上更新当前CPU的架构(在很长一段时间内还不太可能),那么继续提高CPU性能的方法就是超线程CPU模式。那么,作业系统、应用程序要发挥CPU的最大性能,就是要改变到以多线程编程模型为主的并行处理系统和并发式应用程序。

  所以,掌握多线程编程模型,不仅是目前提高应用性能的手段,更是下一代编程模型的核心思想。多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU而只和I/O,OEMBIOS等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。从根本上说,这就是多线程编程的最终目的。

  [第一需要弄清的问题]

  如同程序和进程的区别,要掌握多线程编程,第一要弄清的问题是:线程对象和线程的区别

  线程对象是可以产生线程的对象。比如在java平台中Thread对象,Runnable对象。线程,是指正在执行的一个指点令序列。在java平台上是指从一个线程对象的start()开始,运行run方法体中的那一段相对独立的过程。

  鉴于作者的水平,无法用更确切的词汇来描述它们的定义。但这两个有本质区别的概念请初学者细细体会,随着介绍的深入和例程分析的增加,就会慢慢明白它们所代表的真实含义。

  天下难事必始于易,天下大事必始于细。

  让我们先从最简单的"单线程"来入手:(1)带引号说明只是相对而言的单线程,(2)基于java。

    class BeginClass{
        public static void main(String[] args){
            for(int i="0";i<100;i++)
                System.out.println("Hello,World!");
        }
    }

  如果我们成功编译了该java文件,然后在命令行上敲入:

  java BeginClass

  现在发生了什么呢?每一个java程序员,从他开始学习java的第一分钟里都会接触到这个问

  题,但是,你知道它到底发生发什么?

  JVM进程被启动,在同一个JVM进程中,有且只有一个进程,就是它自己。然后在这个JVM环境中,所有程序的运行都是以线程来运行。JVM最先会产生一个主线程,由它来运行指定程序的入口点。在这个程序中,就是主线程从main方法开始运行。当main方法结束后,主线程运行完成。JVM进程也随之退出。

  我们看到的是一个主线程在运行main方法,这样的只有一个线程执行程序逻辑的流程我们称

  之为单线程。这是JVM提供给我们的单线程环境,事实上,JVM底层还至少有垃圾回收这样的后台线程以及其它非java线程,但这些线程对我们而言不可访问,我们只认为它是单线程的。

  主线程是JVM自己启动的,在这里它不是从线程对象产生的。在这个线程中,它运行了main方法这个指令序列。理解它,但它没有更多可以研究的内容。

  [接触多线程]

    class MyThread extends Thread{
        public void run(){
            System.out.println("Thread say:Hello,World!");
        }
    }

    public class MoreThreads{
        public static void main(String[] args){
            new MyThread();
            new MyThread().start();
            System.out.println("Main say:Hello,World");
        }
    }

  执行这个程序,main方法第一行产生了一个线程对象,但并没有线程启动。

  main方法第二行产生了一个线程对象,并启动了一个线程。

  main方法第三行,产生并启动一个线程后,主线程自己也继续执行其它语句。

  我们先不研究Thread对象的具体内容,稍微来回想一下上面的两个概念,线程对象线程。在JAVA中,线程对象是JVM产生的一个普通的Object子类。而线程是CPU分配给这个对象的一个运行过程。我们说的这个线程在干什么,不是说一个线程对象在干什么,而是这个运行过程在干什么。如果一时想不明白,不要急,但你要记得它们不是一回事就行了。

  累了吧?为不么不继续了?

  基于这种风格来介绍多线程,并不是每个人都喜欢和接受的,如果你不喜欢,正好不浪费你的时间了,而如果你接受的话,那就看下一节吧。

 在进入实战篇以前,我们简单说一下多线程编程的一般原则。

  [安全性]是多线程编程的首要原则,如果两个以上的线程访问同一对象时,一个线程会损坏另一个线程的数据,这就是违反了安全性原则,这样的程序是不能进入实际应用的。

  安全性的保证可以通过设计安全的类和程序员的手工控制。如果多个线程对同一对象访问不会危及安全性,这样的类就是线程安全的类,在JAVA中比如String类就被设计为线程安全的类。而如果不是线程安全的类,那么就需要程序员在访问这些类的实例时手工控制它的安全性。

  [可行性]是多线程编程的另一个重要原则,如果仅仅实现了安全性,程序却在某一点后不能继续执行或者多个线程发生死锁,那么这样的程序也不能作为真正的多线程程序来应用。

  相对而言安全性和可行性是相互抵触的,安全性越高的程序,可性行会越低。要综合平衡。

  [高性能] 多线程的目的本来就是为了增加程序运行的性能,如果一个多线程完成的工作还不如单线程完成得快。那就不要应用多线程了。

  高性能程序主要有以下几个方面的因素:

  数据吞吐率,在一定的时间内所能完成的处理能力。

  响应速度,从发出请求到收到响应的时间。

  容量,指同时处理雅致同任务的数量。

  安全性和可行性是必要条件,如果达到不这两个原则那就不能称为真正的多线程程序。而高性是多线程编程的目的,也可以说是充要条件。否则,为什么采用多线程编程呢?

 

[生产者与消费者模式]

  首先以一个生产者和消费者模式来进入实战篇的第一节。

  生产者和消费者模式中保护的是谁?

  多线程编程都在保护着某些对象,这些个对象是"紧俏资源",要被最大限度地利用,这也是采用多线程方式的理由。在生产者消费者模式中,我们要保护的是"仓库",在我下面的这个例子中,

就是桌子(table)。

  我这个例子的模式完全是生产者-消费者模式,但我换了个名字。厨师-食客模式,这个食堂中只有1张桌子,同时最多放10个盘子,现在有4个厨师做菜,每做好一盘就往桌子上放(生产者将产品往仓库中放),而有6个食客不停地吃(消费者消费产品,为了说明问题,他们的食量是无限的)。

  一般而言,厨师200-400ms做出一盘菜,而食客要400-600ms吃完一盘。当桌子上放满了10个盘子后,所有厨师都不能再往桌子上放,而当桌子是没有盘子时,所有的食客都只好等待。

  下面我们来设计这个程序:

  因为我们不知道具体是什么菜,所以叫它food:

 class Food{}

  然后是桌子,因为它要有序地放而且要有序地取(不能两个食客同时争取第三盘菜),所以我们扩展LinkedList,或者你用聚合把一个LinkedList作为属性也能达到同样的目的,例子中我是用

继承,从构造方法中传入一个可以放置的最大值。

class Table extends java.util.LinkedList{
  int maxSize;
  public Table(int maxSize){
    this.maxSize = maxSize;
  }
}

现在我们要为它加两个方法,一是厨师往上面放菜的方法,一是食客从桌子上拿菜的方法。

放菜:因为一张桌子由多个厨师放菜,所以厨师放菜的要被同步,如果桌子上已经有十盘菜了。所有厨师就要等待:

 public synchronized void putFood(Food f){
    while(this.size() >= this.maxSize){
      try{
        this.wait();
      }catch(Exception e){}
    }
    this.add(f);
    notifyAll();
  }

拿菜:同上面,如果桌子上一盘菜也没有,所有食客都要等待:

 public synchronized Food getFood(){
    while(this.size() <= 0){
      try{
        this.wait();
      }catch(Exception e){}
    }
    Food f = (Food)this.removeFirst();
    notifyAll();
    return f;
  }

厨师类:

  由于多个厨师要往一张桌子上放菜,所以他们要操作的桌子应该是同一个对象,我们从构造方法中将桌子对象传进去以便控制在主线程中只产生一张桌子。

厨师做菜要用一定的时候,我用在make方法中用sleep表示他要消耗和时候,用200加上200的随机数保证时间有200-400ms中。做好后就要往桌子上放。

这里有一个非常重要的问题一定要注意,就是对什么范围同步的问题,因为产生竞争的是桌子,所以所有putFood是同步的,而我们不能把厨师自己做菜的时间也放在同步中,因为做菜是各自做的。同样食客吃菜的时候也不应该同步,只有从桌子中取菜的时候是竞争的,而具体吃的时候是各自在吃。所以厨师类的代码如下:

 class Chef extends Thread{
  Table t;
  Random r = new Random(12345);
  public Chef(Table t){
    this.t = t;
  }
  public void run(){
    while(true){
      Food f = make();
      t.putFood(f);
    }
  }
  private Food make(){

    try{
      Thread.sleep(200+r.nextInt(200));
    }catch(Exception e){}
    return new Food();
  }
}

同理我们产生食客类的代码如下:

class Eater extends Thread{
  Table t;
  Random r = new Random(54321);
  public Eater(Table t){
    this.t = t;
  }
  public void run(){
    while(true){
      Food f = t.getFood();
      eat(f);
    }
  }
  private void eat(Food f){
    
    try{
      Thread.sleep(400+r.nextInt(200));
    }catch(Exception e){}
  }
}

完整的程序在这儿:

package debug;
import java.util.regex.*;
import java.util.*;


class Food{}

class Table extends LinkedList{
  int maxSize;
  public Table(int maxSize){
    this.maxSize = maxSize;
  }
  public synchronized void putFood(Food f){
    while(this.size() >= this.maxSize){
      try{
        this.wait();
      }catch(Exception e){}
    }
    this.add(f);
    notifyAll();
  }
  
  public synchronized Food getFood(){
    while(this.size() <= 0){
      try{
        this.wait();
      }catch(Exception e){}
    }
    Food f = (Food)this.removeFirst();
    notifyAll();
    return f;
  }
}


class Chef extends Thread{
  Table t;
  String name;
  Random r = new Random(12345);
  public Chef(String name,Table t){
    this.t = t;
    this.name = name;
  }
  public void run(){
    while(true){
      Food f = make();
      System.out.println(name+" put a Food:"+f);
      t.putFood(f);
    }
  }
  private Food make(){
    try{
      Thread.sleep(200+r.nextInt(200));
    }catch(Exception e){}
    return new Food();
  }
}

class Eater extends Thread{
  Table t;
  String name;
  Random r = new Random(54321);
  public Eater(String name,Table t){
    this.t = t;
    this.name = name;
  }
  public void run(){
    while(true){
      Food f = t.getFood();
      System.out.println(name+" get a Food:"+f);
      eat(f);
      
    }
  }
  private void eat(Food f){
    
    try{
      Thread.sleep(400+r.nextInt(200));
    }catch(Exception e){}
  }
}

public class Test {
    public static void main(String[] args) throws Exception{
      Table t = new Table(10);
      new Chef("Chef1",t).start();
      new Chef("Chef2",t).start();
      new Chef("Chef3",t).start();
      new Chef("Chef4",t).start();
      new Eater("Eater1",t).start();
      new Eater("Eater2",t).start();
      new Eater("Eater3",t).start();
      new Eater("Eater4",t).start();
      new Eater("Eater5",t).start();
      new Eater("Eater6",t).start();

    }
}

 

这一个例子中,我们主要关注以下几个方面:

  1.同步方法要保护的对象,本例中是保护桌子,不能同时往上放菜或同时取菜。

  假如我们把putFood方法和getFood方法在厨师类和食客类中实现,那么我们应该如此:

(以putFood为例)

class Chef extends Thread{
  Table t;
  String name;
  public Chef(String name,Table t){
    this.t = t;
    this.name = name;
  }
  public void run(){
    while(true){
      Food f = make();
      System.out.println(name+" put a Food:"+f);
      putFood(f);
    }
  }
  private Food make(){
    Random r = new Random(200);
    try{
      Thread.sleep(200+r.nextInt());
    }catch(Exception e){}
    return new Food();
  }
  public void putFood(Food f){//方法本身不能同步,因为它同步的是this.即Chef的实例

    synchronized (t) {//要保护的是t
      while (t.size() >= t.maxSize) {
        try {
          t.wait();
        }
        catch (Exception e) {}
      }
      t.add(f);
      t.notifyAll();
    }
  }
}

  2.同步的范围,在本例中是放和取两个方法,不能把做菜和吃菜这种各自不相干的工作放在受保护的范围中。

  3.参与者与容积比

   对于生产者和消费者的比例,以及桌子所能放置最多菜的数量三者之间的关系是影响性能的重要因素,如果是过多的生产者在等待,则要增加消费者或减少生产者的数据,反之则增加生产者或减少消费者的数量。

  另外如果桌子有足够的容量可以很大程序提升性能,这种情况下可以同时提高生产者和消费者的数量,但足够大的容时往往你要有足够大的物理内存。

转载自dev2dev网友axman的go deep into java专栏。

http://man.lupaworld.com/content/develop/joyfire/system/11.html#I263  自己找的 挺好的

系统分类: 嵌入式
用户分类: 无分类
标签: 无标签
来源: 转贴
发表评论 阅读全文(165) | 回复(0)
总共 , 当前 /