黑马程序员——线程
来源:互联网 发布:淘宝店铺转让后 编辑:程序博客网 时间:2024/06/05 18:56
点击打开链接 点击打开链接 点击打开链接 <a href="target=" _xhe_href="target=" blank"="">.Net培训、期待与您交流!">点击打开链接 点击打开链接
线程是什么?
说道线程,就得跟进程一起说,一个程序安装以后,会在磁盘上占用一块磁盘空间。当要运行该程序时,计算机就将该程序的文件加载到内存上,这时,计算机就会产生一个进程。在这个进程中会有至少一个到一个以上的线程。它是进程中的一个执行任务,一个程序中可以运行多个线程,多个线程可共享数据。
线程的作用,有什么好处?
线程可以让多个代码块同时运行,平时我们用的下载软件,比如迅雷,他就是多线程软件,利用多线程达到快速下载资源的目的,让程序运行更高效。
线程的特点:线程的特点就要说并行和并发两个概念。
并行:
无论是宏观还是微观上讲,并行都是多个同时运行,就比如两个人同时挖坑,1个小时候两个人各挖了一个大坑。
并发:
从宏观上来讲,线程达到同时运行多个代码块的目的,从微观上来讲,两个代码块是在相互进行交替运行在某一个时间点上只有一个在运行。就如同一个人在同时挖两个坑,每个坑挖两下,1个小时以后挖了两个小坑。
而多线程其实就是并发特点。线程之间在做交替运动。
线程的使用
线程的使用必须使用Thread线程类中的start()方法。
我们可以继承Thread类或者实现runnable接口。
1、利用继承Thread的方法启动线程。
public class ThreadExtend {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Demo d = new Demo(); //创建继承Thread子类对象
d.start(); //调用start方法,启用线程,使d对象处于就绪状态,一旦cpu进入即可启动执行。
for (int i = 0; i < 100; i++) { //main方法的线程调用
System.out.println("我是main线程、、。。。");
}
}
}
class Demo extends Thread{
@Override
public void run() { //run方法称为线程体,就是让run方法里面的代码块独立运行。
//必须依托start才会产生线程,直接调用run方法那么他就是一个普通的方法
for (int i = 0; i < 100; i++) {
System.out.println("我是Demo线程、、。。。");
}
}
}
2、利用实现runnable接口启动线程
public class ThreadRunnable {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Demo1 d1 = new Demo1();//创建实现runnable接口的实现类
new Thread(d1).start();//创建匿名的Thread类,将d1作为参数传入,调用匿名Thread类的start方法,启动d1对象的run方法。
for (int i = 0; i < 100; i++) {//main主线程的循环体
System.out.println("我是main的线程");
}
}
}
class Demo1 implements Runnable{//实现runnable接口的Demo1子类
@Override
public void run() {//线程主体
for (int i = 0; i <100; i++) {
System.out.println("我是Demo1的线程。.....");
}
}
}
线程的安全性
多线程的随机性和访问出现网络延迟,会造成数据的安全隐患。
用一个模拟售票的例子来实现场景。
public class ThreadRunnableTicket {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Ticket d1 = new Ticket();//创建实现runnable接口的实现类
new Thread(d1).start();//创建匿名的Thread类,将d1作为参数传入,调用匿名Thread类的start方法,启动d1对象的run方法。
new Thread(d1).start();//创建4个匿名的Thread类,相当于启动了4个线程。同时去进行卖票
new Thread(d1).start();
new Thread(d1).start();
}
}
class Ticket implements Runnable{//实现runnable接口的Demo1子类
private int ticket = 100;
@Override
public void run() {//线程主体
while(ticket > 0){
System.out.println(Thread.currentThread().getName() + "当前卖出第" + ticket + "张票");
ticket--;
}
}
}
//结果可能会出现重票,0票或负票。这就是线程的安全隐患问题,解决此类问题就用到了线程。
线程同步
线程同步有三种方法
① 同步代码块:
synchronized (同步监听对象) {
// 需要同步的代码, 存在线程安全的代码
}
注意 : 同步监听对象必须保证在多个线程中使用的是一个相同的对象;
一般使用相关联的业务对象,或者是类的字节码对象 例如 String.class
② 同步方法,
格式 : 在方法的返回值类型前面 加上 synchronized,
注意: 此处不需要写同步监听对象
同步方法的同步监听对象默认是 this (实例方法)
静态方法是 类的字节码对象
③ 锁 -
Lock 接口
|-- ReentrantLock 实现类
描述: 一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
注意: 要达到同步的效果,得注意是否使用的是同一把锁对象
同步代码块实现线程安全:synchronized(监听对象)多个线程使用的必须是同一个对象
public class ThreadRunnableTicket_syn1 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Ticket2 d1 = new Ticket2();//创建实现runnable接口的实现类
new Thread(d1).start();//创建匿名的Thread类,将d1作为参数传入,调用匿名Thread类的start方法,启动d1对象的run方法。
new Thread(d1).start();//创建4个匿名的Thread类,相当于启动了4个线程。同时去进行卖票
new Thread(d1).start();
new Thread(d1).start();
}
}
class Ticket2 implements Runnable{//实现runnable接口的Demo1子类
private int ticket = 100; //定义出售车票总数
@Override
public void run() {//线程主体
while(ticket > 0){ //判断车票是否售完,如果售完就不执行
try {
Thread.sleep(10); //为了让效果明显,加上的辅助语句,整个try。catch在实际中没有意义,可忽略
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (Ticket2.class) { //重点!!!!此为同步代码块,用synchronized关键字表示,括号里面跟一个唯一的对象,当做锁来控制进入的线程
if(ticket > 0){ //避免出现负票
System.out.println(Thread.currentThread().getName() + "当前卖出第" + ticket + "张票");
ticket--; //卖出一张票存量就减少一张
}
}
}
}
}
同步方法 synchronized 默认是this监听对象。使用过程中确保线程的this是同一个this
public class ThreadRunnableTicket_syn2 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Ticket3 d1 = new Ticket3();//创建实现runnable接口的实现类
new Thread(d1).start();//创建匿名的Thread类,将d1作为参数传入,调用匿名Thread类的start方法,启动d1对象的run方法。
new Thread(d1).start();//创建4个匿名的Thread类,相当于启动了4个线程。同时去进行卖票
new Thread(d1).start();
new Thread(d1).start();
}
}
class Ticket3 implements Runnable{ //实现runnable接口的Demo1子类
private int ticket = 100; //定义出售车票总数
@Override
public void run() { //线程主体
while(ticket > 0){ //判断车票是否售完,如果售完就不执行
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
tick(); //调用售票方法
}
}
public synchronized void tick(){
if(ticket > 0){ //避免出现负票
System.out.println(Thread.currentThread().getName() + "当前卖出第" + ticket + "张票");
ticket--; //卖出一张票存量就减少一张
}
}
}
锁 Lock 在线程类中创建锁的实现类对象,调用lock和unlock方法,实现锁功能
public class ThreadRunnableTicket3_Lock {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Tick d1 = new Tick();//创建实现runnable接口的实现类
new Thread(d1).start();//创建匿名的Thread类,将d1作为参数传入,调用匿名Thread类的start方法,启动d1对象的run方法。
new Thread(d1).start();//创建4个匿名的Thread类,相当于启动了4个线程。同时去进行卖票
new Thread(d1).start();
new Thread(d1).start();
}
}
class Tick implements Runnable{//实现runnable接口的Demo1子类
private int ticket = 100;
private final ReentrantLock lock = new ReentrantLock();// 创建一个Lock锁接口的实现类,实现锁的功能
@Override
public void run() {//线程主体
while(ticket > 0){ //判断票数是否售空。若为空则不执行
lock.lock(); //加锁
try { //模拟网络延迟效果,不管他
Thread.sleep(20);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try{
if(ticket > 0){//判断是否为正常票,小于等于0说明票已经卖完,不在执行下面的售票操作
System.out.println(Thread.currentThread().getName() + "当前卖出第" + ticket + "张票");
ticket--;
}
}finally{ //一定会执行解锁操作
lock.unlock(); //解锁
}
}
}
}
---------------------- <a target="blank">ASP.Net+Unity开发</a>、<a target="blank">.Net培训</a>、期待与您交流! ----------------------
- 黑马程序员—线程
- 黑马程序员 — 线程
- 黑马程序员——线程
- 黑马程序员——线程
- 黑马程序员——线程
- 黑马程序员——线程
- 黑马程序员——线程
- 黑马程序员——线程
- 黑马程序员——线程
- 黑马程序员——线程
- 黑马程序员——线程
- 黑马程序员——线程
- 黑马程序员——线程
- 黑马程序员——线程
- 黑马程序员——线程
- 黑马程序员——线程
- 黑马程序员—线程小结
- 黑马程序员—线程技术
- JNDI
- 一步步构建大型网站架构【转载】
- 《快学Scala》第6章 对象 练习
- PHP中字符串中嵌入变量
- 网络协议学习之局域网监听技术
- 黑马程序员——线程
- 编译默认root的android的user版本
- OpenGL像素缓冲区对象
- linux网络编程学习笔记之六 -----I/O多路复用服务端
- C++如何去除浮点数字符串后面的零
- Image Resizer for Windows资源管理器直接使用右键调整一个或多个图像文件的尺寸
- leetcode -:N-Queens
- Linux--命令--gcc
- poj-3481-Double Queue-splay树的水题